Compare commits
13 Commits
ba5b2694a3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
054715688f | ||
|
|
cb044eb720 | ||
|
|
b1a16e6bb8 | ||
|
|
0979f62718 | ||
|
|
0b88d4e670 | ||
|
|
e7de29542a | ||
| 3a3b331092 | |||
|
|
b482baf56d | ||
|
|
610328129d | ||
|
|
9b83c162f3 | ||
|
|
d9d6a459ed | ||
|
|
0d65cdbc65 | ||
|
|
5138bd3b58 |
14
Dockerfile
Normal file
14
Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM node:latest AS build
|
||||
WORKDIR /app
|
||||
COPY package.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
RUN npm run build --production
|
||||
RUN echo "Contents of /app/dist/byhaider-homepage/browser:" && ls -l /app/dist/byhaider-homepage/browser
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=build /app/dist/byhaider-homepage/browser /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
19
compose.yaml
Normal file
19
compose.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
services:
|
||||
homepage:
|
||||
build:
|
||||
context: .
|
||||
image: byhaider-homepage:latest
|
||||
restart: unless-stopped
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.byhaider-homepage.rule=Host(`byhaider.dev`)"
|
||||
- "traefik.http.routers.byhaider-homepage.entrypoints=websecure"
|
||||
- "traefik.http.routers.byhaider-homepage.tls=true"
|
||||
- "traefik.http.routers.byhaider-homepage.tls.certResolver=cloudflare"
|
||||
- "traefik.http.services.byhaider-homepage.loadbalancer.server.port=80"
|
||||
networks:
|
||||
- traefik-network
|
||||
|
||||
networks:
|
||||
traefik-network:
|
||||
external: true
|
||||
25
deploy.sh
Executable file
25
deploy.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "🚀 Starte Deployment für byHaider-Homepage..."
|
||||
|
||||
cd ~/byhaider-homepage
|
||||
|
||||
echo "📥 Pull latest changes for byhaider-homepage..."
|
||||
git pull origin main
|
||||
|
||||
echo "🛑 Stoppe alte Container..."
|
||||
docker compose down
|
||||
|
||||
echo "📦 Baue Images neu..."
|
||||
docker compose build
|
||||
|
||||
echo "⬆️ Starte Container..."
|
||||
docker compose up -d
|
||||
|
||||
echo "🧹 Bereinige alte Images..."
|
||||
docker image prune -f
|
||||
|
||||
echo "✅ Deployment abgeschlossen!"
|
||||
docker compose ps
|
||||
|
||||
11
nginx.conf
Normal file
11
nginx.conf
Normal file
@@ -0,0 +1,11 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,8 @@
|
||||
"common": {
|
||||
"home": "Startseite",
|
||||
"about": "Über mich",
|
||||
"projects": "Projekte"
|
||||
"projects": "Projekte",
|
||||
"copyright": "© 2025 Nico Haider; Alle Rechte vorbehalten."
|
||||
},
|
||||
"home": {
|
||||
"header": "Hi, ich bin<br/>Nico HAIDER.",
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"common": {
|
||||
"home": "Home",
|
||||
"about": "About",
|
||||
"projects": "Projects"
|
||||
"projects": "Projects",
|
||||
"copyright": "© 2025 Nico Haider; All rights reserved."
|
||||
},
|
||||
"home": {
|
||||
"header": "Hi, I'm<br/>Nico HAIDER.",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 84 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 629 KiB |
@@ -1,3 +1,11 @@
|
||||
<div class="flex flex-col min-h-screen">
|
||||
<app-background/>
|
||||
<main class="flex-1">
|
||||
<app-navigation-bar/>
|
||||
<router-outlet/>
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
|
||||
<p class="text-gray text-center p-5">
|
||||
{{ 'common.copyright' | translate }}
|
||||
</p>
|
||||
</div>
|
||||
@@ -3,10 +3,11 @@ import { RouterOutlet } from '@angular/router';
|
||||
import { ThemeSwitchService } from './core/service/theme-switch.service';
|
||||
import { Background } from "./core/components/background/background";
|
||||
import { NavigationBar } from "./core/components/navigation-bar/navigation-bar";
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [RouterOutlet, Background, NavigationBar],
|
||||
imports: [RouterOutlet, Background, NavigationBar, TranslatePipe],
|
||||
templateUrl: './app.html',
|
||||
})
|
||||
export class App {
|
||||
|
||||
@@ -16,11 +16,11 @@ export class Background {
|
||||
@ViewChild('canvas', { static: true }) canvasRef!: ElementRef<HTMLCanvasElement>;
|
||||
|
||||
// Config
|
||||
private numStars = 300;
|
||||
private numStars = 400;
|
||||
private minBlink = 0.2;
|
||||
private maxBlink = 1;
|
||||
private blinkSpeed = 0.002; // Stern blinkt langsamer oder schneller
|
||||
private maxSpeed = 0.1; // Sternbewegungsgeschwindigkeit
|
||||
private maxSpeed = .8; // Sternbewegungsgeschwindigkeit
|
||||
private parallaxFactor = 0.5; // Scroll-Parallax-Faktor
|
||||
|
||||
// Feature toggles
|
||||
@@ -33,17 +33,24 @@ export class Background {
|
||||
private height = window.innerHeight;
|
||||
private animationId!: number;
|
||||
private scrollY = window.scrollY;
|
||||
private bodyHeight = document.body.scrollHeight;
|
||||
|
||||
ngOnInit() {
|
||||
ngAfterViewInit() {
|
||||
requestAnimationFrame(() => {
|
||||
const canvas = this.canvasRef.nativeElement;
|
||||
this.ctx = canvas.getContext('2d')!;
|
||||
|
||||
this.width = window.innerWidth;
|
||||
this.height = window.innerHeight;
|
||||
canvas.width = this.width;
|
||||
canvas.height = this.height;
|
||||
|
||||
for (let i = 0; i < (this.numStars) * (document.body.scrollHeight / this.height); i++) {
|
||||
this.bodyHeight = document.body.scrollHeight + 110; // navbar height
|
||||
|
||||
for (let i = 0; i < this.numStars * (this.bodyHeight / this.height); i++) {
|
||||
this.stars.push({
|
||||
x: Math.random() * this.width,
|
||||
y: Math.random() * document.body.scrollHeight,
|
||||
y: Math.random() * this.bodyHeight,
|
||||
radius: Math.random() * 1.5 + 0.5,
|
||||
alpha: this.minBlink + Math.random() * (this.maxBlink - this.minBlink),
|
||||
alphaChange: (Math.random() * this.blinkSpeed) + 0.0005,
|
||||
@@ -53,6 +60,7 @@ export class Background {
|
||||
}
|
||||
|
||||
this.animate();
|
||||
});
|
||||
}
|
||||
|
||||
@HostListener('window:resize')
|
||||
@@ -89,8 +97,8 @@ export class Background {
|
||||
// Bildschirm-Ränder
|
||||
if (star.x < 0) star.x = this.width;
|
||||
if (star.x > this.width) star.x = 0;
|
||||
if (star.y < 0) star.y = document.body.scrollHeight;
|
||||
if (star.y > document.body.scrollHeight) star.y = 0;
|
||||
if (star.y < 0) star.y = this.bodyHeight;
|
||||
if (star.y > this.bodyHeight) star.y = 0;
|
||||
|
||||
// Stern zeichnen mit Scroll-Parallax
|
||||
ctx.beginPath();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<nav class="sticky top-0 p-[30px] flex flex-row justify-between relative z-5000">
|
||||
<nav class="sticky top-0 p-[30px] flex flex-row justify-between relative z-30">
|
||||
<div class="logo grid place-items-center">
|
||||
<span class="font-semibold text-3xl">
|
||||
<img [src]="themeSwitchService.darkMode() ? 'logo/square_white.svg' : 'logo/square_black.svg'" alt="bH" width="50">
|
||||
<a routerLink="/"><img [src]="themeSwitchService.darkMode() ? 'logo/square_white.svg' : 'logo/square_black.svg'" alt="bH" width="50"></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="navigation grid place-items-center">
|
||||
<div class="navigation place-items-center hidden md:grid">
|
||||
<ul>
|
||||
<li class="relative list-none flex flex-row gap-3">
|
||||
<a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
|
||||
@@ -19,7 +19,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="options flex flex-row gap-[10px] items-center">
|
||||
<div class="options flex-row gap-[10px] items-center hidden md:flex">
|
||||
@if (themeSwitchService.darkMode()) {
|
||||
<div class="dark-mode size-[50px]" (click)="themeSwitchService.switchToLight()">
|
||||
<fa-icon [icon]="faMoon"/>
|
||||
@@ -33,7 +33,44 @@
|
||||
{{ translateService.getCurrentLang().toUpperCase() }}
|
||||
</div>
|
||||
</div>
|
||||
<p-button icon="fa-solid fa-bars" outlined (click)="showMobileMenu.set(true)" class="block md:hidden"/>
|
||||
</nav>
|
||||
|
||||
<div class="blur blur1"></div>
|
||||
<div class="blur blur2"></div>
|
||||
@if (showMobileMenu()) {
|
||||
<div class="mobile-menu block md:hidden w-full h-full fixed top-0 left-0 z-50">
|
||||
<p-button icon="fa-solid fa-x" outlined (click)="showMobileMenu.set(false)" class="absolute top-8 right-8"/>
|
||||
|
||||
<ul class="h-full p-[100px]">
|
||||
<li class="h-full relative list-none flex flex-col gap-3 align-center justify-center text-center">
|
||||
<a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }" (click)="showMobileMenu.set(false)">
|
||||
{{ 'common.home' | translate }}
|
||||
</a>
|
||||
<a routerLink="/about" routerLinkActive="active" (click)="showMobileMenu.set(false)">
|
||||
{{ 'common.about' | translate }}
|
||||
</a>
|
||||
<a routerLink="/projects" routerLinkActive="active" (click)="showMobileMenu.set(false)">
|
||||
{{ 'common.projects' | translate }}
|
||||
</a>
|
||||
<hr class="h-[1px] w-[80%] bg-(--content-color) mx-auto"/>
|
||||
<div class="options flex flex-row gap-[10px] items-center justify-center">
|
||||
@if (themeSwitchService.darkMode()) {
|
||||
<div class="dark-mode size-[50px]" (click)="themeSwitchService.switchToLight()">
|
||||
<fa-icon [icon]="faMoon"/>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="dark-mode size-[50px]" (click)="themeSwitchService.switchToDark()">
|
||||
<fa-icon [icon]="faSun"/>
|
||||
</div>
|
||||
}
|
||||
<div class="language size-[50px]" (click)="toggleLanguage()">
|
||||
{{ translateService.getCurrentLang().toUpperCase() }}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
<div class="block md:hidden w-full h-full fixed top-0 left-0 z-40 backdrop-blur-md"></div>
|
||||
}
|
||||
|
||||
<div class="z-10 backdrop-blur-xl fixed top-0 left-0 w-full h-[150px] mask-b-from-70%"></div>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
nav {
|
||||
.options > div {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
@@ -8,15 +7,15 @@ nav {
|
||||
transition: border .5s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--content-hover-background);
|
||||
border-color: rgb(from var(--content-color) r g b / 0.5);
|
||||
}
|
||||
|
||||
&:active {
|
||||
border-color: var(--content-border-color);
|
||||
border-color: var(--content-color);
|
||||
}
|
||||
}
|
||||
|
||||
.navigation ul li a {
|
||||
ul li a {
|
||||
display: block;
|
||||
padding: 7.5px 15px;
|
||||
position: relative;
|
||||
@@ -34,27 +33,8 @@ nav {
|
||||
border: 1px solid var(--primary-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.blur {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.blur1 {
|
||||
z-index: 10;
|
||||
backdrop-filter: blur(3px);
|
||||
mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 40%, rgba(0, 0, 0, 1) 70%, rgba(0, 0, 0, 0) 100%);
|
||||
-webkit-mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 40%, rgba(0, 0, 0, 1) 70%, rgba(0, 0, 0, 0) 100%);
|
||||
}
|
||||
|
||||
.blur2 {
|
||||
z-index: 20;
|
||||
backdrop-filter: blur(5px);
|
||||
mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 60%, rgba(0, 0, 0, 1) 85%, rgba(0, 0, 0, 1) 100%);
|
||||
-webkit-mask-image: linear-gradient(to top, rgba(0, 0, 0, 0) 60%, rgba(0, 0, 0, 1) 85%, rgba(0, 0, 0, 1) 100%);
|
||||
.mobile-menu ul li a {
|
||||
font-size: 1rem;
|
||||
padding: 15px 30px;
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { Component, inject, signal } from '@angular/core';
|
||||
import { Router, RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
||||
import { faMoon } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faSun } from '@fortawesome/free-solid-svg-icons';
|
||||
import { ThemeSwitchService } from '../../service/theme-switch.service';
|
||||
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
|
||||
import { ButtonModule } from "primeng/button";
|
||||
|
||||
@Component({
|
||||
selector: 'app-navigation-bar',
|
||||
imports: [FontAwesomeModule, RouterLink, RouterLinkActive, TranslatePipe],
|
||||
imports: [FontAwesomeModule, RouterLink, RouterLinkActive, TranslatePipe, ButtonModule],
|
||||
templateUrl: './navigation-bar.html',
|
||||
styleUrl: './navigation-bar.scss'
|
||||
})
|
||||
@@ -18,6 +19,8 @@ export class NavigationBar {
|
||||
protected themeSwitchService = inject(ThemeSwitchService);
|
||||
protected translateService = inject(TranslateService);
|
||||
|
||||
showMobileMenu = signal(false);
|
||||
|
||||
constructor() {}
|
||||
|
||||
isActive(route: string): boolean {
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
<div class="text-center mt-5">
|
||||
<p>Nico Haider</p>
|
||||
<p>3843 Dobersberg</p>
|
||||
<p>Portfolio</p>
|
||||
</div>
|
||||
<img src="img/page-in-work.jpg" alt="" class="w-[200px] mx-auto mt-10"/>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<main class="flex flex-row flex-wrap justify-center items-center gap-10 py-10">
|
||||
<main class="flex flex-row flex-wrap justify-center items-center gap-10 py-10"
|
||||
pAnimateOnScroll
|
||||
enterClass="animate-enter fade-in-10 slide-in-from-t-8 animate-duration-1000">
|
||||
<div class="lg:w-[40%] w-[80%] text-center lg:text-left">
|
||||
<span class="block text-[3rem]/[1.1] lg:text-[4rem]/[1.1] font-bold mb-4" [innerHTML]="'home.header' | translate"></span>
|
||||
<span class="block text-[1rem]/[1.3] lg:text-[1.5rem]/[1.3] text-gray">{{ 'home.subheader' | translate }}</span>
|
||||
|
||||
@@ -4,7 +4,3 @@ main {
|
||||
mask-image: linear-gradient(to bottom, rgba(0,0,0,1) 60%, rgba(0,0,0,0) 100%);
|
||||
}
|
||||
}
|
||||
|
||||
.text-gray {
|
||||
color: rgb(from var(--content-color) r g b / 0.7);
|
||||
}
|
||||
@@ -3,10 +3,11 @@ import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { ChipModule } from 'primeng/chip';
|
||||
import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { AnimateOnScrollModule } from 'primeng/animateonscroll';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
imports: [TranslatePipe, ChipModule, FontAwesomeModule, ButtonModule],
|
||||
imports: [TranslatePipe, ChipModule, FontAwesomeModule, ButtonModule, AnimateOnScrollModule],
|
||||
templateUrl: './home.html',
|
||||
styleUrl: './home.scss'
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@use "tailwindcss";
|
||||
@plugin "tailwindcss-primeui";
|
||||
@import '@fontsource-variable/open-sans/wght.css';
|
||||
@import '@fontsource-variable/jetbrains-mono/wght.css';
|
||||
@import '@fortawesome/fontawesome-free/css/all.css';
|
||||
@@ -21,3 +22,7 @@ p-card {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.text-gray {
|
||||
color: rgb(from var(--content-color) r g b / 0.7);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user