Add complete bewerbung Next.js application

- Add all Next.js application files
- Include components, app structure, and public assets
- Remove empty bewerbung placeholder folder
- Update .gitignore for Next.js project

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
cbazzaandClaude Sonnet 4.5 committed 2025-12-18 09:11:47 +01:00
1 parent 571c381718
commit 30a380cae8
48 files changed
+9038 -20

No files matched your search

+37 -19
View File
@@ -1,23 +1,41 @@
# macOS
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
.AppleDouble
.LSOverride
*.pem
# Thumbnails
._*
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# env files (can opt-in for committing if needed)
.env*
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
+186
View File
@@ -0,0 +1,186 @@
# 🗂️ Ungenutzte Dateien - Backup & Cleanup
## 📋 Übersicht
Dieses Dokument listet alle ungenutzten Dateien auf, die sicher in einen Backup-Ordner verschoben werden können.
---
## 📁 Downloads (/public/downloads/)
### ✅ **AKTIV - Werden verwendet:**
```
✓ Lebenslauf_2025.pdf → page.tsx
✓ Zeugnisse_2025.pdf → page.tsx
✓ Referenzen.pdf → page.tsx
```
### ❌ **INAKTIV - Können verschoben werden:**
```
✗ Lebenslauf2020.pdf → Veraltet (ersetzt durch _2025)
✗ Zeugnisse.pdf → Veraltet (ersetzt durch _2025)
✗ Bewerbungsmappe 2014...pdf → Sehr alt
```
---
## 🖼️ Bilder (/public/img/)
### ✅ **AKTIV - Werden verwendet:**
```
Hero Section:
✓ bg-ich-2025-background.jpg → Hero.tsx
✓ bg-ich-2025-foreground.png → Hero.tsx
Download Sections:
✓ bg-lebenslauf.jpg → page.tsx
✓ bg-arbeitszeugnisse.jpg → page.tsx
✓ keyboard-bg.jpg → page.tsx
Portfolio (Web):
✓ Referenz - e-stev -Web.jpg → Portfolio.tsx
✓ Referenz---Planet-Touristik---web.jpg → Portfolio.tsx
✓ Referenz---handstaubsauger---web.jpg → Portfolio.tsx
Portfolio (Print):
✓ Referenz---Illy---Print.jpg → Portfolio.tsx
✓ Referenz---SZ---Print.jpg → Portfolio.tsx
✓ Referenz---Planet-Touristik---print.jpg → Portfolio.tsx
```
### ❌ **INAKTIV - Können verschoben werden:**
```
Alte Hero-Bilder:
✗ bg-ich-1.png → Alt
✗ bg-ich-2.png → Alt
✗ bg-ich-2025.jpg → Duplikat
✗ bg-ich-2025-foreground.jpg → Ersetzt durch .png
✗ workspace-bg.jpg → Nicht verwendet
Alte Icons:
✗ Workspace.png
✗ controls.png
✗ h1-bg.png
✗ icon-os.png
✗ icons-cms.png
✗ icons-gfx.png
✗ icons-marketing.png
✗ icons-office.png
✗ icons-os.png
✗ icons-web.png
Portfolio Duplikate:
✗ Referenz - Illy-sommerkarte07.png
✗ Referenz - Planet Touristik - Frontansicht.jpg
✗ Referenz - Planet-Touristik - web01.jpg
✗ Referenz---Illy---Außenansicht.jpg
✗ Referenz---Planet-Touristik---Frontansicht.jpg
✗ Referenz---SZ---Frontansicht.jpg
✗ Referenz---e-stev--Web2.jpg
✗ Referenz---handstaubsauger---web2.jpg
System-Dateien:
✗ .DS_Store (macOS)
```
---
## 🚀 Verwendung des Cleanup-Scripts
### 1. Script ausführbar machen:
```bash
chmod +x cleanup-unused-files.sh
```
### 2. Script ausführen:
```bash
./cleanup-unused-files.sh
```
### 3. Was passiert:
- ✅ Erstellt `public/_unused/downloads/` und `public/_unused/img/`
- ✅ Verschiebt alle ungenutzten Dateien dorthin
- ✅ Zeigt detaillierte Ausgabe was verschoben wurde
- ✅ **LÖSCHT NICHTS** - nur verschieben!
---
## ✅ Test-Checkliste nach Cleanup
```bash
# 1. Development Server starten
npm run dev
# 2. Prüfe diese Seiten/Funktionen:
□ Hero-Section lädt (beide Hintergrundbilder)
□ Download: Lebenslauf_2025.pdf funktioniert
□ Download: Zeugnisse_2025.pdf funktioniert
□ Download: Referenzen.pdf funktioniert
□ Portfolio: Alle 6 Projekt-Bilder werden angezeigt
□ Keine 404-Fehler in der Browser-Console
# 3. Production Build testen
npm run build
npx serve out
# 4. Prüfe erneut alle Downloads und Bilder
```
---
## 🔄 Dateien wiederherstellen (falls nötig)
Falls nach dem Test etwas fehlt:
```bash
# Einzelne Datei zurückkopieren:
cp public/_unused/downloads/DATEINAME.pdf public/downloads/
# Oder kompletten Ordner zurückkopieren:
cp -r public/_unused/* public/
```
---
## 🗑️ Endgültiges Löschen
**NUR nach erfolgreichem Test!**
```bash
# Backup-Ordner komplett entfernen:
rm -rf public/_unused/
# Oder im Finder in den Papierkorb verschieben
```
---
## 📊 Speicherplatz-Ersparnis
### Vorher:
- Downloads: ~7 Dateien (~15-20 MB geschätzt)
- Images: ~35 Dateien (~10-15 MB geschätzt)
### Nachher:
- Downloads: 3 Dateien (aktuelle Versionen)
- Images: ~12 Dateien (nur verwendete)
**Geschätzte Ersparnis:** ~15-20 MB
---
## 📝 Notizen
- **Datum des Cleanups:** [Füge Datum ein]
- **Build vor Cleanup:** [Version/Tag]
- **Funktionstest:** [ ] Bestanden
- **Backup-Ordner gelöscht am:** [Datum]
---
## 🆘 Support
Falls Probleme auftreten:
1. Backup-Ordner `_unused/` ist noch da
2. Alle Dateien können wiederhergestellt werden
3. Git-History hat alte Versionen
+188
View File
@@ -0,0 +1,188 @@
# beWerbung 2025 - Moderne Bewerbungswebseite
Eine moderne, responsive Bewerbungswebseite mit **Next.js 15**, **React 19**, **Tailwind CSS 4** und **TypeScript**.
## 🚀 Features
- ✅ **Modern Stack**: Next.js 15 App Router, React 19, Tailwind CSS 4
- ✅ **TypeScript**: Vollständig typisiert
- ✅ **Responsive**: Mobile-first Design
- ✅ **Performance**: Optimiert mit Next.js Image, lazy loading
- ✅ **Animationen**: Smooth scroll, fade-in effects, hover animations
- ✅ **SEO**: Meta-Tags, strukturierte Daten
- ✅ **Accessibility**: ARIA-Labels, semantisches HTML
## 📦 Installation
```bash
# Dependencies installieren
npm install
# Development Server starten
npm run dev
# Production Build erstellen
npm run build
# Production Server starten
npm start
```
Die Webseite läuft dann auf [http://localhost:3000](http://localhost:3000)
## 🏗️ Projektstruktur
```
bewerbung/
├── app/
│ ├── layout.tsx # Root Layout mit Metadata
│ ├── page.tsx # Hauptseite (Home)
│ └── globals.css # Globale Styles
├── components/
│ ├── Hero.tsx # Hero-Section mit Parallax
│ ├── Intro.tsx # Über mich Sektion
│ ├── Experience.tsx # Berufserfahrung
│ ├── Skills.tsx # Kompetenzen mit Progress Bars
│ ├── Portfolio.tsx # Portfolio mit Modal
│ ├── Contact.tsx # Kontakt-Section
│ ├── DownloadSection.tsx # Download-Komponente
│ └── Footer.tsx # Footer
├── public/
│ ├── downloads/ # PDFs (Lebenslauf, Zeugnisse, etc.)
│ └── img/ # Bilder
└── package.json
```
## 🎨 Anpassungen
### Farben ändern
In `app/globals.css`:
```css
:root {
--color-primary: #3dc9b3; /* Hauptfarbe */
--color-primary-dark: #309383; /* Dunklere Variante */
--color-text: #353535; /* Text */
--color-text-light: #8c9398; /* Heller Text */
--color-bg-light: #f3f5f8; /* Heller Hintergrund */
--color-bg-dark: #242830; /* Dunkler Hintergrund */
}
```
### Inhalte anpassen
#### Persönliche Daten
- `components/Intro.tsx` - Über mich Text & Alter
- `components/Contact.tsx` - Kontaktdaten
- `app/layout.tsx` - Meta-Tags
#### Skills
- `components/Skills.tsx` - Array `skillsData` anpassen
#### Berufserfahrung
- `components/Experience.tsx` - Array `jobs` anpassen
#### Portfolio
- `components/Portfolio.tsx` - Array `projects` anpassen
### Bilder hinzufügen
1. Bilder in `public/img/` ablegen
2. PDFs in `public/downloads/` ablegen
3. Pfade in Komponenten anpassen
## 🎯 Nächste Schritte
### Sofort einsatzbereit:
- [ ] Persönliche Daten anpassen
- [ ] PDFs hochladen (Lebenslauf, Zeugnisse, Referenzen)
- [ ] Bilder aus altem Projekt kopieren
- [ ] Favicon anpassen
### Erweiterungen:
- [ ] Kontaktformular mit Validierung
- [ ] Blog-Sektion
- [ ] Dark Mode Toggle
- [ ] Mehrsprachigkeit (i18n)
- [ ] Analytics (Google Analytics / Plausible)
- [ ] Cookie Consent Banner
## 🖼️ Bilder aus altem Projekt kopieren
```bash
# Im Terminal ausführen:
cp -r /Users/sebastianfrohlich/Projekte/bewerbung/img/* /Users/sebastianfrohlich/Projekte/bewerbung2025/bewerbung/public/img/
cp -r /Users/sebastianfrohlich/Projekte/bewerbung/downloads/* /Users/sebastianfrohlich/Projekte/bewerbung2025/bewerbung/public/downloads/
```
## 📱 Deployment
### Vercel (Empfohlen)
```bash
# Vercel CLI installieren
npm i -g vercel
# Deployen
vercel
```
### Netlify
1. GitHub Repository erstellen
2. Mit Netlify verbinden
3. Build Command: `npm run build`
4. Publish Directory: `.next`
## 🔧 Technologien
- **Framework**: Next.js 15.5.4
- **UI Library**: React 19.1.0
- **Styling**: Tailwind CSS 4
- **Language**: TypeScript 5
- **Font**: Varela Round (Google Fonts)
## 📄 Migration vom alten Projekt
### Was wurde modernisiert:
✅ jQuery → React Hooks
✅ Bootstrap → Tailwind CSS
✅ Google Charts → Custom Progress Bars
✅ FlexSlider → React State Management
✅ Magnific Popup → Custom Modal
✅ Veraltete Dependencies → Moderne Stack
### Was bleibt gleich:
- Design-Sprache und Farbschema
- Inhaltsstruktur
- Bewerbungskonzept "beWerbung"
## 🐛 Troubleshooting
### Port bereits belegt
```bash
# Port ändern
npm run dev -- -p 3001
```
### Build Fehler
```bash
# Cache löschen
rm -rf .next
npm run build
```
### TypeScript Fehler
```bash
# Types neu generieren
npm run build
```
## 📧 Kontakt
Sebastian Fröhlich
- Email: froehlich.sebastian@gmail.com
- Tel: +49 9874 322 429
---
Made with ❤️ and ☕ in Neuendettelsau
+190
View File
@@ -0,0 +1,190 @@
# ✅ beWerbung 2025 - Setup Checkliste
## 🎉 Was wurde erstellt:
### Komponenten
- ✅ Hero.tsx - Hero-Section mit Parallax-Effekt
- ✅ Intro.tsx - Über mich mit Intersection Observer
- ✅ Experience.tsx - Berufserfahrung mit Tab-Navigation
- ✅ Skills.tsx - Kompetenzen mit animierten Progress Bars
- ✅ Portfolio.tsx - Portfolio mit Filter und Modal
- ✅ Contact.tsx - Kontakt-Section mit Icons
- ✅ DownloadSection.tsx - Wiederverwendbare Download-Komponente
- ✅ Footer.tsx - Footer mit Call-to-Action
### Konfiguration
- ✅ layout.tsx - SEO & Metadata konfiguriert
- ✅ page.tsx - Alle Komponenten zusammengeführt
- ✅ globals.css - Custom Farben & Animationen
- ✅ README.md - Vollständige Dokumentation
- ✅ .env.example - Environment Variables Template
## 🚀 Nächste Schritte zum Start:
### 1. Assets kopieren (Bilder & PDFs)
```bash
cd /Users/sebastianfrohlich/Projekte/bewerbung2025/bewerbung
npm run copy-assets
```
**Oder manuell:**
```bash
mkdir -p public/img public/downloads
cp /Users/sebastianfrohlich/Projekte/bewerbung/img/*.jpg public/img/
cp /Users/sebastianfrohlich/Projekte/bewerbung/img/*.png public/img/
cp /Users/sebastianfrohlich/Projekte/bewerbung/downloads/*.pdf public/downloads/
```
### 2. Development Server starten
```bash
npm run dev
```
Dann öffne: **http://localhost:3000**
### 3. Inhalte anpassen
#### 📝 Persönliche Daten
- [ ] `components/Intro.tsx` - Geburtsjahr in Zeile 26 anpassen
- [ ] `components/Contact.tsx` - Kontaktdaten prüfen
- [ ] `app/layout.tsx` - Meta-Beschreibung anpassen
#### 💼 Berufserfahrung
- [ ] `components/Experience.tsx` - Jobs-Array aktualisieren
- [ ] Aktuelle Tätigkeiten hinzufügen
- [ ] Zeiträume prüfen
#### 🎨 Skills
- [ ] `components/Skills.tsx` - skillsData Array anpassen
- [ ] Neue Technologien hinzufügen
- [ ] Level-Werte überprüfen (1-5)
#### 🖼️ Portfolio
- [ ] `components/Portfolio.tsx` - projects Array aktualisieren
- [ ] Neue Projekte hinzufügen
- [ ] Bilder-Pfade prüfen
### 4. Bilder optimieren
**Fehlende Bilder erstellen:**
- [ ] workspace-bg.jpg - Für Lebenslauf-Download-Section
- [ ] certificates-bg.jpg - Für Zeugnisse-Download-Section
- [ ] keyboard-bg.jpg - Für Referenzen-Download-Section
**Tipp:** Du kannst die vorhandenen Bilder aus dem alten Projekt umbenennen:
```bash
cp public/img/Workspace.png public/img/workspace-bg.jpg
cp public/img/Flyer-Mockup-3---Infinity---Zeugnisse.jpg public/img/certificates-bg.jpg
cp public/img/keyboard-bg.jpg public/img/keyboard-bg.jpg
```
### 5. PDFs prüfen
Stelle sicher, dass diese PDFs in `public/downloads/` liegen:
- [ ] Lebenslauf2020.pdf (oder aktualisiere zu Lebenslauf2025.pdf)
- [ ] Zeugnisse.pdf
- [ ] Referenzen.pdf
## 🎨 Farben anpassen (Optional)
In `app/globals.css`:
```css
:root {
--color-primary: #3dc9b3; /* Deine Wunschfarbe */
--color-primary-dark: #309383;
}
```
## 📱 Responsive Testen
Teste die Webseite auf verschiedenen Bildschirmgrößen:
- [ ] Desktop (1920px+)
- [ ] Laptop (1280px)
- [ ] Tablet (768px)
- [ ] Mobile (375px)
**Browser Dev Tools:** F12 → Responsive Design Mode
## 🚀 Deployment Vorbereiten
### Vercel (Empfohlen)
1. GitHub Repository erstellen
2. Code pushen
3. Mit Vercel verbinden
4. Automatisches Deployment
### Manuell
```bash
npm run build
npm start
```
## 🐛 Bekannte Todos
- [ ] Bilder durch Next.js Image-Komponente ersetzen (Performance)
- [ ] Loading States für Animationen
- [ ] Error Boundaries hinzufügen
- [ ] 404 Seite erstellen
- [ ] Sitemap generieren
- [ ] robots.txt erstellen
## 💡 Optionale Erweiterungen
- [ ] Kontaktformular mit Backend (z.B. Resend, SendGrid)
- [ ] Blog-Sektion für Artikel
- [ ] Dark Mode Toggle
- [ ] Animations-Library (Framer Motion)
- [ ] Analytics Integration
- [ ] Cookie Consent Banner
- [ ] Newsletter Integration
## 📊 Vergleich Alt vs. Neu
| Feature | Alt (2014) | Neu (2025) |
|---------|------------|------------|
| Framework | Vanilla HTML | Next.js 15 |
| CSS | Bootstrap 3 | Tailwind 4 |
| JavaScript | jQuery 1.11 | React 19 |
| Charts | Google Charts | Custom Components |
| Responsive | Ja | Ja (Mobile-First) |
| TypeScript | Nein | Ja |
| Performance | 🟡 Mittel | 🟢 Hoch |
| Wartbarkeit | 🟡 Mittel | 🟢 Hoch |
| Modern | 🔴 Veraltet | 🟢 Aktuell |
## ✨ Verbesserungen gegenüber alter Version
1. **Performance:**
- Server-Side Rendering mit Next.js
- Automatisches Code Splitting
- Image Optimization
- Moderne JavaScript Features
2. **Wartbarkeit:**
- Komponentenbasiert
- TypeScript für Type Safety
- Modularer Code
- Klare Struktur
3. **Sicherheit:**
- Keine veralteten Dependencies
- Keine jQuery Vulnerabilities
- Moderne Security Headers möglich
4. **UX:**
- Smooth Animations
- Bessere Mobile Experience
- Schnellere Ladezeiten
- Moderne UI Patterns
## 📞 Support
Bei Fragen oder Problemen:
1. README.md lesen
2. Console Errors prüfen (F12)
3. Google / Stack Overflow
4. Next.js Dokumentation: https://nextjs.org/docs
---
🎉 **Viel Erfolg mit deiner neuen Bewerbungswebseite!**
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+65
View File
@@ -0,0 +1,65 @@
@import "tailwindcss";
:root {
--color-primary: #3dc9b3;
--color-primary-dark: #309383;
--color-text: #353535;
--color-text-light: #8c9398;
--color-bg-light: #f3f5f8;
--color-bg-dark: #242830;
}
@theme inline {
--font-varela: "Varela Round", system-ui, sans-serif;
--color-primary: #3dc9b3;
--color-primary-dark: #309383;
--color-text: #353535;
--color-text-light: #8c9398;
--color-bg-light: #f3f5f8;
--color-bg-dark: #242830;
}
body {
color: var(--color-text);
font-family: var(--font-varela);
}
/* Smooth scroll behavior */
html {
scroll-behavior: smooth;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: var(--color-bg-light);
}
::-webkit-scrollbar-thumb {
background: var(--color-primary);
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-primary-dark);
}
/* Animations */
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fade-in 1s ease-out;
}
+37
View File
@@ -0,0 +1,37 @@
import type { Metadata } from "next";
import { Varela_Round } from "next/font/google";
import "./globals.css";
const varelaRound = Varela_Round({
weight: "400",
subsets: ["latin"],
variable: "--font-varela",
});
export const metadata: Metadata = {
title: "beWerbung - Sebastian Fröhlich",
description: "Die Bewerberwebseite von Sebastian Fröhlich - Full-Stack Developer, technischer PO, Team Leader mit Fokus auf moderne Webtechnologien",
keywords: ["Sebastian Fröhlich", "Bewerbung", "Full-Stack Developer", "Web Development", "PHP", "JavaScript", "Python"],
authors: [{ name: "Sebastian Fröhlich" }],
openGraph: {
title: "beWerbung - Sebastian Fröhlich",
description: "Full-Stack Developer, technischer PO, Team Leader mit Leidenschaft für moderne Webtechnologien",
type: "website",
},
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="de" className="scroll-smooth">
<body
className={`${varelaRound.variable} antialiased font-varela`}
>
{children}
</body>
</html>
);
}
+55
View File
@@ -0,0 +1,55 @@
import Hero from '@/components/Hero';
import Intro from '@/components/Intro';
import DownloadSection from '@/components/DownloadSection';
import Experience from '@/components/Experience';
import Skills from '@/components/Skills';
import SoftSkills from '@/components/SoftSkills';
import Portfolio from '@/components/Portfolio';
import Contact from '@/components/Contact';
import Footer from '@/components/Footer';
export default function Home() {
return (
<main className="min-h-screen">
<Hero />
<Intro />
<DownloadSection
title="Sebastian Fröhlich"
subtitle="Meinen Lebenslauf herunterladen"
buttonText="Jetzt downloaden"
href="/downloads/Lebenslauf_2025.pdf"
backgroundImage="/img/bg-lebenslauf.jpg"
/>
<Experience />
<Skills />
<SoftSkills />
<DownloadSection
title="Sebastian Fröhlich"
subtitle="Meine Arbeitszeugnisse herunterladen"
buttonText="Jetzt downloaden"
href="/downloads/Zeugnisse_2025.pdf"
backgroundImage="/img/bg-arbeitszeugnisse.jpg"
/>
<Portfolio />
<DownloadSection
title="Sebastian Fröhlich"
subtitle="Meine Referenzen herunterladen"
buttonText="Jetzt downloaden"
href="/downloads/Referenzen.pdf"
backgroundImage="/img/keyboard-bg.jpg"
/>
<Contact />
<Footer />
</main>
);
}
Submodule bewerbung deleted from 44cd00d5b2.
+132
View File
@@ -0,0 +1,132 @@
#!/bin/bash
# Script zum sicheren Verschieben ungenutzter Dateien in Backup-Ordner
# Erstellt: $(date +%Y-%m-%d)
echo "🗂️ Erstelle Backup-Ordner..."
# Backup-Ordner erstellen
mkdir -p public/_unused/downloads
mkdir -p public/_unused/img
echo ""
echo "📦 Verschiebe ungenutzte DOWNLOADS..."
echo "======================================"
# Alte PDFs verschieben (falls vorhanden)
if [ -f "public/downloads/Lebenslauf2020.pdf" ]; then
mv "public/downloads/Lebenslauf2020.pdf" "public/_unused/downloads/"
echo "✅ Lebenslauf2020.pdf → _unused/downloads/"
else
echo "⏭️ Lebenslauf2020.pdf nicht gefunden (bereits verschoben?)"
fi
if [ -f "public/downloads/Zeugnisse.pdf" ]; then
mv "public/downloads/Zeugnisse.pdf" "public/_unused/downloads/"
echo "✅ Zeugnisse.pdf → _unused/downloads/"
else
echo "⏭️ Zeugnisse.pdf nicht gefunden (bereits verschoben?)"
fi
if [ -f "public/downloads/Bewerbungsmappe 2014 - Refererenzen.pdf" ]; then
mv "public/downloads/Bewerbungsmappe 2014 - Refererenzen.pdf" "public/_unused/downloads/"
echo "✅ Bewerbungsmappe 2014 - Refererenzen.pdf → _unused/downloads/"
else
echo "⏭️ Bewerbungsmappe nicht gefunden (bereits verschoben?)"
fi
echo ""
echo "🖼️ Verschiebe ungenutzte BILDER..."
echo "======================================"
# Alte Hintergrundbilder
declare -a old_images=(
"bg-ich-1.png"
"bg-ich-2.png"
"bg-ich-2025.jpg"
"bg-ich-2025-foreground.jpg"
"workspace-bg.jpg"
)
for img in "${old_images[@]}"; do
if [ -f "public/img/$img" ]; then
mv "public/img/$img" "public/_unused/img/"
echo "✅ $img → _unused/img/"
fi
done
# Alte Icon-Dateien
declare -a old_icons=(
"Workspace.png"
"controls.png"
"h1-bg.png"
"icon-os.png"
"icons-cms.png"
"icons-gfx.png"
"icons-marketing.png"
"icons-office.png"
"icons-os.png"
"icons-web.png"
)
for icon in "${old_icons[@]}"; do
if [ -f "public/img/$icon" ]; then
mv "public/img/$icon" "public/_unused/img/"
echo "✅ $icon → _unused/img/"
fi
done
# Portfolio-Duplikate (vorsichtig - nur offensichtliche Duplikate)
declare -a portfolio_duplicates=(
"Referenz - Illy-sommerkarte07.png"
"Referenz - Planet Touristik - Frontansicht.jpg"
"Referenz - Planet-Touristik - web01.jpg"
"Referenz---Illy---Außenansicht.jpg"
"Referenz---Planet-Touristik---Frontansicht.jpg"
"Referenz---SZ---Frontansicht.jpg"
"Referenz---e-stev--Web2.jpg"
"Referenz---handstaubsauger---web2.jpg"
)
echo ""
echo "📸 Verschiebe Portfolio-Duplikate..."
echo "======================================"
for img in "${portfolio_duplicates[@]}"; do
if [ -f "public/img/$img" ]; then
mv "public/img/$img" "public/_unused/img/"
echo "✅ $img → _unused/img/"
fi
done
# .DS_Store Dateien (macOS)
if [ -f "public/downloads/.DS_Store" ]; then
mv "public/downloads/.DS_Store" "public/_unused/downloads/"
echo "✅ .DS_Store (downloads) → _unused/"
fi
if [ -f "public/img/.DS_Store" ]; then
mv "public/img/.DS_Store" "public/_unused/img/"
echo "✅ .DS_Store (img) → _unused/"
fi
echo ""
echo "✅ Fertig!"
echo ""
echo "📊 Zusammenfassung:"
echo "==================="
echo "Alle ungenutzten Dateien wurden nach public/_unused/ verschoben."
echo ""
echo "📁 Backup-Ordner:"
echo " public/_unused/downloads/"
echo " public/_unused/img/"
echo ""
echo "🔍 Nächste Schritte:"
echo " 1. Teste die Webseite: npm run dev"
echo " 2. Prüfe ob alles funktioniert"
echo " 3. Falls etwas fehlt, Dateien aus _unused/ zurückkopieren"
echo " 4. Nach erfolgreicher Prüfung: _unused/ Ordner komplett löschen"
echo ""
echo "🗑️ Zum endgültigen Löschen (erst nach Test!):"
echo " rm -rf public/_unused/"
echo ""
+82
View File
@@ -0,0 +1,82 @@
'use client';
export default function Contact() {
const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
};
return (
<section id="contact" className="relative py-20 px-4 sm:px-6 lg:px-8 bg-bg-dark text-white">
{/* Scroll to Top Button */}
<button
onClick={scrollToTop}
className="absolute -top-6 left-1/2 -translate-x-1/2 w-12 h-12 bg-primary rounded-full flex items-center justify-center hover:bg-primary-dark transition-all shadow-lg group"
aria-label="Nach oben scrollen"
>
<svg
className="w-6 h-6 text-white group-hover:-translate-y-1 transition-transform"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
</svg>
</button>
<div className="max-w-6xl mx-auto">
<h2 className="text-3xl sm:text-4xl font-bold text-center mb-16 relative inline-block w-full after:content-[''] after:block after:w-12 after:h-1 after:bg-primary after:mt-6 after:mx-auto">
Jetzt Kontakt aufnehmen
</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{/* Address */}
<div className="bg-white/5 backdrop-blur-sm rounded-lg p-8 text-center hover:bg-white/10 transition-all hover:transform hover:scale-105">
<div className="w-16 h-16 bg-primary rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</div>
<h3 className="text-xl font-bold mb-2">Adresse</h3>
<p className="text-white/80">
Rosenstraße 4<br />
91564 Neuendettelsau
</p>
</div>
{/* Phone */}
<div className="bg-white/5 backdrop-blur-sm rounded-lg p-8 text-center hover:bg-white/10 transition-all hover:transform hover:scale-105">
<div className="w-16 h-16 bg-primary rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
</div>
<h3 className="text-xl font-bold mb-2">Telefon</h3>
<a href="tel:+4915732469960" className="text-white/80 hover:text-primary transition-colors">
+49 157 32469960
</a>
</div>
{/* Email */}
<div className="bg-white/5 backdrop-blur-sm rounded-lg p-8 text-center hover:bg-white/10 transition-all hover:transform hover:scale-105">
<div className="w-16 h-16 bg-primary rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
</div>
<h3 className="text-xl font-bold mb-2">Email</h3>
<a
href="mailto:froehlich.sebastian@gmail.com?subject=Antwort - Ihre Bewerbung"
className="text-white/80 hover:text-primary transition-colors break-all"
>
froehlich.sebastian@gmail.com
</a>
</div>
</div>
</div>
</section>
);
}
+42
View File
@@ -0,0 +1,42 @@
interface DownloadSectionProps {
title: string;
subtitle: string;
buttonText: string;
href: string;
backgroundImage: string;
}
export default function DownloadSection({
title,
subtitle,
buttonText,
href,
backgroundImage,
}: DownloadSectionProps) {
return (
<section
className="relative py-24 px-4 sm:px-6 lg:px-8 bg-cover bg-center bg-fixed"
style={{ backgroundImage: `url(${backgroundImage})` }}
>
{/* Overlay */}
<div className="absolute inset-0 bg-black/60" />
<div className="relative z-10 max-w-4xl mx-auto text-center">
<h2 className="text-4xl sm:text-5xl font-bold text-white mb-4">
{title}
</h2>
<p className="text-xl sm:text-2xl text-white/90 mb-8">
{subtitle}
</p>
<a
href={href}
target="_blank"
rel="noopener noreferrer"
className="inline-block px-10 py-4 bg-primary text-white font-bold text-lg rounded-md hover:bg-primary-dark transition-all transform hover:scale-105 shadow-lg hover:shadow-xl"
>
{buttonText}
</a>
</div>
</section>
);
}
+256
View File
@@ -0,0 +1,256 @@
'use client';
import { useState } from 'react';
interface Job {
id: string;
title: string;
company: string;
location: string;
period: string;
description: string;
responsibilities: string[];
}
const jobs: Job[] = [
{
id: 'job1',
title: 'Technischer Product Owner',
company: 'IT im Bereich Finanzen und Banken',
location: 'Regulatory & Risk, Nürnberg',
period: 'Juni 2021 - heute',
description: 'Technischer Product Owner in einem Nürnberger Unternehmen, welches die komplette Banken IT steuert.',
responsibilities: [
'Sicherstellung des Betriebs',
'Planung und Priorisierung der Ressourcen & Aufgaben',
'Technische Kommunikation und Architektur',
'Migration der Infrastruktur K8s on prem & Cloud',
'Aufbau neuer Services',
],
},
{
id: 'job2',
title: 'Product Owner',
company: 'Mobile Applikationen Unternehmen',
location: 'Nürnberg',
period: 'Februar 2019 - März 2020',
description: 'Product Owner in einem Nürnberger Unternehmen, welches mobile Applikationen zur Digitalisierung von Prozessen herstellt.',
responsibilities: [
'Abklärung der Anforderungen zwischen Stakeholdern und Entwicklern',
'Planung und Priorisierung der Ressourcen & Aufgaben',
'Technische Kommunikation und Architektur',
'Migration der bestehenden Infrastruktur in die MagentaCloud (Kubernetes)',
'Weiterentwicklung des Produktes',
],
},
{
id: 'job3',
title: 'Leiter IT',
company: 'Unternehmen im Bereich Fahrzeugtechnik',
location: 'Georgensgmünd',
period: 'März 2014 - Januar 2019',
description: 'Leiter einer IT-Abteilung in einem mittelfränkischen Unternehmen aus dem Bereich Fahrzeugtechnik.',
responsibilities: [
'Programmierung & Weiterentwicklung der Online Shops',
'Anbindung der Warenwirtschaft',
'Aufbau und Entwicklung eines Intranets',
'Architektur der Datenbanken',
'API Schnittstellenprogrammierung',
'Migration/Neuaufbau und Entwicklung der Linux Server',
],
},
{
id: 'job4',
title: 'Marketing Manager',
company: 'Trustec Energy GmbH',
location: 'Solarbranche, Mittelfranken',
period: 'Januar 2010 - März 2014',
description: 'Marketing Manager in einem Unternehmen der Solarbranche mit Verantwortung für die komplette digitale Präsenz und Marketing-Kommunikation.',
responsibilities: [
'Aufbau, Programmierung und Pflege der Firmenwebseiten (trustec-energy.eu, mhm-marketing.com) mit Joomla CMS',
'Entwicklung und Programmierung neuer CMS-Inhaltstypen und Module',
'E-Mail Marketing: Erstellung, Versand und Erfolgsmessung von Newslettern und Messe-Respondern',
'Print Design: Kataloge, Preislisten, Flyer, Rollups und Messestände (InDesign, Photoshop, Illustrator)',
'Erstellung von 3D Produkt Mock-ups und Landing Pages',
'Web Analytics mit Google Analytics und Piwik',
'Koordination externer Dienstleister (Grafiker, Übersetzer, Messebauer)',
'IT-Support: Betreuung Windows Server 2008, CRM-Software und Telefonanlage',
],
},
{
id: 'job5',
title: 'Freelancer Web Development & Design',
company: 'Selbstständig',
location: 'Verschiedene Branchen',
period: 'Januar 2000 - Dezember 2010',
description: 'Über 10 Jahre selbstständig für Unternehmenskunden aus unterschiedlichen Branchen tätig, mit Fokus auf webbasierte Lösungen.',
responsibilities: [
'Entwicklung von Präsentations- und Buchungssystemen',
'Programmierung von CMS-Systemen und Content-Management-Lösungen',
'Aufbau und Entwicklung von E-Commerce Shops',
'Webdesign und Frontend-Entwicklung (HTML, CSS, JavaScript, Flash/Actionscript)',
'Kundenberatung und Anforderungsanalyse',
'Print Design und Corporate Design Entwicklung',
],
},
];
export default function Experience() {
const [activeJob, setActiveJob] = useState(0);
return (
<section id="experience" className="py-20 px-4 sm:px-6 lg:px-8 bg-white">
<div className="max-w-6xl mx-auto">
<h2 className="text-3xl sm:text-4xl font-bold text-text text-center mb-16 relative inline-block w-full after:content-[''] after:block after:w-12 after:h-1 after:bg-primary after:mt-6 after:mx-auto">
Berufserfahrung
</h2>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Job Tabs - Jetzt mit besserer Mobile-Darstellung */}
<div className="lg:col-span-1">
<div className="flex flex-col gap-3">
{jobs.map((job, index) => (
<button
key={job.id}
onClick={() => setActiveJob(index)}
className={`text-left p-4 sm:p-5 rounded-lg transition-all duration-300 ${
activeJob === index
? 'bg-primary text-white shadow-lg scale-105'
: 'bg-gray-100 text-text hover:bg-gray-200 hover:shadow-md'
}`}
>
<h3 className="font-bold text-sm sm:text-base lg:text-lg mb-1">
{job.title}
</h3>
<p className={`text-xs sm:text-sm ${activeJob === index ? 'text-white/80' : 'text-text-light'}`}>
{job.company}
</p>
<p className={`text-xs mt-1 font-semibold ${activeJob === index ? 'text-white/90' : 'text-primary'}`}>
{job.period.split(' - ')[0]}
</p>
</button>
))}
</div>
</div>
{/* Job Details */}
<div className="lg:col-span-2">
<div className="bg-bg-light rounded-lg p-6 sm:p-8 shadow-md hover:shadow-xl transition-shadow">
{/* Header */}
<div className="mb-6 pb-6 border-b border-gray-200">
<h3 className="text-xl sm:text-2xl lg:text-3xl font-bold text-text mb-3">
{jobs[activeJob].title}
</h3>
<div className="flex flex-col gap-2 mb-2">
<p className="text-base sm:text-lg font-semibold text-text-light flex items-center">
<svg className="w-5 h-5 mr-2 text-primary flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
</svg>
{jobs[activeJob].company}
</p>
<p className="text-sm sm:text-base text-text-light flex items-center">
<svg className="w-5 h-5 mr-2 text-primary flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
{jobs[activeJob].location}
</p>
</div>
<p className="text-primary font-bold text-base sm:text-lg flex items-center">
<svg className="w-5 h-5 mr-2 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
{jobs[activeJob].period}
</p>
</div>
{/* Description */}
<p className="text-text-light mb-6 text-sm sm:text-base leading-relaxed">
{jobs[activeJob].description}
</p>
{/* Responsibilities */}
<h4 className="font-bold text-text mb-4 text-base sm:text-lg flex items-center">
<svg className="w-5 h-5 mr-2 text-primary flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" />
</svg>
Verantwortlichkeiten:
</h4>
<ul className="space-y-3">
{jobs[activeJob].responsibilities.map((resp, idx) => (
<li
key={idx}
className="flex items-start group hover:translate-x-1 transition-transform"
>
<svg
className="w-5 h-5 text-primary mt-0.5 mr-3 flex-shrink-0 group-hover:scale-110 transition-transform"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clipRule="evenodd"
/>
</svg>
<span className="text-text-light leading-relaxed text-sm sm:text-base">{resp}</span>
</li>
))}
</ul>
{/* Tags for quick tech overview */}
<div className="mt-8 pt-6 border-t border-gray-200">
<div className="flex flex-wrap gap-2">
{activeJob === 0 && (
<>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Kubernetes</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Cloud Migration</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Systemarchitektur</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Financial IT</span>
</>
)}
{activeJob === 1 && (
<>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Kubernetes</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Mobile Apps</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Stakeholder Management</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Digitalisierung</span>
</>
)}
{activeJob === 2 && (
<>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">E-Commerce</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Laravel</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Vue.js</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">API Development</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Team Leadership</span>
</>
)}
{activeJob === 3 && (
<>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Joomla</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">E-Mail Marketing</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Adobe Suite</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Google Analytics</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">3D Design</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Print Design</span>
</>
)}
{activeJob === 4 && (
<>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">HTML/CSS/JS</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">CMS Development</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">E-Commerce</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Flash/Actionscript</span>
<span className="px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">Webdesign</span>
</>
)}
</div>
</div>
</div>
</div>
</div>
</div>
</section>
);
}
+28
View File
@@ -0,0 +1,28 @@
export default function Footer() {
return (
<footer className="bg-bg-dark text-white py-8 px-4 sm:px-6 lg:px-8">
<div className="max-w-6xl mx-auto">
<div className="flex flex-col md:flex-row justify-between items-center gap-4">
<div className="text-center md:text-left">
<h3 className="text-xl font-bold mb-1">Sebastian Fröhlich</h3>
<p className="text-white/60 text-sm">Full-Stack Developer, technischer PO, Team Leader</p>
</div>
<div className="text-center md:text-right">
<p className="text-white/80 font-semibold mb-2">Nicht alles hat ein Ende...</p>
<a
href="mailto:froehlich.sebastian@gmail.com?subject=Antwort - Ihre Bewerbung"
className="inline-block px-6 py-2 bg-primary text-white font-semibold rounded-lg hover:bg-primary-dark transition-colors"
>
Jetzt Kontakt aufnehmen
</a>
</div>
</div>
<div className="mt-8 pt-8 border-t border-white/10 text-center text-white/60 text-sm">
<p>&copy; {new Date().getFullYear()} Sebastian Fröhlich. Alle Rechte vorbehalten.</p>
</div>
</div>
</footer>
);
}
+81
View File
@@ -0,0 +1,81 @@
'use client';
import { useEffect, useState } from 'react';
import Image from 'next/image';
export default function Hero() {
const [scrollY, setScrollY] = useState(0);
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
const handleScroll = () => setScrollY(window.scrollY);
const handleResize = () => setIsMobile(window.innerWidth < 768);
handleResize(); // Initial check
window.addEventListener('scroll', handleScroll);
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('scroll', handleScroll);
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<section
id="hero"
className="relative min-h-screen flex items-center justify-center overflow-hidden"
>
{/* Background Image mit Next.js Image */}
<div className="absolute inset-0 w-full h-full">
<Image
src={isMobile ? "/img/bg-ich-mobile.jpg" : "/img/bg-ich-2.png"}
alt="Hero Background"
fill
priority
quality={90}
className="object-cover object-center"
style={{
transform: `translateY(${scrollY * 0.5}px)`,
}}
/>
{/* Overlay für bessere Lesbarkeit */}
<div className="absolute inset-0 bg-gradient-to-br from-slate-900/80 via-slate-800/70 to-slate-900/80" />
</div>
<div className="relative z-10 text-center px-4 sm:px-6 lg:px-8">
<div className="space-y-6 animate-fade-in">
<h1 className="text-5xl sm:text-6xl lg:text-7xl font-bold text-white tracking-wider drop-shadow-2xl">
be<span className="text-primary">Werbung</span>
</h1>
<p className="text-xl sm:text-2xl text-slate-300 max-w-2xl mx-auto drop-shadow-lg">
Die Bewerberwebseite von <br />
<em className="text-primary font-semibold">Sebastian Fröhlich</em>
</p>
</div>
{/* Scroll indicator */}
<div className="absolute bottom-10 left-1/2 -translate-x-1/2 animate-bounce">
<button
onClick={() => document.getElementById('intro')?.scrollIntoView({ behavior: 'smooth' })}
className="w-12 h-12 rounded-full bg-primary/20 backdrop-blur-sm border-2 border-primary flex items-center justify-center hover:bg-primary/30 transition-all group"
aria-label="Scroll nach unten"
>
<svg
className="w-6 h-6 text-primary group-hover:translate-y-1 transition-transform"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path d="M19 14l-7 7m0 0l-7-7m7 7V3"></path>
</svg>
</button>
</div>
</div>
</section>
);
}
+82
View File
@@ -0,0 +1,82 @@
'use client';
import { useEffect, useState } from 'react';
export default function Hero() {
const [scrollY, setScrollY] = useState(0);
useEffect(() => {
const handleScroll = () => setScrollY(window.scrollY);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return (
<section
id="hero"
className="relative min-h-screen flex items-center justify-center overflow-hidden"
>
{/* Background Layer 1 - Wiederholendes Muster - passt sich an Bildschirmhöhe an */}
<div
className="absolute inset-0 w-full h-full"
style={{
backgroundImage: 'url(/img/bg-ich-2025-background.jpg)',
backgroundRepeat: 'repeat',
backgroundSize: 'auto 100%',
backgroundPosition: 'center',
transform: `translateY(${scrollY * 0.3}px)`,
}}
/>
{/* Background Layer 2 - Portrait rechts (PNG mit Transparenz) */}
<div
className="absolute inset-0 w-full h-full"
style={{
backgroundImage: 'url(/img/bg-ich-2025-foreground.png)',
backgroundRepeat: 'no-repeat',
backgroundSize: 'auto 100%',
backgroundPosition: 'right center',
transform: `translateY(${scrollY * 0.5}px)`,
}}
/>
{/* Overlay für bessere Lesbarkeit */}
<div className="absolute inset-0 bg-gradient-to-r from-slate-900/80 via-slate-900/50 to-transparent sm:from-slate-900/70 sm:via-slate-900/40 lg:from-slate-900/60 lg:via-slate-900/20" />
{/* Content - Responsive Positionierung */}
<div className="relative z-10 w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="max-w-full sm:max-w-2xl lg:max-w-xl space-y-6 animate-fade-in">
<h1 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold text-white tracking-wider drop-shadow-2xl">
be<span className="text-primary">Werbung</span>
</h1>
<p className="text-lg sm:text-xl md:text-2xl text-slate-300 drop-shadow-lg">
Die Bewerberwebseite von <br />
<em className="text-primary font-semibold">Sebastian Fröhlich</em>
</p>
</div>
</div>
{/* Scroll indicator */}
<div className="absolute bottom-10 left-1/2 -translate-x-1/2 animate-bounce z-20">
<button
onClick={() => document.getElementById('intro')?.scrollIntoView({ behavior: 'smooth' })}
className="w-12 h-12 rounded-full bg-primary/20 backdrop-blur-sm border-2 border-primary flex items-center justify-center hover:bg-primary/30 transition-all group"
aria-label="Scroll nach unten"
>
<svg
className="w-6 h-6 text-primary group-hover:translate-y-1 transition-transform"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path d="M19 14l-7 7m0 0l-7-7m7 7V3"></path>
</svg>
</button>
</div>
</section>
);
}
+258
View File
@@ -0,0 +1,258 @@
'use client';
import { useEffect, useRef, useState } from 'react';
export default function Intro() {
const [isVisible, setIsVisible] = useState(false);
const ref = useRef<HTMLElement>(null);
useEffect(() => {
const currentRef = ref.current;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
}
},
{ threshold: 0.1 }
);
if (currentRef) {
observer.observe(currentRef);
}
return () => {
if (currentRef) {
observer.unobserve(currentRef);
}
};
}, []);
const highlights = [
{ value: '20+', label: 'Jahre Erfahrung' },
{ value: '10+', label: 'Jahre Freelancing' },
{ value: '9+', label: 'Mitarbeiter geführt' },
{ value: '3', label: 'Produkte verantwortet' },
];
const skills = [
'Product Owner',
'Technical Project Manager',
'Laravel',
'Vue.js',
'PHP',
'JavaScript',
'MySQL',
'MSSQL',
'Systemarchitektur',
'User Experience',
'Team Leadership',
'Compliance',
];
const timeline = [
{ year: '2021 - heute', company: 'Sopra Financial Technology GmbH', role: 'Technical Product Owner' },
{ year: '2019 - 2020', company: 'APPsolute Mobility', role: 'Product Owner & Technical Project Manager' },
{ year: '2014 - 2019', company: 'SCC Fahrzeugtechnik GmbH', role: 'Leiter IT Abteilung' },
{ year: '2010 - 2014', company: 'Trustec Energy GmbH', role: 'Marketing Manager' },
{ year: '2000 - 2010', company: 'Freelancer', role: 'Web Development & Design' },
];
return (
<section
id="intro"
ref={ref}
className={`py-20 px-4 sm:px-6 lg:px-8 bg-white transition-opacity duration-1000 ${
isVisible ? 'opacity-100' : 'opacity-0'
}`}
>
<div className="max-w-6xl mx-auto">
<h2 className="text-3xl sm:text-4xl font-bold text-text mb-12 text-center relative inline-block w-full after:content-[''] after:block after:w-12 after:h-1 after:bg-primary after:mt-6 after:mx-auto">
Über mich
</h2>
{/* Highlights Box */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-12">
{highlights.map((item, index) => (
<div
key={index}
className={`bg-gradient-to-br from-primary/10 to-primary/5 rounded-lg p-6 text-center border border-primary/20 hover:border-primary/40 transition-all hover:shadow-lg ${
isVisible ? 'animate-fade-in' : 'opacity-0'
}`}
style={{ animationDelay: `${index * 100}ms` }}
>
<div className="text-3xl sm:text-4xl font-bold text-primary mb-2">
{item.value}
</div>
<div className="text-sm text-text-light font-semibold">
{item.label}
</div>
</div>
))}
</div>
{/* Main Text */}
<div className="max-w-4xl mx-auto space-y-6 text-text-light leading-relaxed text-base sm:text-lg mb-12">
<p>
Mein Name ist Sebastian Fröhlich, ich bin 47 Jahre alt und lebe zusammen mit meiner
Familie in Neuendettelsau im Landkreis Ansbach.
</p>
<p>
Von der Diskette zur Cloud durfte ich diesen spannenden und rasanten technischen
Wandel miterleben. Besonders die Themen{' '}
<strong className="text-text font-semibold">
redundante Datenerfassung, Systemarchitektur und User Experience
</strong>{' '}
haben mich schon immer besonders fasziniert.
</p>
<p>
Bereits während meiner Berufsausbildung wurde mir die Verantwortung für den
Firmenserver (Netware) übertragen.
</p>
<p>
Für die A-Socks GmbH, einem Unternehmen aus der Textilbranche entwickelte ich einen{' '}
<strong className="text-text font-semibold">
Shop (HTML/ Javascript/ Actionscript - Flash)
</strong>, gestaltete die Verkaufsverpackungen und programmierte ein automatisiertes
EAN Vergabe System.
</p>
<p>
Als Freelancer war ich über 10 Jahre für Unternehmenskunden aus unterschiedlichen
Branchen tätig. Mein Fokus lag dabei auf webbasierten Lösungen wie z.B.{' '}
<strong className="text-text font-semibold">
Präsentations- und Buchungssystemen, CMS-Systemen und Internetshops
</strong>.
</p>
<p>
Im Jahr 2010 erhielt ich die Gelegenheit meine Kompetenzen als Marketing Manager für
die Trustec Energy GmbH in der Solarbranche einzusetzen. Nach 4 Jahren erhielt ich eine
betriebsbedingte Kündigung und wechselte 2014 zur SCC Fahrzeugtechnik GmbH.
</p>
<p>
Als{' '}
<strong className="text-text font-semibold">Leiter der IT Abteilung</strong> war ich
hier fachlich und disziplinarisch für 6 Mitarbeiter und 3 Auszubildende verantwortlich.
Darüber hinaus entwickelte und programmierte ich ein eigenes Firmen-Intranet{' '}
<strong className="text-text font-semibold">
(PHP Framework: Laravel, Javascript/ Vue.js, MySql/ MSSQL)
</strong>, wartete und migrierte die Server und sorgte für ein funktionierendes Backup-
und Sicherheitssystem{' '}
<strong className="text-text font-semibold">(Master-Master Replication, BashScript)</strong>.
</p>
<p>
Die nächste Karrierestufe erreichte ich mit meinem Wechsel zu einem Startupunternehmen,
wo ich als{' '}
<strong className="text-text font-semibold">
Product Owner & Technical Project Manager
</strong>{' '}
für die Firma &quot;APPsolute Mobility&quot; tätig war. Leider konnte das Startup die gesetzten
Ziele nicht erreichen, weswegen ich 1 Jahr später zu meinem jetzigen Arbeitgeber wechselte.
</p>
<p>
Bei der{' '}
<strong className="text-text font-semibold">Sopra Financial Technology GmbH</strong> bin
ich seit 2021 als{' '}
<strong className="text-text font-semibold">Technical Product Owner</strong> im
Bereich Regulatory & Risk tätig. Ich verantworte{' '}
<strong className="text-text font-semibold">3 Fintech-Produkte</strong> im{' '}
<strong className="text-text font-semibold">Bankenmeldewesen</strong> und stelle
deren reibungslosen Betrieb unter strikten{' '}
<strong className="text-text font-semibold">regulatorischen Vorgaben</strong> sicher.
</p>
<p>
Zu meinen Kernaufgaben gehören die{' '}
<strong className="text-text font-semibold">
technische Projektleitung komplexer Migrationen
</strong>{' '}
(z.B. Kubernetes-Migration ohne Ausfallzeiten), die Steuerung{' '}
<strong className="text-text font-semibold">
internationaler Teams (inkl. Nearshore)
</strong>, Code Reviews sowie die aktive Teilnahme an{' '}
<strong className="text-text font-semibold">Cost-Savings-Programmen</strong>.
Darüber hinaus arbeite ich in den Bereichen{' '}
<strong className="text-text font-semibold">
Managed Application & Cloud Service
</strong>, wo ich neue Produkte architektonisch begleite und bei
Bieterverfahren meine Expertise einbringe.
</p>
<p className="pt-4 border-t border-gray-200">
Mit dieser Präsentation möchte ich die Gelegenheit nutzen, mich als motivierten und
kompetenten Bewerber bei Ihrem Unternehmen vorzustellen.
</p>
<p>
Nachfolgend können Sie sich einen Eindruck über mich und meine abgeschlossenen Projekte{' '}
<a
href="#portfolio"
className="text-primary hover:text-primary-dark font-semibold underline underline-offset-4 transition-colors"
>
hier
</a>{' '}
verschaffen. Gerne überzeuge ich Sie auch in einem persönlichen Gespräch von mir.
</p>
</div>
{/* Timeline */}
<div className="mb-12">
<h3 className="text-2xl font-bold text-text mb-8 text-center">
Karriere-Timeline
</h3>
<div className="max-w-3xl mx-auto">
{timeline.map((item, index) => (
<div
key={index}
className={`relative pl-8 pb-8 border-l-2 border-primary/30 last:border-transparent ${
isVisible ? 'animate-fade-in' : 'opacity-0'
}`}
style={{ animationDelay: `${(index + 4) * 100}ms` }}
>
{/* Timeline dot */}
<div className="absolute -left-[9px] top-0 w-4 h-4 rounded-full bg-primary border-4 border-white shadow-md" />
<div className="bg-bg-light rounded-lg p-6 hover:shadow-lg transition-shadow">
<div className="flex flex-col sm:flex-row sm:justify-between sm:items-start mb-2">
<h4 className="text-lg font-bold text-text">{item.role}</h4>
<span className="text-sm font-semibold text-primary whitespace-nowrap">
{item.year}
</span>
</div>
<p className="text-text-light">{item.company}</p>
</div>
</div>
))}
</div>
</div>
{/* Skills Tags */}
<div className="max-w-4xl mx-auto">
<h3 className="text-2xl font-bold text-text mb-6 text-center">
Kernkompetenzen
</h3>
<div className="flex flex-wrap justify-center gap-3">
{skills.map((skill, index) => (
<span
key={index}
className={`px-4 py-2 bg-primary/10 text-primary font-semibold rounded-full text-sm border border-primary/20 hover:bg-primary hover:text-white transition-all cursor-default hover:scale-105 ${
isVisible ? 'animate-fade-in' : 'opacity-0'
}`}
style={{ animationDelay: `${(index + timeline.length + 4) * 50}ms` }}
>
{skill}
</span>
))}
</div>
</div>
</div>
</section>
);
}
+255
View File
@@ -0,0 +1,255 @@
'use client';
import { useState } from 'react';
import Image from 'next/image';
interface Project {
id: string;
title: string;
category: 'web' | 'print';
description: string;
image: string;
details: string[];
link?: string;
}
const projects: Project[] = [
{
id: 'estev',
title: 'e-stev Radsport',
category: 'web',
description: '2013 für die STEV Radport GmbH in Gunzenhausen wurde die Webseite e-stev von Grund auf neu geschaffen und für ein CMS umgesetzt.',
image: '/img/Referenz - e-stev -Web.jpg',
details: [
'Modernes Responsive Design',
'CMS-Integration für einfache Pflege',
'Produktkatalog für E-Bikes',
'Kontaktformular mit Kartenfunktion',
],
link: 'https://e-stev.de',
},
{
id: 'planet',
title: 'Planet-Touristik',
category: 'web',
description: 'Für Planet-Touristik, das Reisebüro im Nürnberger Norden, wurde die Startseite 2014 einem Redesign unterworfen.',
image: '/img/Referenz---Planet-Touristik---web.jpg',
details: [
'Redesign der Startseite',
'Optimierung der User Experience',
'Integration in bestehendes System',
'Responsive Layout',
],
},
{
id: 'handstaubsauger',
title: 'Handstaubsauger24.de',
category: 'web',
description: 'Für handstaubsauger24.de wurde ein Magento E-Commerce Template gestaltet.',
image: '/img/Referenz---handstaubsauger---web.jpg',
details: [
'Magento Theme Development',
'E-Commerce Optimierung',
'Produktfilter und Suche',
'Mobile-First Ansatz',
],
},
{
id: 'illy',
title: 'Illy Caffè Ansbach',
category: 'print',
description: '2007 für das Illy Caffe in Ansbach entstandener Trifolder als Menükarte.',
image: '/img/Referenz---Illy---Print.jpg',
details: [
'Tri-Folder Design',
'Hochwertige Druckvorlagen',
'Corporate Design Integration',
'Fotografie und Layout',
],
},
{
id: 'sz',
title: 'SZ Kirchbirlingen',
category: 'print',
description: '2008 für die SZ in Kirchbirlingen entstandener Flyer als Wurfsendung.',
image: '/img/Referenz---SZ---Print.jpg',
details: [
'DIN Lang Format',
'Wurfsendung Konzept',
'Print Design',
'Layout und Satz',
],
},
{
id: 'planet-print',
title: 'Planet-Touristik Flyer',
category: 'print',
description: '2014 wurde ein neuer Flyer für Planet Touristik geschaffen.',
image: '/img/Referenz---Planet-Touristik---print.jpg',
details: [
'DIN A5 Format',
'Verteiler und Auslage',
'Moderne Gestaltung',
'Print-Produktion',
],
},
];
export default function Portfolio() {
const [activeCategory, setActiveCategory] = useState<'all' | 'web' | 'print'>('all');
const [selectedProject, setSelectedProject] = useState<Project | null>(null);
const [imageErrors, setImageErrors] = useState<Set<string>>(new Set());
const filteredProjects =
activeCategory === 'all'
? projects
: projects.filter((p) => p.category === activeCategory);
const handleImageError = (projectId: string) => {
setImageErrors(prev => new Set(prev).add(projectId));
};
return (
<section id="portfolio" className="py-20 px-4 sm:px-6 lg:px-8 bg-white">
<div className="max-w-7xl mx-auto">
<h2 className="text-3xl sm:text-4xl font-bold text-text text-center mb-6 relative inline-block w-full after:content-[''] after:block after:w-12 after:h-1 after:bg-primary after:mt-6 after:mx-auto">
Portfolio
</h2>
<p className="text-text-light text-center mb-12 max-w-3xl mx-auto">
Zwischen 2000 bis 2010 war ich auch als Freelancer tätig und arbeitete mit Kunden und
Agenturen zusammen. Eine Auswahl meiner Arbeiten aus diesem Zeitraum habe ich hier
zusammengestellt.
</p>
{/* Filter Buttons */}
<div className="flex justify-center gap-4 mb-12">
{(['all', 'web', 'print'] as const).map((cat) => (
<button
key={cat}
onClick={() => setActiveCategory(cat)}
className={`px-6 py-3 rounded-lg font-semibold transition-all capitalize ${
activeCategory === cat
? 'bg-primary text-white shadow-lg'
: 'bg-gray-100 text-text hover:bg-gray-200'
}`}
>
{cat === 'all' ? 'Alle' : cat === 'web' ? 'Web' : 'Print'}
</button>
))}
</div>
{/* Projects Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{filteredProjects.map((project) => (
<div
key={project.id}
className="group relative overflow-hidden rounded-lg shadow-lg hover:shadow-2xl transition-all duration-300 cursor-pointer bg-white"
onClick={() => setSelectedProject(project)}
>
<div className="relative h-64 bg-gray-200 overflow-hidden">
{!imageErrors.has(project.id) ? (
<Image
src={project.image}
alt={project.title}
fill
className="object-cover group-hover:scale-110 transition-transform duration-300"
onError={() => handleImageError(project.id)}
/>
) : (
<div className="w-full h-full bg-gradient-to-br from-primary/20 to-primary/5 flex items-center justify-center">
<div className="text-center p-4">
<svg className="w-16 h-16 mx-auto mb-2 text-primary/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<p className="text-sm text-gray-500">{project.title}</p>
</div>
</div>
)}
</div>
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex flex-col justify-end p-6">
<h3 className="text-white font-bold text-xl mb-2">{project.title}</h3>
<p className="text-white/90 text-sm line-clamp-2">{project.description}</p>
<button className="mt-4 text-primary font-semibold hover:text-white transition-colors">
Details ansehen →
</button>
</div>
<div className="p-4 bg-white">
<h3 className="font-bold text-text mb-2">{project.title}</h3>
<span className="inline-block px-3 py-1 bg-primary/10 text-primary text-xs font-semibold rounded-full">
{project.category === 'web' ? 'Web Design' : 'Print Design'}
</span>
</div>
</div>
))}
</div>
</div>
{/* Modal */}
{selectedProject && (
<div
className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4"
onClick={() => setSelectedProject(null)}
>
<div
className="bg-white rounded-lg max-w-2xl w-full max-h-[90vh] overflow-y-auto"
onClick={(e) => e.stopPropagation()}
>
<div className="sticky top-0 bg-white border-b p-4 flex justify-between items-center z-10">
<h3 className="text-2xl font-bold text-text">{selectedProject.title}</h3>
<button
onClick={() => setSelectedProject(null)}
className="text-gray-500 hover:text-text transition-colors"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div className="p-6">
<div className="relative h-64 bg-gray-200 rounded-lg mb-6 overflow-hidden">
{!imageErrors.has(selectedProject.id) ? (
<Image
src={selectedProject.image}
alt={selectedProject.title}
fill
className="object-cover"
onError={() => handleImageError(selectedProject.id)}
/>
) : (
<div className="w-full h-full bg-gradient-to-br from-primary/20 to-primary/5 flex items-center justify-center">
<div className="text-center">
<svg className="w-20 h-20 mx-auto mb-2 text-primary/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
<p className="text-sm text-gray-500">Bild wird geladen...</p>
</div>
</div>
)}
</div>
<p className="text-text-light mb-6">{selectedProject.description}</p>
<h4 className="font-bold text-text mb-3">Details:</h4>
<ul className="space-y-2 mb-6">
{selectedProject.details.map((detail, idx) => (
<li key={idx} className="flex items-start">
<svg className="w-5 h-5 text-primary mt-0.5 mr-2 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
</svg>
<span className="text-text-light">{detail}</span>
</li>
))}
</ul>
</div>
</div>
</div>
)}
</section>
);
}
+170
View File
@@ -0,0 +1,170 @@
'use client';
import { useEffect, useRef, useState } from 'react';
interface Skill {
name: string;
level: number; // 1-5
category: string;
}
const skillsData: Skill[] = [
// Programmiersprachen
{ name: 'PHP & FW Laravel', level: 4, category: 'Programmiersprachen' },
{ name: 'Python & Perl', level: 4, category: 'Programmiersprachen' },
{ name: 'JavaScript', level: 4, category: 'Programmiersprachen' },
{ name: 'Node.js', level: 3, category: 'Programmiersprachen' },
{ name: 'Vue.js / Next.js', level: 4, category: 'Programmiersprachen' },
{ name: 'HTML', level: 5, category: 'Programmiersprachen' },
{ name: 'CSS & Frameworks', level: 5, category: 'Programmiersprachen' },
{ name: 'Sass', level: 4, category: 'Programmiersprachen' },
// Datenbanken
{ name: 'SQL (MS/DB2/Oracle)', level: 4, category: 'Datenbanken' },
{ name: 'MySQL, MariaDB, Postgres', level: 4, category: 'Datenbanken' },
{ name: 'Redis', level: 4, category: 'Datenbanken' },
{ name: 'MongoDB', level: 3, category: 'Datenbanken' },
{ name: 'CouchDB', level: 3, category: 'Datenbanken' },
{ name: 'Firebase', level: 1, category: 'Datenbanken' },
// DevOps
{ name: 'Linux Bash Scripting', level: 4, category: 'DevOps' },
{ name: 'Git / SVN', level: 4, category: 'DevOps' },
{ name: 'Docker / Kubernetes', level: 3, category: 'DevOps' },
{ name: 'AWS', level: 3, category: 'DevOps' },
{ name: 'CI/CD Pipelines', level: 4, category: 'DevOps' },
{ name: 'Draw.io Architektur', level: 4, category: 'DevOps' },
// Grafik & Design
{ name: 'Adobe Suite (u.a. PS/XD)', level: 4, category: 'Grafik & Design' },
{ name: 'Affinity', level: 4, category: 'Grafik & Design' },
{ name: 'FinalCut', level: 4, category: 'Grafik & Design' },
];
const categories = [
'Programmiersprachen',
'Datenbanken',
'DevOps',
'Grafik & Design',
];
export default function Skills() {
const [activeCategory, setActiveCategory] = useState<string>('Programmiersprachen');
const [isVisible, setIsVisible] = useState(false);
const ref = useRef<HTMLElement>(null);
useEffect(() => {
const currentRef = ref.current;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
}
},
{ threshold: 0.1 }
);
if (currentRef) {
observer.observe(currentRef);
}
return () => {
if (currentRef) {
observer.unobserve(currentRef);
}
};
}, []);
const filteredSkills = skillsData.filter((skill) => skill.category === activeCategory);
return (
<section
id="skills"
ref={ref}
className={`py-20 px-4 sm:px-6 lg:px-8 bg-gradient-to-br from-bg-light to-white transition-opacity duration-1000 ${
isVisible ? 'opacity-100' : 'opacity-0'
}`}
>
<div className="max-w-6xl mx-auto">
<h2 className="text-3xl sm:text-4xl font-bold text-text text-center mb-4 relative inline-block w-full">
Technische Fähigkeiten
</h2>
<p className="text-center text-text-light mb-12 max-w-2xl mx-auto">
Umfassende Expertise in modernen Technologien und Frameworks
</p>
{/* Category Tabs */}
<div className="flex flex-wrap justify-center gap-3 mb-12">
{categories.map((category, index) => (
<button
key={category}
onClick={() => setActiveCategory(category)}
className={`px-5 py-3 rounded-lg font-semibold text-sm transition-all duration-300 ${
activeCategory === category
? 'bg-primary text-white shadow-lg scale-105'
: 'bg-white text-text-light hover:bg-gray-100 hover:text-text shadow-md'
} ${isVisible ? 'animate-fade-in' : 'opacity-0'}`}
style={{ animationDelay: `${index * 50}ms` }}
>
{category}
</button>
))}
</div>
{/* Skills Grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredSkills.map((skill, index) => (
<div
key={skill.name}
className={`bg-white rounded-lg p-6 shadow-md hover:shadow-xl transition-all duration-300 hover:-translate-y-1 ${
isVisible ? 'animate-fade-in' : 'opacity-0'
}`}
style={{ animationDelay: `${(index + categories.length) * 50}ms` }}
>
{/* Skill Name */}
<h3 className="font-bold text-text mb-3 text-base">{skill.name}</h3>
{/* Level Bar */}
<div className="flex items-center gap-2">
<div className="flex-1 bg-gray-200 rounded-full h-2.5 overflow-hidden">
<div
className="bg-gradient-to-r from-primary to-primary-dark h-full rounded-full transition-all duration-1000 ease-out"
style={{
width: isVisible ? `${(skill.level / 5) * 100}%` : '0%',
transitionDelay: `${(index + categories.length) * 50 + 200}ms`,
}}
/>
</div>
<span className="text-sm font-bold text-primary min-w-[2rem] text-right">
{skill.level}/5
</span>
</div>
{/* Level Dots */}
<div className="flex gap-1 mt-3">
{[1, 2, 3, 4, 5].map((dot) => (
<div
key={dot}
className={`w-2 h-2 rounded-full transition-all duration-300 ${
dot <= skill.level ? 'bg-primary' : 'bg-gray-300'
}`}
style={{
transitionDelay: `${(index + categories.length) * 50 + dot * 50}ms`,
}}
/>
))}
</div>
</div>
))}
</div>
{/* Info Text */}
<div className="mt-12 text-center">
<p className="text-text-light text-sm">
<strong className="text-primary">Bewertungsskala:</strong> 1 = Grundkenntnisse | 5 = Expertenwissen
</p>
</div>
</div>
</section>
);
}
+133
View File
@@ -0,0 +1,133 @@
'use client';
import { useEffect, useRef, useState } from 'react';
interface SoftSkill {
name: string;
level: number; // 1-5
icon: string;
}
const softSkills: SoftSkill[] = [
{ name: 'Organisation', level: 4, icon: '📋' },
{ name: 'Kommunikation', level: 5, icon: '💬' },
{ name: 'Selbstständigkeit', level: 4, icon: '🎯' },
{ name: 'Teamfähigkeit', level: 5, icon: '🤝' },
{ name: 'Verantwortung', level: 5, icon: '✓' },
{ name: 'Engagement', level: 5, icon: '🚀' },
{ name: 'Kompromissbereitschaft', level: 4, icon: '🤔' },
{ name: 'Aufgeschlossenheit', level: 5, icon: '🌟' },
{ name: 'Lernbereitschaft', level: 5, icon: '📚' },
{ name: 'Zielstrebigkeit', level: 5, icon: '🎯' },
{ name: 'Überzeugungsfähigkeit', level: 4, icon: '💡' },
{ name: 'Lösungsorientiert', level: 5, icon: '🔧' },
];
export default function SoftSkills() {
const [isVisible, setIsVisible] = useState(false);
const ref = useRef<HTMLElement>(null);
useEffect(() => {
const currentRef = ref.current;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
}
},
{ threshold: 0.1 }
);
if (currentRef) {
observer.observe(currentRef);
}
return () => {
if (currentRef) {
observer.unobserve(currentRef);
}
};
}, []);
return (
<section
id="soft-skills"
ref={ref}
className={`py-20 px-4 sm:px-6 lg:px-8 bg-white transition-opacity duration-1000 ${
isVisible ? 'opacity-100' : 'opacity-0'
}`}
>
<div className="max-w-6xl mx-auto">
<h2 className="text-3xl sm:text-4xl font-bold text-text text-center mb-4 relative inline-block w-full">
Persönliche Fähigkeiten
</h2>
<p className="text-center text-text-light mb-12 max-w-2xl mx-auto">
Soft Skills, die mich als Teamplayer und Leader auszeichnen
</p>
{/* Skills Grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{softSkills.map((skill, index) => (
<div
key={skill.name}
className={`group bg-gradient-to-br from-white to-bg-light rounded-xl p-6 shadow-md hover:shadow-2xl transition-all duration-300 hover:-translate-y-2 border border-gray-100 hover:border-primary/30 ${
isVisible ? 'animate-fade-in' : 'opacity-0'
}`}
style={{ animationDelay: `${index * 50}ms` }}
>
{/* Icon */}
<div className="text-4xl mb-4 group-hover:scale-110 transition-transform duration-300">
{skill.icon}
</div>
{/* Skill Name */}
<h3 className="font-bold text-text mb-3 text-base group-hover:text-primary transition-colors">
{skill.name}
</h3>
{/* Level Dots */}
<div className="flex gap-1.5">
{[1, 2, 3, 4, 5].map((dot) => (
<div
key={dot}
className={`w-2.5 h-2.5 rounded-full transition-all duration-300 ${
dot <= skill.level
? 'bg-primary group-hover:scale-125'
: 'bg-gray-300'
}`}
style={{
transitionDelay: `${index * 50 + dot * 30}ms`,
}}
/>
))}
</div>
{/* Hover Effect - Level Text */}
<div className="mt-3 transition-opacity duration-300">
<span className="text-xs text-primary font-semibold">
{skill.level === 5 ? 'Expertenlevel' : skill.level === 4 ? 'Sehr gut' : 'Gut'}
</span>
</div>
</div>
))}
</div>
{/* Bottom Info */}
<div className="mt-16 bg-gradient-to-r from-primary/5 via-primary/10 to-primary/5 rounded-xl p-8 text-center">
<h3 className="text-xl font-bold text-text mb-4">
Warum diese Fähigkeiten wichtig sind
</h3>
<p className="text-text-light leading-relaxed max-w-3xl mx-auto">
In über 20 Jahren Berufserfahrung habe ich gelernt, dass technisches Know-how nur die
halbe Miete ist. Erfolgreiche Projekte entstehen durch{' '}
<strong className="text-primary">klare Kommunikation</strong>,
<strong className="text-primary"> Teamarbeit</strong> und die Fähigkeit, auch in
stressigen Situationen <strong className="text-primary">lösungsorientiert</strong> zu bleiben.
Diese Soft Skills ermöglichen es mir, komplexe technische Anforderungen verständlich zu
vermitteln und Teams erfolgreich zu führen.
</p>
</div>
</div>
</section>
);
}
Executable
+32
View File
@@ -0,0 +1,32 @@
#!/bin/bash
# Script zum Kopieren der Assets aus dem alten Projekt
echo "🚀 Kopiere Assets aus dem alten beWerbung Projekt..."
OLD_PROJECT="/Users/sebastianfrohlich/Projekte/bewerbung"
NEW_PROJECT="/Users/sebastianfrohlich/Projekte/bewerbung2025/bewerbung/public"
# Bilder kopieren
echo "📸 Kopiere Bilder..."
mkdir -p "$NEW_PROJECT/img"
cp -r "$OLD_PROJECT/img/"*.jpg "$NEW_PROJECT/img/" 2>/dev/null || true
cp -r "$OLD_PROJECT/img/"*.png "$NEW_PROJECT/img/" 2>/dev/null || true
cp -r "$OLD_PROJECT/img/"*.svg "$NEW_PROJECT/img/" 2>/dev/null || true
# Downloads kopieren
echo "📄 Kopiere PDFs..."
mkdir -p "$NEW_PROJECT/downloads"
cp -r "$OLD_PROJECT/downloads/"*.pdf "$NEW_PROJECT/downloads/" 2>/dev/null || true
# Workspace Hintergrundbild
echo "🖼️ Kopiere Workspace Hintergrund..."
cp "$OLD_PROJECT/img/Workspace.png" "$NEW_PROJECT/img/workspace-bg.jpg" 2>/dev/null || true
cp "$OLD_PROJECT/img/keyboard-bg.jpg" "$NEW_PROJECT/img/" 2>/dev/null || true
echo "✅ Fertig! Assets wurden kopiert."
echo ""
echo "📋 Nächste Schritte:"
echo "1. npm run dev - Development Server starten"
echo "2. Öffne http://localhost:3000"
echo "3. Prüfe ob alle Bilder korrekt angezeigt werden"
+216
View File
@@ -0,0 +1,216 @@
# 📧 E-Mail Bewerbungsvorlage 2025
Moderne, responsive HTML E-Mail-Vorlage für Bewerbungen - perfekt abgestimmt auf die Webseite apply4jobs.de/bewerbung/
---
## 📋 Was wurde aktualisiert?
### ✅ **Alle wichtigen Daten auf 2025 aktualisiert:**
| Kategorie | Alt (2014) | Neu (2025) |
|-----------|------------|------------|
| **Alter** | 36 Jahre | **46 Jahre** ✅ |
| **Position** | Marketing Manager | **Technical Product Owner** ✅ |
| **Firma** | Trustec Energy | **Sopra Financial Technology** ✅ |
| **Erfahrung** | Webentwicklung, Marketing | **Kubernetes, Cloud, DevOps** ✅ |
| **Downloads** | Dropbox 2014 | **apply4jobs.de/bewerbung/** ✅ |
| **Webseite** | uberspace.de (alt) | **apply4jobs.de/bewerbung/** ✅ |
| **Telefon** | +49 9874 322 429 | **0157 324 699 60** ✅ |
---
## 🎨 Design-Features:
✅ **Moderne Farbpalette:**
- Primary: `#e63946` (Rot - passend zur Webseite)
- Text: `#1d3557` (Dunkelblau)
- Gradient Header: `#1d3557` → `#457b9d`
✅ **Responsive Design:**
- Mobile-optimiert (stacked layout auf kleinen Bildschirmen)
- Funktioniert in allen gängigen E-Mail-Clients
✅ **Strukturiert:**
1. Header mit Positionsangabe
2. Begrüßung + Kurzvorstellung
3. Highlight-Box "Warum ich?"
4. Aktuelle Position (4 Info-Boxen)
5. Download-Buttons (Lebenslauf, Zeugnisse, Referenzen)
6. Online-Bewerbung Button
7. Kontaktdaten (3-spaltig)
8. Abschluss + Footer
---
## 🚀 Verwendung:
### **Schritt 1: Position anpassen**
```html
<!-- Zeile 44: -->
<p style="color: rgba(255, 255, 255, 0.9); margin: 0; font-size: 18px;">
[Position einfügen] ← Hier die Stelle eintragen
</p>
```
### **Schritt 2: Anrede anpassen**
```html
<!-- Zeile 52: -->
<p style="color: #1d3557; font-size: 16px; line-height: 1.6; margin: 0 0 20px 0;">
Sehr geehrte Damen und Herren, ← Bei Bedarf personalisieren
</p>
```
### **Schritt 3: E-Mail versenden**
**Option A: Als HTML-Mail versenden**
- Öffne `bewerbung-2025.html` im Browser
- Kopiere den kompletten Quelltext
- Füge ihn in dein E-Mail-Programm ein (HTML-Modus)
**Option B: In E-Mail-Client importieren**
- Thunderbird: "Einfügen" → "HTML"
- Gmail: Erweiterungen wie "Compose" verwenden
- Outlook: "Datei" → "HTML einfügen"
---
## 📥 Download-Links:
Die E-Mail enthält Links zu:
- ✅ https://apply4jobs.de/bewerbung/downloads/Lebenslauf_2025.pdf
- ✅ https://apply4jobs.de/bewerbung/downloads/Zeugnisse_2025.pdf
- ✅ https://apply4jobs.de/bewerbung/downloads/Referenzen.pdf
- ✅ https://apply4jobs.de/bewerbung/ (Online-Bewerbung)
**Wichtig:** Stelle sicher, dass diese PDFs auf dem Server liegen!
---
## 🧪 Testen:
### **1. Browser-Test:**
```bash
# Öffne die Datei im Browser:
open email-template/bewerbung-2025.html
```
### **2. E-Mail-Client-Test:**
Sende eine Test-E-Mail an dich selbst und prüfe:
- ✅ Layout korrekt (Desktop + Mobile)
- ✅ Alle Links funktionieren
- ✅ Buttons sind klickbar
- ✅ Farben werden richtig angezeigt
### **3. Online-Tools:**
- [Litmus](https://litmus.com/) - E-Mail-Preview in 90+ Clients
- [Email on Acid](https://www.emailonacid.com/) - E-Mail-Testing
- [Mail Tester](https://www.mail-tester.com/) - Spam-Score prüfen
---
## 🎯 Highlights der neuen E-Mail:
### **Inhaltlich:**
✅ Alter korrekt (46 statt 36)
✅ Aktuelle Position (Technical Product Owner)
✅ Moderne Skills (Kubernetes, Cloud, DevOps)
✅ Bankenmeldewesen erwähnt
✅ Nearshore-Teams erwähnt
✅ 20+ Jahre Erfahrung hervorgehoben
### **Visuell:**
✅ Moderne, cleane Optik
✅ Gradient-Header
✅ Highlight-Box mit rotem Akzent
✅ Icon-Boxen (📍 📱 ✉️)
✅ Call-to-Action Buttons
✅ Responsive für Mobile
### **Technisch:**
✅ Alle Links aktualisiert (2025)
✅ Korrekte Telefonnummer
✅ Funktionierende Download-Links
✅ E-Mail-Client kompatibel
---
## 📝 Personalisierungs-Tipps:
### **Für verschiedene Bewerbungen:**
1. **Position anpassen** (Zeile 44)
2. **Anrede personalisieren** (Zeile 52)
3. **Highlight-Box anpassen** (Zeile 70-79)
- Betone Skills, die für die Stelle wichtig sind
### **Beispiele:**
**Für DevOps-Position:**
```html
<li>Kubernetes & Docker Expertise</li>
<li>CI/CD Pipelines (GitLab, Jenkins)</li>
<li>AWS & Cloud Infrastructure</li>
```
**Für Product Owner Position:**
```html
<li>Agile Methoden (Scrum, Kanban)</li>
<li>Stakeholder Management</li>
<li>Technical Background</li>
```
---
## 🆚 Vergleich Alt vs. Neu:
| Feature | Alt (2014) | Neu (2025) |
|---------|------------|------------|
| Alter | 36 (falsch!) | 46 ✅ |
| Position | Marketing Manager | Technical Product Owner ✅ |
| Skills | Newsletter, Print | Kubernetes, Cloud ✅ |
| Links | Dropbox, uberspace | apply4jobs.de ✅ |
| Design | Template-basiert | Custom, modern ✅ |
| Responsive | Ja | Ja, verbessert ✅ |
| Farbschema | Grau/Türkis | Blau/Rot (Webseite) ✅ |
---
## 📞 Kontakt in der E-Mail:
```
📍 Rosenstraße 4, 91564 Neuendettelsau
📱 0157 324 699 60
✉️ froehlich.sebastian@gmail.com
```
---
## ⚠️ Wichtige Hinweise:
1. **Vor dem Versenden:**
- [ ] Position eingefügt
- [ ] Anrede personalisiert
- [ ] Test-Mail versendet
- [ ] Links geprüft
- [ ] Mobile-Ansicht getestet
2. **Dateiname beim Versenden:**
- Speichere keine Anhänge, alle Dokumente sind verlinkt
- Falls doch Anhänge: `Bewerbung_Sebastian_Froehlich_[Position].pdf`
3. **Betreff-Vorschlag:**
- `Bewerbung als [Position] - Sebastian Fröhlich`
- `Initiativbewerbung - Technical Product Owner - Sebastian Fröhlich`
---
## 🎉 Fertig!
Die E-Mail ist bereit zum Versenden. Viel Erfolg bei deiner Bewerbung! 🚀
---
**Erstellt:** Oktober 2025
**Version:** 1.0
**Datei:** `bewerbung-2025.html`
+312
View File
@@ -0,0 +1,312 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Bewerbung von Sebastian Fröhlich - Technical Product Owner</title>
<style type="text/css">
/* RESET */
body {
margin: 0;
padding: 0;
width: 100% !important;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
font-family: Arial, Helvetica, sans-serif;
}
img {
outline: none;
text-decoration: none;
border: none;
-ms-interpolation-mode: bicubic;
max-width: 100%;
height: auto;
}
table {
border-collapse: collapse;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
}
/* Responsive */
@media only screen and (max-width: 600px) {
.container {
width: 100% !important;
}
.mobile-padding {
padding: 10px !important;
}
.stack {
display: block !important;
width: 100% !important;
}
}
</style>
</head>
<body style="margin: 0; padding: 0; background-color: #f8f9fa;">
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #f8f9fa;">
<tr>
<td align="center" style="padding: 20px 0;">
<!-- Main Container -->
<table class="container" width="600" border="0" cellspacing="0" cellpadding="0" style="background-color: #ffffff; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
<!-- Header -->
<tr>
<td style="background: linear-gradient(135deg, #1d3557 0%, #457b9d 100%); padding: 40px 30px; text-align: center;">
<h1 style="color: #ffffff; margin: 0 0 10px 0; font-size: 28px; font-weight: bold;">
Bewerbung als
</h1>
<p style="color: rgba(255, 255, 255, 0.9); margin: 0; font-size: 18px;">
[Position einfügen]
</p>
</td>
</tr>
<!-- Greeting -->
<tr>
<td style="padding: 30px 30px 20px 30px;">
<p style="color: #1d3557; font-size: 16px; line-height: 1.6; margin: 0 0 20px 0;">
Sehr geehrte Damen und Herren,
</p>
<p style="color: #6c757d; font-size: 16px; line-height: 1.6; margin: 0;">
mein Name ist <strong style="color: #1d3557;">Sebastian Fröhlich</strong>, ich bin <strong>46 Jahre</strong> alt und arbeite seit 2021 als <strong style="color: #e63946;">Technical Product Owner</strong> im Bereich Financial Technology. Mit über <strong>20 Jahren IT-Erfahrung</strong> bringe ich umfassende Expertise in modernen Technologien und Projektmanagement mit.
</p>
</td>
</tr>
<!-- Highlight Box -->
<tr>
<td style="padding: 0 30px 30px 30px;">
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="background: linear-gradient(135deg, rgba(230, 57, 70, 0.1) 0%, rgba(230, 57, 70, 0.05) 100%); border-left: 4px solid #e63946; border-radius: 8px;">
<tr>
<td style="padding: 25px;">
<h2 style="color: #1d3557; font-size: 20px; margin: 0 0 15px 0;">
🎯 Warum ich die richtige Wahl bin
</h2>
<ul style="color: #6c757d; font-size: 15px; line-height: 1.8; margin: 0; padding-left: 20px;">
<li style="margin-bottom: 8px;"><strong style="color: #1d3557;">20+ Jahre IT-Erfahrung</strong> – von der Diskette zur Cloud</li>
<li style="margin-bottom: 8px;"><strong style="color: #1d3557;">Technical Product Owner</strong> für 3 Fintech-Produkte im Bankenmeldewesen</li>
<li style="margin-bottom: 8px;"><strong style="color: #1d3557;">Kubernetes & Cloud Expertise</strong> – erfolgreiche Migration ohne Ausfallzeiten</li>
<li style="margin-bottom: 8px;"><strong style="color: #1d3557;">Team Leadership</strong> – Steuerung internationaler Teams (inkl. Nearshore)</li>
<li style="margin-bottom: 8px;"><strong style="color: #1d3557;">Full-Stack Know-how</strong> – PHP/Laravel, Vue.js, DevOps, Systemarchitektur</li>
</ul>
</td>
</tr>
</table>
</td>
</tr>
<!-- Divider -->
<tr>
<td style="padding: 0 30px;">
<hr style="border: none; border-top: 2px solid #e9ecef; margin: 0;">
</td>
</tr>
<!-- Key Facts Section -->
<tr>
<td style="padding: 30px;">
<h2 style="color: #1d3557; font-size: 20px; margin: 0 0 20px 0; text-align: center;">
💼 Meine aktuelle Position
</h2>
<!-- Facts Grid -->
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td class="stack" width="50%" style="padding: 10px; vertical-align: top;">
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #f8f9fa; border-radius: 8px;">
<tr>
<td style="padding: 20px; text-align: center;">
<div style="font-size: 32px; margin-bottom: 10px;">🏢</div>
<p style="color: #1d3557; font-weight: bold; margin: 0 0 5px 0; font-size: 14px;">Unternehmen</p>
<p style="color: #6c757d; margin: 0; font-size: 13px;">Sopra Financial Technology GmbH</p>
</td>
</tr>
</table>
</td>
<td class="stack" width="50%" style="padding: 10px; vertical-align: top;">
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #f8f9fa; border-radius: 8px;">
<tr>
<td style="padding: 20px; text-align: center;">
<div style="font-size: 32px; margin-bottom: 10px;">⚙️</div>
<p style="color: #1d3557; font-weight: bold; margin: 0 0 5px 0; font-size: 14px;">Position</p>
<p style="color: #6c757d; margin: 0; font-size: 13px;">Technical Product Owner</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="stack" width="50%" style="padding: 10px; vertical-align: top;">
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #f8f9fa; border-radius: 8px;">
<tr>
<td style="padding: 20px; text-align: center;">
<div style="font-size: 32px; margin-bottom: 10px;">📊</div>
<p style="color: #1d3557; font-weight: bold; margin: 0 0 5px 0; font-size: 14px;">Verantwortung</p>
<p style="color: #6c757d; margin: 0; font-size: 13px;">3 Produkte im Bankenmeldewesen</p>
</td>
</tr>
</table>
</td>
<td class="stack" width="50%" style="padding: 10px; vertical-align: top;">
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #f8f9fa; border-radius: 8px;">
<tr>
<td style="padding: 20px; text-align: center;">
<div style="font-size: 32px; margin-bottom: 10px;">🚀</div>
<p style="color: #1d3557; font-weight: bold; margin: 0 0 5px 0; font-size: 14px;">Highlights</p>
<p style="color: #6c757d; margin: 0; font-size: 13px;">K8s-Migration, Code Reviews</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!-- Divider -->
<tr>
<td style="padding: 0 30px;">
<hr style="border: none; border-top: 2px solid #e9ecef; margin: 0;">
</td>
</tr>
<!-- Downloads Section -->
<tr>
<td style="padding: 30px;">
<h2 style="color: #1d3557; font-size: 20px; margin: 0 0 20px 0; text-align: center;">
📥 Meine Bewerbungsunterlagen
</h2>
<!-- Download Buttons -->
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td style="padding: 10px; text-align: center;">
<a href="https://apply4jobs.de/bewerbung/downloads/Lebenslauf_2025.pdf" style="background-color: #e63946; color: #ffffff; padding: 14px 28px; text-decoration: none; border-radius: 8px; display: inline-block; font-weight: bold; font-size: 15px;">
📄 Lebenslauf 2025
</a>
</td>
</tr>
<tr>
<td style="padding: 10px; text-align: center;">
<a href="https://apply4jobs.de/bewerbung/downloads/Zeugnisse_2025.pdf" style="background-color: #457b9d; color: #ffffff; padding: 14px 28px; text-decoration: none; border-radius: 8px; display: inline-block; font-weight: bold; font-size: 15px;">
📜 Arbeitszeugnisse 2025
</a>
</td>
</tr>
<tr>
<td style="padding: 10px; text-align: center;">
<a href="https://apply4jobs.de/bewerbung/downloads/Referenzen.pdf" style="background-color: #457b9d; color: #ffffff; padding: 14px 28px; text-decoration: none; border-radius: 8px; display: inline-block; font-weight: bold; font-size: 15px;">
⭐ Referenzen
</a>
</td>
</tr>
<tr>
<td style="padding: 20px 10px 10px 10px; text-align: center;">
<a href="https://apply4jobs.de/bewerbung/" style="background: linear-gradient(135deg, #e63946 0%, #d62839 100%); color: #ffffff; padding: 16px 32px; text-decoration: none; border-radius: 8px; display: inline-block; font-weight: bold; font-size: 16px; box-shadow: 0 4px 6px rgba(230, 57, 70, 0.3);">
🌐 Vollständige Online-Bewerbung ansehen
</a>
</td>
</tr>
</table>
</td>
</tr>
<!-- Divider -->
<tr>
<td style="padding: 0 30px;">
<hr style="border: none; border-top: 2px solid #e9ecef; margin: 0;">
</td>
</tr>
<!-- Contact Section -->
<tr>
<td style="padding: 30px;">
<h2 style="color: #1d3557; font-size: 20px; margin: 0 0 20px 0; text-align: center;">
📞 Kontakt
</h2>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td class="stack" width="33.33%" style="padding: 10px; vertical-align: top;">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td style="text-align: center;">
<div style="font-size: 32px; margin-bottom: 10px;">📍</div>
<p style="color: #1d3557; font-weight: bold; margin: 0 0 5px 0; font-size: 13px;">Adresse</p>
<p style="color: #6c757d; margin: 0; font-size: 12px; line-height: 1.5;">
Rosenstraße 4<br>
91564 Neuendettelsau
</p>
</td>
</tr>
</table>
</td>
<td class="stack" width="33.33%" style="padding: 10px; vertical-align: top;">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td style="text-align: center;">
<div style="font-size: 32px; margin-bottom: 10px;">📱</div>
<p style="color: #1d3557; font-weight: bold; margin: 0 0 5px 0; font-size: 13px;">Telefon</p>
<p style="color: #6c757d; margin: 0; font-size: 12px;">
<a href="tel:+4915732469960" style="color: #e63946; text-decoration: none;">0157 324 699 60</a>
</p>
</td>
</tr>
</table>
</td>
<td class="stack" width="33.33%" style="padding: 10px; vertical-align: top;">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td style="text-align: center;">
<div style="font-size: 32px; margin-bottom: 10px;">✉️</div>
<p style="color: #1d3557; font-weight: bold; margin: 0 0 5px 0; font-size: 13px;">E-Mail</p>
<p style="color: #6c757d; margin: 0; font-size: 12px;">
<a href="mailto:froehlich.sebastian@gmail.com" style="color: #e63946; text-decoration: none;">froehlich.sebastian@gmail.com</a>
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!-- Closing -->
<tr>
<td style="padding: 0 30px 30px 30px;">
<p style="color: #6c757d; font-size: 15px; line-height: 1.6; margin: 0;">
Ich freue mich darauf, Sie in einem persönlichen Gespräch von meinen Fähigkeiten zu überzeugen und mehr über die Position zu erfahren.
</p>
<p style="color: #6c757d; font-size: 15px; line-height: 1.6; margin: 15px 0 0 0;">
Mit freundlichen Grüßen<br>
<strong style="color: #1d3557;">Sebastian Fröhlich</strong>
</p>
</td>
</tr>
<!-- Footer -->
<tr>
<td style="background-color: #f8f9fa; padding: 20px 30px; text-align: center; border-top: 1px solid #e9ecef;">
<p style="color: #6c757d; font-size: 11px; line-height: 1.5; margin: 0;">
Diese E-Mail wurde als responsive HTML-Mail erstellt.<br>
Falls die Darstellung nicht korrekt ist, können Sie die
<a href="https://apply4jobs.de/bewerbung/" style="color: #e63946; text-decoration: underline;">Online-Version</a> aufrufen.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
+25
View File
@@ -0,0 +1,25 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
});
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
{
ignores: [
"node_modules/**",
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
],
},
];
export default eslintConfig;
+10
View File
@@ -0,0 +1,10 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: 'export', // Statischer Export aktivieren
images: {
unoptimized: true, // Next.js Image Optimierung deaktivieren (für statischen Export nötig)
},
};
export default nextConfig;
+6101
View File
File diff suppressed because it is too large. Load diff
+28
View File
@@ -0,0 +1,28 @@
{
"name": "bewerbung",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build --turbopack",
"start": "next start",
"lint": "eslint",
"copy-assets": "bash copy-assets.sh"
},
"dependencies": {
"react": "19.1.0",
"react-dom": "19.1.0",
"next": "15.5.4"
},
"devDependencies": {
"typescript": "^5",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4",
"eslint": "^9",
"eslint-config-next": "15.5.4",
"@eslint/eslintrc": "^3"
}
}
+5
View File
@@ -0,0 +1,5 @@
const config = {
plugins: ["@tailwindcss/postcss"],
};
export default config;
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 391 B

+1
View File
@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 128 B

+1
View File
@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

After

Width:  |  Height:  |  Size: 385 B

+27
View File
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}