764 lines
26 KiB
Markdown
764 lines
26 KiB
Markdown
# Website-Erweiterung Implementation Plan
|
|
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
|
**Goal:** Zwei neue Service-Karten, eine DSGVO/GoBD/Paperless-Sektion und drei Spezialeffekte (Scroll-Reveal, Typewriter, Counter) zur bestehenden MBO-Tech-IT Next.js Website hinzufügen.
|
|
|
|
**Architecture:** Alle Effekte werden mit nativen Web-APIs (Intersection Observer, requestAnimationFrame) ohne externe Packages implementiert. Neue Komponenten folgen dem bestehenden Muster (Tailwind, dark theme, orange/blau Akzente). `"use client"` nur wo Browser-APIs benötigt werden.
|
|
|
|
**Tech Stack:** Next.js 14 (App Router), TypeScript, Tailwind CSS, Intersection Observer API
|
|
|
|
---
|
|
|
|
## Dateiübersicht
|
|
|
|
| Aktion | Datei | Zweck |
|
|
|--------|-------|-------|
|
|
| Create | `components/ScrollReveal.tsx` | Wiederverwendbarer Wrapper mit Intersection Observer |
|
|
| Create | `hooks/useTypewriter.ts` | Hook für Typewriter-Effekt |
|
|
| Create | `components/StatsBar.tsx` | Animierte Statistik-Leiste mit Counter |
|
|
| Create | `components/PaperlessSection.tsx` | DSGVO & GoBD mit Paperless Sektion |
|
|
| Modify | `components/Services.tsx` | 2 neue Karten (WLAN, WebApps) + ScrollReveal |
|
|
| Modify | `components/Hero.tsx` | Typewriter-Effekt integrieren |
|
|
| Modify | `app/page.tsx` | StatsBar + PaperlessSection einbinden |
|
|
|
|
---
|
|
|
|
## Task 1: ScrollReveal-Komponente
|
|
|
|
**Files:**
|
|
- Create: `components/ScrollReveal.tsx`
|
|
|
|
- [ ] **Step 1: Komponente erstellen**
|
|
|
|
```tsx
|
|
// components/ScrollReveal.tsx
|
|
"use client";
|
|
|
|
import { useEffect, useRef, useState } from "react";
|
|
|
|
interface ScrollRevealProps {
|
|
children: React.ReactNode;
|
|
delay?: number;
|
|
className?: string;
|
|
}
|
|
|
|
export default function ScrollReveal({ children, delay = 0, className = "" }: ScrollRevealProps) {
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
const [visible, setVisible] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const el = ref.current;
|
|
if (!el) return;
|
|
|
|
const observer = new IntersectionObserver(
|
|
([entry]) => {
|
|
if (entry.isIntersecting) {
|
|
setVisible(true);
|
|
observer.disconnect();
|
|
}
|
|
},
|
|
{ threshold: 0.1 }
|
|
);
|
|
|
|
observer.observe(el);
|
|
return () => observer.disconnect();
|
|
}, []);
|
|
|
|
return (
|
|
<div
|
|
ref={ref}
|
|
className={className}
|
|
style={{
|
|
opacity: visible ? 1 : 0,
|
|
transform: visible ? "translateY(0)" : "translateY(24px)",
|
|
transition: `opacity 500ms ease-out ${delay}ms, transform 500ms ease-out ${delay}ms`,
|
|
}}
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 2: Dev-Server starten und Datei prüfen**
|
|
|
|
```bash
|
|
npm run dev
|
|
```
|
|
|
|
Öffne http://localhost:3000 — Seite soll unverändert aussehen (Komponente noch nicht eingebunden).
|
|
|
|
- [ ] **Step 3: Commit**
|
|
|
|
```bash
|
|
git add components/ScrollReveal.tsx
|
|
git commit -m "feat: add ScrollReveal component with Intersection Observer"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 2: useTypewriter Hook
|
|
|
|
**Files:**
|
|
- Create: `hooks/useTypewriter.ts`
|
|
|
|
- [ ] **Step 1: `hooks/` Ordner anlegen und Hook erstellen**
|
|
|
|
```bash
|
|
mkdir -p hooks
|
|
```
|
|
|
|
```ts
|
|
// hooks/useTypewriter.ts
|
|
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
export function useTypewriter(words: string[], typingSpeed = 80, deletingSpeed = 40, pauseMs = 2000) {
|
|
const [displayed, setDisplayed] = useState("");
|
|
const [wordIndex, setWordIndex] = useState(0);
|
|
const [isDeleting, setIsDeleting] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const current = words[wordIndex % words.length];
|
|
|
|
const timeout = setTimeout(() => {
|
|
if (!isDeleting) {
|
|
setDisplayed(current.slice(0, displayed.length + 1));
|
|
if (displayed.length + 1 === current.length) {
|
|
setTimeout(() => setIsDeleting(true), pauseMs);
|
|
}
|
|
} else {
|
|
setDisplayed(current.slice(0, displayed.length - 1));
|
|
if (displayed.length - 1 === 0) {
|
|
setIsDeleting(false);
|
|
setWordIndex((i) => i + 1);
|
|
}
|
|
}
|
|
}, isDeleting ? deletingSpeed : typingSpeed);
|
|
|
|
return () => clearTimeout(timeout);
|
|
}, [displayed, isDeleting, wordIndex, words, typingSpeed, deletingSpeed, pauseMs]);
|
|
|
|
return displayed;
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 2: Commit**
|
|
|
|
```bash
|
|
git add hooks/useTypewriter.ts
|
|
git commit -m "feat: add useTypewriter hook"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 3: Hero — Typewriter-Effekt integrieren
|
|
|
|
**Files:**
|
|
- Modify: `components/Hero.tsx`
|
|
|
|
- [ ] **Step 1: Hero.tsx anpassen**
|
|
|
|
Ersetze die gesamte Datei `components/Hero.tsx` mit folgendem Inhalt:
|
|
|
|
```tsx
|
|
// components/Hero.tsx
|
|
"use client";
|
|
|
|
import { useTypewriter } from "@/hooks/useTypewriter";
|
|
|
|
export default function Hero() {
|
|
const typed = useTypewriter(["Professionell.", "DSGVO-konform.", "Zuverlässig.", "Skalierbar."]);
|
|
|
|
return (
|
|
<section className="relative min-h-screen flex items-center justify-center overflow-hidden">
|
|
{/* Background grid */}
|
|
<div className="absolute inset-0 bg-grid opacity-50" />
|
|
|
|
{/* Radial gradient glow */}
|
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,rgba(249,115,22,0.10)_0%,transparent_65%)]" />
|
|
|
|
{/* Floating orbs */}
|
|
<div className="absolute top-1/4 left-1/4 w-72 h-72 bg-orange-500/5 rounded-full blur-3xl animate-pulse-slow" />
|
|
<div className="absolute bottom-1/4 right-1/4 w-80 h-80 bg-blue-600/5 rounded-full blur-3xl animate-pulse-slow" />
|
|
|
|
<div className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
|
{/* Tagline badge */}
|
|
<div className="inline-flex items-center gap-2 px-5 py-2 rounded-full border border-orange-500/40 bg-orange-500/10 text-orange-400 text-xs font-black tracking-[0.3em] uppercase mb-8">
|
|
<span className="w-1.5 h-1.5 bg-orange-400 rounded-full animate-pulse" />
|
|
Digital Denken. Lokal Handeln.
|
|
</div>
|
|
|
|
{/* Headline */}
|
|
<h1 className="text-5xl sm:text-6xl lg:text-7xl font-black text-white leading-tight mb-6 tracking-tight">
|
|
Ihre IT-Infrastruktur.{" "}
|
|
<span className="text-gradient">
|
|
{typed}
|
|
<span className="animate-pulse">|</span>
|
|
</span>
|
|
<br />
|
|
Skalierbar. Zuverlässig.
|
|
</h1>
|
|
|
|
{/* Subheadline */}
|
|
<p className="text-xl sm:text-2xl text-slate-400 max-w-3xl mx-auto mb-4 leading-relaxed">
|
|
Von Docker-Containern über Kubernetes-Cluster bis hin zu
|
|
Proxmox-Virtualisierung auf Hetzner Cloud — wir bringen Ihre
|
|
IT auf das nächste Level.
|
|
</p>
|
|
|
|
{/* Tech pills */}
|
|
<div className="flex flex-wrap items-center justify-center gap-3 mb-10">
|
|
{["Docker", "Kubernetes", "Proxmox", "Hetzner Cloud", "Linux"].map((tech) => (
|
|
<span
|
|
key={tech}
|
|
className="px-3 py-1 rounded-md bg-gray-900 border border-gray-700 text-slate-300 text-sm font-mono"
|
|
>
|
|
{tech}
|
|
</span>
|
|
))}
|
|
</div>
|
|
|
|
{/* CTAs */}
|
|
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
|
<a href="#contact" className="btn-primary w-full sm:w-auto px-8 py-4 text-lg">
|
|
Projekt anfragen
|
|
</a>
|
|
<a href="#services" className="btn-secondary w-full sm:w-auto px-8 py-4 text-lg">
|
|
Services ansehen
|
|
</a>
|
|
</div>
|
|
|
|
{/* Scroll indicator */}
|
|
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2 text-slate-600">
|
|
<span className="text-xs font-bold tracking-[0.3em] uppercase">Scroll</span>
|
|
<div className="w-px h-12 bg-gradient-to-b from-slate-600 to-transparent" />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 2: Im Browser prüfen**
|
|
|
|
Öffne http://localhost:3000 — der Hero-Text soll zwischen "Professionell.", "DSGVO-konform.", "Zuverlässig.", "Skalierbar." wechseln mit Cursor `|`.
|
|
|
|
- [ ] **Step 3: Commit**
|
|
|
|
```bash
|
|
git add components/Hero.tsx hooks/useTypewriter.ts
|
|
git commit -m "feat: add typewriter effect to Hero headline"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 4: StatsBar — Animierte Statistiken
|
|
|
|
**Files:**
|
|
- Create: `components/StatsBar.tsx`
|
|
|
|
- [ ] **Step 1: Komponente erstellen**
|
|
|
|
```tsx
|
|
// components/StatsBar.tsx
|
|
"use client";
|
|
|
|
import { useEffect, useRef, useState } from "react";
|
|
|
|
const stats = [
|
|
{ value: 30, suffix: "+", label: "Jahre IT-Erfahrung", accent: "orange" },
|
|
{ value: 99, suffix: "%", label: "Server-Uptime", accent: "blue" },
|
|
{ value: 50, suffix: "+", label: "Abgeschlossene Projekte", accent: "orange" },
|
|
{ value: 24, suffix: "h", label: "Support-Reaktionszeit", accent: "blue" },
|
|
];
|
|
|
|
function Counter({ value, suffix, accent }: { value: number; suffix: string; accent: string }) {
|
|
const [count, setCount] = useState(0);
|
|
const ref = useRef<HTMLSpanElement>(null);
|
|
const started = useRef(false);
|
|
|
|
useEffect(() => {
|
|
const el = ref.current;
|
|
if (!el) return;
|
|
|
|
const observer = new IntersectionObserver(
|
|
([entry]) => {
|
|
if (entry.isIntersecting && !started.current) {
|
|
started.current = true;
|
|
const duration = 1500;
|
|
const start = performance.now();
|
|
|
|
const tick = (now: number) => {
|
|
const progress = Math.min((now - start) / duration, 1);
|
|
const eased = 1 - Math.pow(1 - progress, 3);
|
|
setCount(Math.floor(eased * value));
|
|
if (progress < 1) requestAnimationFrame(tick);
|
|
};
|
|
|
|
requestAnimationFrame(tick);
|
|
observer.disconnect();
|
|
}
|
|
},
|
|
{ threshold: 0.3 }
|
|
);
|
|
|
|
observer.observe(el);
|
|
return () => observer.disconnect();
|
|
}, [value]);
|
|
|
|
const color = accent === "orange" ? "text-orange-400" : "text-blue-400";
|
|
|
|
return (
|
|
<span ref={ref} className={`text-4xl sm:text-5xl font-black ${color}`}>
|
|
{count}{suffix}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
export default function StatsBar() {
|
|
return (
|
|
<section className="py-12 px-4 sm:px-6 lg:px-8 relative">
|
|
<div className="absolute inset-0 bg-[#111925]" />
|
|
<div className="max-w-7xl mx-auto relative">
|
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-8">
|
|
{stats.map((stat) => (
|
|
<div key={stat.label} className="text-center">
|
|
<Counter value={stat.value} suffix={stat.suffix} accent={stat.accent} />
|
|
<p className="text-slate-500 text-sm mt-1">{stat.label}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 2: StatsBar in `app/page.tsx` einbinden**
|
|
|
|
Füge den Import und die Komponente in `app/page.tsx` ein. Die Datei soll danach so aussehen:
|
|
|
|
```tsx
|
|
// app/page.tsx
|
|
import Header from "@/components/Header";
|
|
import Hero from "@/components/Hero";
|
|
import TaglineBanner from "@/components/TaglineBanner";
|
|
import StatsBar from "@/components/StatsBar";
|
|
import Services from "@/components/Services";
|
|
import Technologies from "@/components/Technologies";
|
|
import DataSovereignty from "@/components/DataSovereignty";
|
|
import PaperlessSection from "@/components/PaperlessSection";
|
|
import About from "@/components/About";
|
|
import Contact from "@/components/Contact";
|
|
import Footer from "@/components/Footer";
|
|
|
|
export default function Home() {
|
|
return (
|
|
<main className="min-h-screen bg-[#18212f]">
|
|
<Header />
|
|
<Hero />
|
|
<TaglineBanner />
|
|
<StatsBar />
|
|
<Services />
|
|
<TaglineBanner />
|
|
<PaperlessSection />
|
|
<DataSovereignty />
|
|
<TaglineBanner />
|
|
<Technologies />
|
|
<About />
|
|
<TaglineBanner />
|
|
<Contact />
|
|
<Footer />
|
|
</main>
|
|
);
|
|
}
|
|
```
|
|
|
|
**Hinweis:** `PaperlessSection` existiert noch nicht — TypeScript wird einen Fehler werfen. Das ist normal, wir erstellen sie in Task 6.
|
|
|
|
- [ ] **Step 3: Im Browser prüfen**
|
|
|
|
Öffne http://localhost:3000 — nach dem Hero soll die dunkle StatsBar erscheinen. Beim Scrollen dazu zählen die Zahlen hoch.
|
|
|
|
- [ ] **Step 4: Commit**
|
|
|
|
```bash
|
|
git add components/StatsBar.tsx app/page.tsx
|
|
git commit -m "feat: add animated StatsBar with counter effect"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 5: Services — 2 neue Karten + ScrollReveal
|
|
|
|
**Files:**
|
|
- Modify: `components/Services.tsx`
|
|
|
|
- [ ] **Step 1: `services`-Array in `Services.tsx` um 2 Einträge erweitern**
|
|
|
|
Füge am Ende des `services`-Arrays (nach dem Kubernetes-Eintrag, vor der schließenden `]`) folgende zwei Objekte ein:
|
|
|
|
```ts
|
|
{
|
|
icon: (
|
|
<svg viewBox="0 0 24 24" className="w-8 h-8" fill="currentColor">
|
|
<path d="M1 6l5 4-5 4V6zm7 0h14v2H8V6zm0 5h14v2H8v-2zm0 5h14v2H8v-2z" />
|
|
<path d="M5.828 7l-1.414 1.414L6.586 10.6 4.414 12.8l1.414 1.4L10 10.6 5.828 7z" opacity=".6"/>
|
|
<path d="M19 3H5a2 2 0 00-2 2v1h2V5h14v14H5v-1H3v1a2 2 0 002 2h14a2 2 0 002-2V5a2 2 0 00-2-2z"/>
|
|
<circle cx="4" cy="12" r="3"/>
|
|
<path d="M4 10.5A1.5 1.5 0 115.5 12 1.5 1.5 0 014 10.5z" fill="#18212f"/>
|
|
</svg>
|
|
),
|
|
title: "WLAN & Netzwerk",
|
|
description:
|
|
"Professionelle WLAN-Planung, Installation und Troubleshooting für Privat- und Gewerbekunden. Mesh-Netzwerke, Access Points und hartnäckige Netzwerkprobleme — auch in schwierigen Gebäuden.",
|
|
features: ["WLAN-Planung & Installation", "Mesh-Netzwerke", "Netzwerk-Troubleshooting", "Privat- & Gewerbekunden"],
|
|
accent: "orange",
|
|
},
|
|
{
|
|
icon: (
|
|
<svg viewBox="0 0 24 24" className="w-8 h-8" fill="currentColor">
|
|
<path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/>
|
|
</svg>
|
|
),
|
|
title: "Individuelle Webanwendungen",
|
|
description:
|
|
"Maßgeschneiderte Web-Apps für spezielle Geschäftsanforderungen. Von Kundenportalen über interne Tools bis zu Buchungssystemen — entwickelt auf Ihre Prozesse zugeschnitten.",
|
|
features: ["Maßgeschneiderte Entwicklung", "Kundenportale & Tools", "API-Integrationen", "Moderner Tech-Stack"],
|
|
accent: "blue",
|
|
},
|
|
```
|
|
|
|
- [ ] **Step 2: ScrollReveal in die Karten-Schleife integrieren**
|
|
|
|
Füge am Anfang der Datei den Import hinzu:
|
|
|
|
```tsx
|
|
import ScrollReveal from "@/components/ScrollReveal";
|
|
```
|
|
|
|
Ändere dann die Karten-Schleife in der `return`-Anweisung von:
|
|
|
|
```tsx
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{services.map((service) => {
|
|
const c = colorMap[service.accent as keyof typeof colorMap];
|
|
return (
|
|
<div
|
|
key={service.title}
|
|
className={`group p-6 rounded-2xl bg-gray-900 border ${c.border} transition-all duration-300 hover:-translate-y-1 hover:shadow-xl ${c.shadow}`}
|
|
>
|
|
```
|
|
|
|
zu:
|
|
|
|
```tsx
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{services.map((service, index) => {
|
|
const c = colorMap[service.accent as keyof typeof colorMap];
|
|
return (
|
|
<ScrollReveal key={service.title} delay={index * 75}>
|
|
<div
|
|
className={`group p-6 rounded-2xl bg-gray-900 border ${c.border} transition-all duration-300 hover:-translate-y-1 hover:shadow-xl ${c.shadow}`}
|
|
>
|
|
```
|
|
|
|
Und die schließende `</div>` der Karte wird zu `</div></ScrollReveal>`. Die vollständige `return`-Schleife sieht dann so aus:
|
|
|
|
```tsx
|
|
{services.map((service, index) => {
|
|
const c = colorMap[service.accent as keyof typeof colorMap];
|
|
return (
|
|
<ScrollReveal key={service.title} delay={index * 75}>
|
|
<div
|
|
className={`group p-6 rounded-2xl bg-gray-900 border ${c.border} transition-all duration-300 hover:-translate-y-1 hover:shadow-xl ${c.shadow}`}
|
|
>
|
|
<div className={`w-14 h-14 rounded-xl ${c.iconBg} ${c.icon} flex items-center justify-center mb-5`}>
|
|
{service.icon}
|
|
</div>
|
|
<h3 className="text-xl font-bold text-white mb-3">{service.title}</h3>
|
|
<p className="text-slate-400 text-sm leading-relaxed mb-5">{service.description}</p>
|
|
<ul className="space-y-2">
|
|
{service.features.map((feature) => (
|
|
<li key={feature} className="flex items-center gap-2 text-sm text-slate-300">
|
|
<span className={`w-1.5 h-1.5 rounded-full ${c.dot} flex-shrink-0`} />
|
|
{feature}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</ScrollReveal>
|
|
);
|
|
})}
|
|
```
|
|
|
|
- [ ] **Step 3: Im Browser prüfen**
|
|
|
|
http://localhost:3000 → Services-Bereich soll 8 Karten zeigen, die beim Scrollen gestaffelt einfahren.
|
|
|
|
- [ ] **Step 4: Commit**
|
|
|
|
```bash
|
|
git add components/Services.tsx
|
|
git commit -m "feat: add WLAN and WebApps service cards with scroll-reveal"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 6: PaperlessSection — DSGVO & GoBD
|
|
|
|
**Files:**
|
|
- Create: `components/PaperlessSection.tsx`
|
|
|
|
- [ ] **Step 1: Komponente erstellen**
|
|
|
|
```tsx
|
|
// components/PaperlessSection.tsx
|
|
import ScrollReveal from "@/components/ScrollReveal";
|
|
|
|
const requirements = [
|
|
{
|
|
accent: "orange",
|
|
title: "DSGVO-konforme Archivierung",
|
|
text: "Alle Dokumente bleiben auf Ihrer eigenen Infrastruktur — in Deutschland oder bei Hetzner. Kein Zugriff durch Drittanbieter, keine US-Cloud. Vollständig DSGVO-konform.",
|
|
},
|
|
{
|
|
accent: "blue",
|
|
title: "GoBD-Anforderungen erfüllt",
|
|
text: "Revisionssichere, unveränderliche Ablage steuerrelevanter Belege. Paperless-ngx protokolliert alle Änderungen und erfüllt die Anforderungen der GoBD an die digitale Buchführung.",
|
|
},
|
|
{
|
|
accent: "orange",
|
|
title: "Selbstgehostet & unter Kontrolle",
|
|
text: "Betrieb auf Ihrem eigenen Server oder auf einem deutschen Hetzner-VPS. Sie haben die volle Kontrolle über Ihre Daten — heute, morgen und in 10 Jahren.",
|
|
},
|
|
];
|
|
|
|
const services = [
|
|
"Installation & Konfiguration von Paperless-ngx",
|
|
"Einrichtung automatischer Dokumentenerkennung (OCR)",
|
|
"Migration bestehender Dokumentenarchive",
|
|
"Schulung & Dokumentation für Ihr Team",
|
|
];
|
|
|
|
const colorMap = {
|
|
orange: {
|
|
border: "border-orange-500/20 hover:border-orange-500/50",
|
|
dot: "bg-orange-400",
|
|
dotBg: "bg-orange-500/10",
|
|
},
|
|
blue: {
|
|
border: "border-blue-500/20 hover:border-blue-500/50",
|
|
dot: "bg-blue-400",
|
|
dotBg: "bg-blue-500/10",
|
|
},
|
|
};
|
|
|
|
export default function PaperlessSection() {
|
|
return (
|
|
<section id="paperless" className="py-24 px-4 sm:px-6 lg:px-8 relative">
|
|
<div className="absolute inset-0 bg-[#111925]" />
|
|
<div className="absolute inset-0 bg-grid opacity-20" />
|
|
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_top_right,rgba(34,197,94,0.04)_0%,transparent_60%)]" />
|
|
|
|
<div className="max-w-7xl mx-auto relative">
|
|
|
|
{/* Header */}
|
|
<ScrollReveal>
|
|
<div className="text-center mb-16">
|
|
<span className="text-orange-400 font-mono text-xs font-bold tracking-[0.25em] uppercase">
|
|
Compliance & Dokumentenmanagement
|
|
</span>
|
|
<h2 className="text-4xl sm:text-5xl font-black text-white mt-3 mb-4">
|
|
DSGVO & GoBD mit{" "}
|
|
<span className="text-gradient-orange">Paperless-ngx</span>
|
|
</h2>
|
|
<p className="text-slate-400 text-lg max-w-2xl mx-auto">
|
|
Digitale Dokumentenverwaltung, die rechtssicher ist — DSGVO-konform,
|
|
GoBD-tauglich, selbstgehostet. Kein Abo, keine US-Cloud, volle Kontrolle.
|
|
</p>
|
|
</div>
|
|
</ScrollReveal>
|
|
|
|
{/* What is Paperless */}
|
|
<ScrollReveal delay={100}>
|
|
<div className="mb-12 p-6 rounded-2xl bg-gray-900 border border-orange-500/20 flex flex-col md:flex-row gap-6 items-start">
|
|
<div className="flex-shrink-0 w-14 h-14 rounded-xl bg-orange-500/10 text-orange-400 flex items-center justify-center">
|
|
<svg className="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<h3 className="text-white font-bold text-lg mb-2">Was ist Paperless-ngx?</h3>
|
|
<p className="text-slate-400 text-sm leading-relaxed">
|
|
Paperless-ngx ist ein Open-Source Dokumentenmanagementsystem (DMS), das Ihre Papierflut digitalisiert
|
|
und durchsuchbar macht. Rechnungen, Verträge, Briefe — automatisch per OCR erkannt, kategorisiert und
|
|
archiviert. Betrieben auf Ihrer eigenen Infrastruktur, ohne Abhängigkeit von Cloud-Anbietern.
|
|
</p>
|
|
<div className="flex flex-wrap gap-2 mt-3">
|
|
{["Open Source", "Self-Hosted", "OCR", "Volltextsuche", "Automatische Tags"].map((tag) => (
|
|
<span key={tag} className="px-3 py-1 rounded-full bg-orange-500/10 border border-orange-500/20 text-orange-400 text-xs font-mono">
|
|
{tag}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</ScrollReveal>
|
|
|
|
{/* Requirements grid */}
|
|
<div className="grid md:grid-cols-3 gap-6 mb-16">
|
|
{requirements.map((req, index) => {
|
|
const c = colorMap[req.accent as keyof typeof colorMap];
|
|
return (
|
|
<ScrollReveal key={req.title} delay={index * 100}>
|
|
<div className={`p-6 rounded-2xl bg-gray-900 border ${c.border} transition-all duration-300 h-full`}>
|
|
<div className={`w-2 h-2 rounded-full ${c.dot} mb-4`} />
|
|
<h3 className="text-white font-bold text-base mb-3">{req.title}</h3>
|
|
<p className="text-slate-400 text-sm leading-relaxed">{req.text}</p>
|
|
</div>
|
|
</ScrollReveal>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{/* Divider */}
|
|
<div className="flex items-center gap-6 mb-12">
|
|
<div className="flex-1 h-px bg-gradient-to-r from-transparent via-gray-700 to-transparent" />
|
|
<div className="px-6 py-2 rounded-full border border-orange-500/40 bg-orange-500/5 text-orange-400 text-sm font-black tracking-wider whitespace-nowrap">
|
|
Unser Leistungsumfang
|
|
</div>
|
|
<div className="flex-1 h-px bg-gradient-to-r from-transparent via-gray-700 to-transparent" />
|
|
</div>
|
|
|
|
{/* Services list */}
|
|
<ScrollReveal delay={100}>
|
|
<div className="grid sm:grid-cols-2 gap-4 mb-16">
|
|
{services.map((item) => (
|
|
<div key={item} className="flex items-center gap-3 p-4 rounded-xl bg-gray-900 border border-gray-800">
|
|
<div className="w-5 h-5 rounded-full bg-orange-500 flex items-center justify-center flex-shrink-0">
|
|
<svg className="w-3 h-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
</div>
|
|
<span className="text-slate-300 text-sm">{item}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</ScrollReveal>
|
|
|
|
{/* CTA */}
|
|
<ScrollReveal delay={150}>
|
|
<div className="p-8 rounded-2xl border border-orange-500/30 bg-orange-500/5 text-center">
|
|
<p className="text-orange-400 font-black text-sm tracking-[0.2em] uppercase mb-2">
|
|
Compliance leicht gemacht
|
|
</p>
|
|
<p className="text-white text-xl font-bold mb-2">
|
|
DSGVO & GoBD — ohne Kompromisse.
|
|
</p>
|
|
<p className="text-slate-400 text-sm max-w-xl mx-auto mb-6">
|
|
Wir richten Paperless-ngx auf Ihrer Infrastruktur ein, migrieren
|
|
Ihre bestehenden Dokumente und schulen Ihr Team. Rechtssicher,
|
|
selbstgehostet, dauerhaft unter Ihrer Kontrolle.
|
|
</p>
|
|
<a href="#contact" className="btn-primary inline-flex px-8 py-3 text-sm">
|
|
Jetzt Beratung anfragen
|
|
</a>
|
|
</div>
|
|
</ScrollReveal>
|
|
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 2: Im Browser prüfen**
|
|
|
|
http://localhost:3000 → Zwischen Services und DataSovereignty soll die neue Paperless-Sektion erscheinen mit dunkelgrauem Hintergrund, 3 Anforderungskarten und CTA.
|
|
|
|
- [ ] **Step 3: Commit**
|
|
|
|
```bash
|
|
git add components/PaperlessSection.tsx
|
|
git commit -m "feat: add PaperlessSection with DSGVO/GoBD content"
|
|
```
|
|
|
|
---
|
|
|
|
## Task 7: page.tsx — PaperlessSection finalisieren
|
|
|
|
**Files:**
|
|
- Modify: `app/page.tsx`
|
|
|
|
Nach Task 4 wurde `PaperlessSection` bereits in `page.tsx` importiert. In Task 6 wurde die Komponente erstellt.
|
|
|
|
- [ ] **Step 1: Prüfen dass page.tsx korrekt ist**
|
|
|
|
`app/page.tsx` soll exakt so aussehen (wurde bereits in Task 4 geschrieben):
|
|
|
|
```tsx
|
|
import Header from "@/components/Header";
|
|
import Hero from "@/components/Hero";
|
|
import TaglineBanner from "@/components/TaglineBanner";
|
|
import StatsBar from "@/components/StatsBar";
|
|
import Services from "@/components/Services";
|
|
import Technologies from "@/components/Technologies";
|
|
import DataSovereignty from "@/components/DataSovereignty";
|
|
import PaperlessSection from "@/components/PaperlessSection";
|
|
import About from "@/components/About";
|
|
import Contact from "@/components/Contact";
|
|
import Footer from "@/components/Footer";
|
|
|
|
export default function Home() {
|
|
return (
|
|
<main className="min-h-screen bg-[#18212f]">
|
|
<Header />
|
|
<Hero />
|
|
<TaglineBanner />
|
|
<StatsBar />
|
|
<Services />
|
|
<TaglineBanner />
|
|
<PaperlessSection />
|
|
<DataSovereignty />
|
|
<TaglineBanner />
|
|
<Technologies />
|
|
<About />
|
|
<TaglineBanner />
|
|
<Contact />
|
|
<Footer />
|
|
</main>
|
|
);
|
|
}
|
|
```
|
|
|
|
Falls noch nicht so, jetzt anpassen.
|
|
|
|
- [ ] **Step 2: Finaler Check im Browser**
|
|
|
|
http://localhost:3000 — vollständige Seite prüfen:
|
|
- Hero: Typewriter läuft ✓
|
|
- StatsBar: Zahlen zählen hoch beim Scrollen ✓
|
|
- Services: 8 Karten, gleiten beim Scrollen ein ✓
|
|
- PaperlessSection: erscheint nach Services ✓
|
|
- Keine TypeScript/Console-Fehler ✓
|
|
|
|
- [ ] **Step 3: Finaler Commit**
|
|
|
|
```bash
|
|
git add app/page.tsx
|
|
git commit -m "feat: wire up all new sections in page layout"
|
|
```
|
|
|
|
---
|
|
|
|
## Self-Review Ergebnis
|
|
|
|
- **Spec-Abdeckung:** Alle 7 Anforderungen (2 Karten, 1 Sektion, 3 Effekte, page.tsx) haben Tasks ✓
|
|
- **Keine Platzhalter:** Alle Code-Blöcke sind vollständig ✓
|
|
- **Typ-Konsistenz:** `ScrollReveal` wird in Tasks 5 und 6 identisch verwendet ✓
|
|
- **useTypewriter:** Hook gibt `string` zurück, wird in Hero als `{typed}` eingesetzt ✓
|