feat(privat/CV): sync to latest upstream (cv-upstream/main, 30f9608b)
This commit is contained in:
@@ -0,0 +1,794 @@
|
||||
# Design Document: personal-homepage (andreknie.de)
|
||||
|
||||
## Overview
|
||||
|
||||
A personal homepage for Dr. Andre Knie at andreknie.de, built as a modern single-page application with multiple routes. The site serves as a central platform for trust-building, content access, speaking visibility, consulting positioning, and lead generation. It uses the same tech stack and architectural patterns as the Projekt-KIQ-HP reference implementation: Vite 8 + React 19 (JSX), react-router-dom v7, Express 5 backend, CSS custom properties for theming, glass morphism design, and Docker + Caddy deployment.
|
||||
|
||||
Content is managed via markdown and YAML files in a `content/` directory (file-based CMS), eliminating the need for a database for content storage. The backend handles only form submissions (contact, talk requests, newsletter signup, resource lead capture) via nodemailer.
|
||||
|
||||
All colors are defined exclusively through CSS custom properties, making the entire color scheme changeable from a single `:root` block.
|
||||
|
||||
### Content Visibility Model
|
||||
|
||||
Each content object has a `visibility` field:
|
||||
- **primary** — actively featured on the homepage, content highlights, and CTAs. These are the core topics (KI, Transformation, Leadership, Digitalisierung).
|
||||
- **secondary** — visible in content archives and when browsing/filtering, but NOT featured on the homepage hero, highlights section, or CTAs. These are adjacent topics (Fehlerkultur, Agilität-Seminare, non-AI workshops) that demonstrate breadth without diluting the core positioning.
|
||||
|
||||
Default visibility is `primary` if omitted. The homepage highlights section and ContentHighlights component only pull `primary` content. The full archive pages (Kniepunkt, Podcast, Speaking, etc.) show all content regardless of visibility.
|
||||
|
||||
## Architecture
|
||||
|
||||
### High-Level Architecture
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Client (Browser)"
|
||||
SPA[React 19 SPA]
|
||||
Router[react-router-dom v7]
|
||||
end
|
||||
|
||||
subgraph "Build Time"
|
||||
Vite[Vite 8 Build]
|
||||
ContentLoader[Content Loader Plugin]
|
||||
MD[Markdown Files]
|
||||
YAML[YAML Metadata Files]
|
||||
end
|
||||
|
||||
subgraph "Production Server"
|
||||
Caddy[Caddy Reverse Proxy]
|
||||
Static[Static Files /opt/andreknie/site]
|
||||
Express[Express 5 Backend :3003]
|
||||
Nodemailer[Nodemailer]
|
||||
end
|
||||
|
||||
subgraph "External"
|
||||
SMTP[SMTP Server]
|
||||
PodcastHost[Podcast Hosting Platform]
|
||||
end
|
||||
|
||||
MD --> ContentLoader
|
||||
YAML --> ContentLoader
|
||||
ContentLoader --> Vite
|
||||
Vite --> Static
|
||||
|
||||
SPA --> Router
|
||||
Router --> Caddy
|
||||
Caddy -->|/api/*| Express
|
||||
Caddy -->|static| Static
|
||||
Express --> Nodemailer
|
||||
Nodemailer --> SMTP
|
||||
SPA -->|audio links| PodcastHost
|
||||
```
|
||||
|
||||
### Content Pipeline
|
||||
|
||||
Content flows from markdown/YAML files through a Vite build-time plugin that transforms them into JSON modules importable by React components. This means:
|
||||
|
||||
1. Stakeholder creates/edits `.md` or `.yaml` files in `content/`
|
||||
2. A Vite plugin (`vite-plugin-content`) reads and transforms these at build time
|
||||
3. The built SPA includes all content as static JSON data
|
||||
4. New content becomes visible after the next `npm run build` + deploy
|
||||
|
||||
### Request Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant V as Visitor
|
||||
participant C as Caddy
|
||||
participant S as Static Files
|
||||
participant E as Express Backend
|
||||
|
||||
V->>C: GET /kniepunkt
|
||||
C->>S: Serve index.html (SPA)
|
||||
S-->>V: React app loads
|
||||
|
||||
V->>C: POST /api/contact
|
||||
C->>E: Proxy to Express
|
||||
E->>E: Validate + store
|
||||
E->>E: Send confirmation email
|
||||
E-->>V: 201 Created
|
||||
```
|
||||
|
||||
## Components and Interfaces
|
||||
|
||||
### Component Hierarchy
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
App[App.jsx]
|
||||
App --> BG[BackgroundPattern]
|
||||
App --> Nav[Navigation]
|
||||
App --> Routes[Routes]
|
||||
App --> Footer[Footer]
|
||||
App --> Dock[BottomDock]
|
||||
|
||||
Routes --> Home[HomePage]
|
||||
Routes --> Kniepunkt[KniepunktPage]
|
||||
Routes --> Podcast[PodcastPage]
|
||||
Routes --> Speaking[SpeakingPage]
|
||||
Routes --> Consulting[ConsultingPage]
|
||||
Routes --> Resources[ResourcesPage]
|
||||
Routes --> Contact[ContactPage]
|
||||
Routes --> Impressum[ImpressumPage]
|
||||
Routes --> Datenschutz[DatenschutzPage]
|
||||
|
||||
Home --> Hero[Hero]
|
||||
Home --> Stats[Stats]
|
||||
Home --> ContentHighlights[ContentHighlights]
|
||||
Home --> AudienceSection[AudienceSection]
|
||||
Home --> CTA[CTASection]
|
||||
|
||||
Kniepunkt --> IssueList[IssueList]
|
||||
Kniepunkt --> IssueDetail[IssueDetail]
|
||||
|
||||
Podcast --> EpisodeList[EpisodeList]
|
||||
Podcast --> EpisodeCard[EpisodeCard]
|
||||
|
||||
Speaking --> TalkList[TalkList]
|
||||
Speaking --> TalkRequestForm[TalkRequestForm]
|
||||
|
||||
Consulting --> ExampleGrid[ExampleGrid]
|
||||
Consulting --> ExampleCard[ExampleCard]
|
||||
|
||||
Resources --> ResourceList[ResourceList]
|
||||
Resources --> LeadCaptureForm[LeadCaptureForm]
|
||||
|
||||
Contact --> ContactForm[ContactForm]
|
||||
|
||||
Footer --> NewsletterForm[NewsletterForm]
|
||||
Footer --> DHiveLink[dHive Cross-Reference]
|
||||
|
||||
```
|
||||
|
||||
### File/Folder Structure
|
||||
|
||||
```
|
||||
andreknie.de/
|
||||
├── content/ # File-based CMS (stakeholder edits here)
|
||||
│ ├── posts/ # LinkedIn posts & articles (.md)
|
||||
│ │ └── 2025-01-15-ai-act.md
|
||||
│ ├── kniepunkt/ # Column issues (.md)
|
||||
│ │ └── 038-sovereign-ai.md
|
||||
│ ├── podcast/ # Episode metadata (.yaml)
|
||||
│ │ └── ep-012-llm-limits.yaml
|
||||
│ ├── talks/ # Talk metadata (.yaml)
|
||||
│ │ └── ki-fraitag-2025.yaml
|
||||
│ ├── consulting/ # Consulting examples (.yaml)
|
||||
│ │ └── mittelstand-predictive.yaml
|
||||
│ ├── resources/ # Resource metadata (.yaml)
|
||||
│ │ └── ai-readiness-checklist.yaml
|
||||
│ ├── events/ # Events (.yaml)
|
||||
│ │ └── 2025-09-ai-masterclass.yaml
|
||||
│ └── meta/ # Site-wide metadata
|
||||
│ ├── profile.yaml # Stakeholder profile data
|
||||
│ ├── stats.yaml # Stats section numbers
|
||||
│ └── audiences.yaml # Target audience descriptions
|
||||
├── public/
|
||||
│ ├── images/ # Static images
|
||||
│ ├── logos/ # Logos (personal + dHive)
|
||||
│ ├── resources/ # Downloadable files (PDFs etc.)
|
||||
│ └── patterns/ # Background pattern SVGs
|
||||
├── src/
|
||||
│ ├── components/ # Shared UI components
|
||||
│ │ ├── BackgroundPattern.jsx + .css
|
||||
│ │ ├── BottomDock.jsx + .css
|
||||
│ │ ├── Navigation.jsx + .css
|
||||
│ │ ├── Footer.jsx + .css
|
||||
│ │ ├── Hero.jsx + .css
|
||||
│ │ ├── Stats.jsx + .css
|
||||
│ │ ├── ContentHighlights.jsx + .css
|
||||
│ │ ├── AudienceSection.jsx + .css
|
||||
│ │ ├── CTASection.jsx + .css
|
||||
│ │ ├── NewsletterForm.jsx + .css
|
||||
│ │ ├── ContactForm.jsx + .css
|
||||
│ │ ├── TalkRequestForm.jsx + .css
|
||||
│ │ ├── LeadCaptureForm.jsx + .css
|
||||
│ │ ├── ContentFilter.jsx + .css
|
||||
│ │ ├── Pagination.jsx + .css
|
||||
│ │ ├── GlassCard.jsx + .css
|
||||
│ │ ├── BentoGrid.jsx + .css
|
||||
│ │ ├── ScrollToTop.jsx
|
||||
│ │ └── SEOHead.jsx
|
||||
│ ├── pages/
|
||||
│ │ ├── Home.jsx
|
||||
│ │ ├── Kniepunkt.jsx + .css
|
||||
│ │ ├── KniepunktIssue.jsx + .css
|
||||
│ │ ├── Podcast.jsx + .css
|
||||
│ │ ├── Speaking.jsx + .css
|
||||
│ │ ├── Consulting.jsx + .css
|
||||
│ │ ├── Resources.jsx + .css
|
||||
│ │ ├── Contact.jsx + .css
|
||||
│ │ ├── Impressum.jsx
|
||||
│ │ └── Datenschutz.jsx
|
||||
│ ├── data/ # Build-time generated JSON (via Vite plugin)
|
||||
│ │ └── (auto-generated)
|
||||
│ ├── hooks/ # Custom React hooks
|
||||
│ │ ├── useContent.js # Content loading/filtering
|
||||
│ │ └── useFormSubmit.js # Form submission with validation
|
||||
│ ├── utils/
|
||||
│ │ ├── contentLoader.js # Content transformation utilities
|
||||
│ │ ├── validation.js # Form validation logic
|
||||
│ │ └── formatDate.js # Date formatting (German locale)
|
||||
│ ├── App.jsx
|
||||
│ ├── index.css # Global styles + CSS variables
|
||||
│ └── main.jsx
|
||||
├── server/
|
||||
│ ├── index.js # Express 5 entry point
|
||||
│ ├── routes/
|
||||
│ │ ├── contact.js # Contact form handler
|
||||
│ │ ├── talk-request.js # Talk request handler
|
||||
│ │ ├── newsletter.js # Newsletter signup handler
|
||||
│ │ └── resource-download.js # Lead capture handler
|
||||
│ ├── middleware/
|
||||
│ │ ├── rateLimiter.js # Rate limiting
|
||||
│ │ └── validator.js # Input validation
|
||||
│ ├── services/
|
||||
│ │ ├── mailer.js # Nodemailer configuration
|
||||
│ │ └── confirmationToken.js # Token generation/verification
|
||||
│ └── data/ # Runtime data (leads, pending confirmations)
|
||||
│ └── .gitkeep
|
||||
├── plugins/
|
||||
│ └── vite-plugin-content.js # Vite plugin for content transformation
|
||||
├── Dockerfile
|
||||
├── docker-compose.yml
|
||||
├── Caddyfile
|
||||
├── package.json
|
||||
├── vite.config.js
|
||||
└── .env.example
|
||||
```
|
||||
|
||||
### Shared Components
|
||||
|
||||
| Component | Purpose | Pattern from Blueprint |
|
||||
|-----------|---------|----------------------|
|
||||
| `BackgroundPattern` | Animated drifting SVG background | Same mirrored-quadrant technique |
|
||||
| `BottomDock` | Fixed bottom navigation pill with rotating light border | Same dock pattern, adapted nav items |
|
||||
| `Navigation` | Top bar with logo + hamburger overlay menu | Same overlay pattern |
|
||||
| `Footer` | CTA section + newsletter form + legal links + dHive reference | Extended from blueprint |
|
||||
| `GlassCard` | Reusable glass-morphism card | Extracted from blueprint's inline usage |
|
||||
| `BentoGrid` | Responsive grid layout for content cards | Same bento pattern |
|
||||
| `ContentFilter` | Tag-based filtering UI | New component |
|
||||
| `Pagination` | Page navigation for lists (10 items/page) | New component |
|
||||
|
||||
### Page Composition
|
||||
|
||||
| Page | URL | Key Components |
|
||||
|------|-----|---------------|
|
||||
| Home | `/` | Hero, Stats, ContentHighlights, AudienceSection, CTASection |
|
||||
| Kniepunkt | `/kniepunkt` | IssueList with ContentFilter |
|
||||
| Kniepunkt Issue | `/kniepunkt/:slug` | Full issue content (rendered markdown) |
|
||||
| Podcast | `/podcast` | EpisodeList with Pagination |
|
||||
| Speaking | `/speaking` | TalkList + TalkRequestForm |
|
||||
| Consulting | `/consulting` | ExampleGrid (BentoGrid of ExampleCards) |
|
||||
| Resources | `/resources` | ResourceList + LeadCaptureForm (modal) |
|
||||
| Contact | `/kontakt` | ContactForm |
|
||||
| Impressum | `/impressum` | Static legal content |
|
||||
| Datenschutz | `/datenschutz` | Static legal content |
|
||||
|
||||
## Data Models
|
||||
|
||||
### Content File Schemas
|
||||
|
||||
#### Post (Markdown with Frontmatter)
|
||||
|
||||
```yaml
|
||||
# content/posts/2025-01-15-ai-act.md frontmatter
|
||||
---
|
||||
title: "Der AI Act ist da — was bedeutet das für den Mittelstand?"
|
||||
date: 2025-01-15
|
||||
tags: [ai-act, regulation, mittelstand]
|
||||
summary: "Eine nüchterne Einordnung des EU AI Act für deutsche Unternehmen."
|
||||
source: linkedin # or "original"
|
||||
visibility: primary # primary = featured on homepage/highlights | secondary = visible in archive only
|
||||
---
|
||||
# Markdown body follows...
|
||||
```
|
||||
|
||||
#### Kniepunkt Issue (Markdown with Frontmatter)
|
||||
|
||||
```yaml
|
||||
# content/kniepunkt/038-sovereign-ai.md frontmatter
|
||||
---
|
||||
title: "Souveräne KI — mehr als ein Buzzword"
|
||||
issue: 38
|
||||
date: 2025-06-01
|
||||
tags: [sovereignty, infrastructure, germany]
|
||||
summary: "Warum digitale Souveränität bei KI kein Marketing-Claim sein darf."
|
||||
---
|
||||
# Markdown body follows...
|
||||
```
|
||||
|
||||
#### Podcast Episode (YAML)
|
||||
|
||||
```yaml
|
||||
# content/podcast/ep-012-llm-limits.yaml
|
||||
id: ep-012-llm-limits
|
||||
title: "Die Grenzen großer Sprachmodelle"
|
||||
date: 2025-05-15
|
||||
summary: "Was LLMs können, was nicht — und warum das wichtig ist."
|
||||
duration: "42:15"
|
||||
tags: [llm, limits, practical-ai]
|
||||
audio_url: "https://podcast-host.example/episodes/012.mp3"
|
||||
external_links:
|
||||
spotify: "https://open.spotify.com/episode/..."
|
||||
apple: "https://podcasts.apple.com/..."
|
||||
```
|
||||
|
||||
#### Talk (YAML)
|
||||
|
||||
```yaml
|
||||
# content/talks/ki-fraitag-2025.yaml
|
||||
id: ki-fraitag-2025
|
||||
title: "KI im Mittelstand — Chancen ohne Hype"
|
||||
description: "Keynote über praktische KI-Anwendungen für mittelständische Unternehmen."
|
||||
format: keynote # keynote | workshop | panel
|
||||
target_audience: [geschaeftsfuehrung, it-leitung]
|
||||
tags: [mittelstand, practical-ai, sovereignty]
|
||||
visibility: primary # primary = actively promoted on homepage | secondary = visible in archive but not featured
|
||||
events:
|
||||
- name: "KI-FreiTag Nordhessen"
|
||||
date: 2025-03-14
|
||||
location: "Science Park Kassel"
|
||||
- name: "RKW Hessen Workshop"
|
||||
date: 2025-01-20
|
||||
location: "Frankfurt am Main"
|
||||
```
|
||||
|
||||
#### Consulting Example (YAML)
|
||||
|
||||
```yaml
|
||||
# content/consulting/mittelstand-predictive.yaml
|
||||
id: mittelstand-predictive
|
||||
title: "Predictive Maintenance für Produktionsunternehmen"
|
||||
industry: manufacturing
|
||||
organization_size: "200-500" # employees range
|
||||
problem_domain: "Ungeplante Maschinenausfälle verursachten hohe Kosten."
|
||||
approach: "Aufbau eines ML-Modells auf Basis vorhandener Sensordaten, souverän gehostet."
|
||||
outcomes: "40% weniger ungeplante Stillstände innerhalb von 6 Monaten."
|
||||
tags: [predictive-maintenance, manufacturing, ml]
|
||||
```
|
||||
|
||||
#### Resource (YAML)
|
||||
|
||||
```yaml
|
||||
# content/resources/ai-readiness-checklist.yaml
|
||||
id: ai-readiness-checklist
|
||||
title: "AI Readiness Checklist für den Mittelstand"
|
||||
description: "Praktische Checkliste zur Bewertung der KI-Bereitschaft."
|
||||
file: "ai-readiness-checklist.pdf" # relative to public/resources/
|
||||
requires_lead: true
|
||||
tags: [checklist, readiness, mittelstand]
|
||||
```
|
||||
|
||||
#### Event (YAML)
|
||||
|
||||
```yaml
|
||||
# content/events/2025-09-ai-masterclass.yaml
|
||||
id: 2025-09-ai-masterclass
|
||||
title: "AI Masterclass — Science Park Kassel"
|
||||
date: 2025-09-18
|
||||
location: "Science Park Kassel"
|
||||
event_type: workshop # talk | workshop | conference | appearance
|
||||
description: "Ganztägiger Workshop zu praktischer KI-Implementierung."
|
||||
tags: [workshop, kassel, hands-on]
|
||||
```
|
||||
|
||||
#### Profile Metadata (YAML)
|
||||
|
||||
```yaml
|
||||
# content/meta/profile.yaml
|
||||
name: "Dr. Andre Knie"
|
||||
roles:
|
||||
- "AI Expert"
|
||||
- "Consultant"
|
||||
- "Speaker"
|
||||
- "Content Author"
|
||||
- "Managing Director, dHive"
|
||||
summary: "Nüchterne KI-Expertise für den deutschen Mittelstand. Souverän, praktisch, evidenzbasiert."
|
||||
photo: "/images/andre-knie.png"
|
||||
positioning:
|
||||
sovereignty: "Unabhängig von US-Hyperscalern — KI-Lösungen aus und für Deutschland."
|
||||
data_sovereignty: "100% DSGVO- und AI-Act-konform, EU-Hosting."
|
||||
practical: "Fokus auf anwendbare KI, nicht auf Hype."
|
||||
dhive:
|
||||
role: "Founder & Managing Director"
|
||||
url: "https://d-hive.de"
|
||||
description: "Data Hive Cassel — souveräne KI-Infrastruktur für den Mittelstand."
|
||||
audiences:
|
||||
- id: mittelstand
|
||||
label: "Mittelständische Unternehmen"
|
||||
- id: public-admin
|
||||
label: "Öffentliche Verwaltung"
|
||||
- id: social-institutions
|
||||
label: "Soziale Einrichtungen"
|
||||
- id: universities
|
||||
label: "Universitäten & Hochschulen"
|
||||
- id: hr-od
|
||||
label: "Personal- & Organisationsentwicklung"
|
||||
- id: it-leadership
|
||||
label: "IT-Leitung & CTO"
|
||||
- id: executives
|
||||
label: "Geschäftsführung"
|
||||
- id: event-organizers
|
||||
label: "Veranstalter & Speaker-Booking"
|
||||
```
|
||||
|
||||
#### Stats (YAML)
|
||||
|
||||
```yaml
|
||||
# content/meta/stats.yaml
|
||||
items:
|
||||
- value: "50+"
|
||||
label: "Projekte"
|
||||
- value: "13+"
|
||||
label: "Jahre KI"
|
||||
- value: "60+"
|
||||
label: "Publikationen"
|
||||
- value: "100%"
|
||||
label: "DSGVO & AI-Act"
|
||||
```
|
||||
|
||||
### API Data Models
|
||||
|
||||
#### Contact Request (POST /api/contact)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "string (required, max 100)",
|
||||
"email": "string (required, valid email)",
|
||||
"message": "string (required, max 2000)"
|
||||
}
|
||||
```
|
||||
|
||||
#### Talk Request (POST /api/talk-request)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "string (required, max 100)",
|
||||
"email": "string (required, valid email)",
|
||||
"event_name": "string (required, max 200)",
|
||||
"topic": "string (required, max 200)",
|
||||
"message": "string (required, max 2000)"
|
||||
}
|
||||
```
|
||||
|
||||
#### Newsletter Signup (POST /api/newsletter)
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "string (required, valid email)"
|
||||
}
|
||||
```
|
||||
|
||||
#### Resource Lead Capture (POST /api/resource-download)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "string (required, max 100)",
|
||||
"email": "string (required, valid email)",
|
||||
"company": "string (required, max 150)",
|
||||
"resource_id": "string (required)"
|
||||
}
|
||||
```
|
||||
|
||||
### API Endpoints
|
||||
|
||||
| Method | Path | Purpose | Auth |
|
||||
|--------|------|---------|------|
|
||||
| POST | `/api/contact` | Submit contact form, triggers confirmation email | Public |
|
||||
| POST | `/api/contact/confirm/:token` | Confirm contact request, forwards to stakeholder | Public |
|
||||
| POST | `/api/talk-request` | Submit talk request, triggers confirmation email | Public |
|
||||
| POST | `/api/talk-request/confirm/:token` | Confirm talk request, forwards to stakeholder | Public |
|
||||
| POST | `/api/newsletter` | Newsletter signup, triggers double opt-in email | Public |
|
||||
| GET | `/api/newsletter/confirm/:token` | Confirm newsletter subscription | Public |
|
||||
| POST | `/api/resource-download` | Submit lead data, returns download URL | Public |
|
||||
| GET | `/api/health` | Health check | Public |
|
||||
|
||||
### Confirmation Token Flow
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant V as Visitor
|
||||
participant API as Express API
|
||||
participant DB as JSON File Store
|
||||
participant Mail as Nodemailer
|
||||
|
||||
V->>API: POST /api/contact {name, email, message}
|
||||
API->>API: Validate input
|
||||
API->>DB: Store pending request + token (expires 24h)
|
||||
API->>Mail: Send confirmation email with link
|
||||
Mail-->>V: Email with confirmation link
|
||||
V->>API: POST /api/contact/confirm/:token
|
||||
API->>DB: Verify token, mark confirmed
|
||||
API->>Mail: Forward message to stakeholder
|
||||
API-->>V: 200 OK (success page)
|
||||
```
|
||||
|
||||
### Theming System
|
||||
|
||||
All visual theming is controlled via CSS custom properties in `:root`. Changing the accent color updates the entire site.
|
||||
|
||||
```css
|
||||
:root {
|
||||
/* Base colors — dark theme */
|
||||
--bg-color: #0a0a0a;
|
||||
--bg-elevated: #141414;
|
||||
--text-primary: #f0f0f0;
|
||||
--text-secondary: #999999;
|
||||
|
||||
/* Accent — CHANGE THESE to retheme the entire site */
|
||||
--accent-color: #4a9eff; /* Placeholder: neutral blue */
|
||||
--accent-color-rgb: 74, 158, 255; /* Same as above in RGB for rgba() usage */
|
||||
--accent-gradient: linear-gradient(135deg, #4a9eff, #2563eb);
|
||||
--accent-hover: #2563eb;
|
||||
|
||||
/* Glass morphism */
|
||||
--glass-bg: rgba(25, 25, 25, 0.6);
|
||||
--glass-border: rgba(255, 255, 255, 0.08);
|
||||
--glass-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
|
||||
|
||||
/* Spacing */
|
||||
--section-padding: 120px;
|
||||
--container-max: 1200px;
|
||||
|
||||
/* Typography */
|
||||
--font-family: 'Inter', sans-serif;
|
||||
--font-weight-normal: 400;
|
||||
--font-weight-semibold: 600;
|
||||
--font-weight-bold: 800;
|
||||
|
||||
/* Transitions */
|
||||
--transition-smooth: 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
--transition-fast: 0.3s ease;
|
||||
|
||||
/* Border radius */
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 16px;
|
||||
--radius-lg: 24px;
|
||||
--radius-pill: 50px;
|
||||
}
|
||||
```
|
||||
|
||||
**Design rationale:** Using `Inter` (from Google Fonts) instead of `Outfit` from the blueprint, as Inter is the stakeholder's established corporate font. The placeholder accent is a neutral blue — the stakeholder will decide final colors later. All accent references use the CSS variables, so a single edit to `:root` changes everything.
|
||||
|
||||
### Visual Effects (from Blueprint)
|
||||
|
||||
| Effect | Implementation | CSS Pattern |
|
||||
|--------|---------------|-------------|
|
||||
| Glass panels | `backdrop-filter: blur(10px)` + semi-transparent bg + subtle border | `.glass-panel` class |
|
||||
| Gradient text | `background-clip: text` with accent gradient | `.text-gradient` class |
|
||||
| Animated background | Fixed SVG pattern with CSS `@keyframes` drift animation | `BackgroundPattern` component |
|
||||
| Hover glow | Radial gradient overlay on hover | `.bento-glow` pattern |
|
||||
| Card lift | `transform: translateY(-8px)` on hover | Applied to all interactive cards |
|
||||
| Dock light | `conic-gradient` rotating border via `::before` pseudo-element | `BottomDock` component |
|
||||
| Fade-up entrance | `@keyframes fadeUp` with opacity + translateY | Hero and section entrances |
|
||||
| Smooth scrolling | `scroll-behavior: smooth` on html | Global CSS |
|
||||
|
||||
### Deployment Architecture
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph "VPS / Server"
|
||||
subgraph "Docker"
|
||||
Backend[Express Container :3003]
|
||||
end
|
||||
Caddy[Caddy Server]
|
||||
StaticFiles[/opt/andreknie/site/]
|
||||
end
|
||||
|
||||
DNS[andreknie.de DNS] --> Caddy
|
||||
Caddy -->|HTTPS auto-cert| Caddy
|
||||
Caddy -->|/api/*| Backend
|
||||
Caddy -->|static| StaticFiles
|
||||
```
|
||||
|
||||
**Caddyfile** for andreknie.de:
|
||||
- Auto HTTPS via Let's Encrypt
|
||||
- Reverse proxy `/api/*` to Express backend on port 3003
|
||||
- Serve static SPA files with `try_files {path} /index.html`
|
||||
- Security headers (HSTS, X-Content-Type-Options, CSP, etc.)
|
||||
- Gzip/zstd compression
|
||||
- Immutable cache for `/assets/*`
|
||||
|
||||
**Docker Compose:**
|
||||
- Single `backend` service running Express
|
||||
- Volume mount for `server/data/` (persistent lead/confirmation data)
|
||||
- Environment variables via `.env` file
|
||||
|
||||
**Build & Deploy workflow:**
|
||||
1. `npm run build` → produces `dist/` with static SPA
|
||||
2. Copy `dist/` to `/opt/andreknie/site/` on server
|
||||
3. `docker compose up -d` for backend
|
||||
4. Caddy serves both static files and proxies API
|
||||
|
||||
### Content Management Workflow
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Stakeholder creates .md or .yaml file] --> B[Commits to git repository]
|
||||
B --> C[CI/CD triggers build]
|
||||
C --> D[Vite plugin transforms content to JSON]
|
||||
D --> E[Static site built to dist/]
|
||||
E --> F[Deploy to server]
|
||||
F --> G[Content visible on andreknie.de]
|
||||
```
|
||||
|
||||
**How the stakeholder adds content:**
|
||||
1. Create a new file in the appropriate `content/` subdirectory
|
||||
2. Follow the schema (frontmatter for markdown, full YAML for metadata-only content)
|
||||
3. Commit and push (or manually trigger deploy)
|
||||
4. The Vite build picks up the new file automatically
|
||||
5. No code changes required for new content
|
||||
|
||||
**Content validation:** The Vite plugin validates content files at build time against expected schemas. Missing required fields cause build warnings (not failures, to avoid blocking deploys for minor issues).
|
||||
|
||||
|
||||
## Correctness Properties
|
||||
|
||||
*A property is a characteristic or behavior that should hold true across all valid executions of a system — essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
||||
|
||||
### Property 1: Content rendering completeness
|
||||
|
||||
*For any* content object of a given type (Post, Talk, Consulting_Example, Newsletter_Issue, Podcast_Episode, Resource, Event), rendering that object must produce output containing all required fields defined for that type (e.g., a Post must show title, date, and body; a Talk must show title, description, and at least one past event; a Consulting_Example must show title, industry, org-size, problem, approach, and outcomes).
|
||||
|
||||
**Validates: Requirements 3.1, 3.2, 3.5, 4.1, 5.2, 6.1, 6.3, 7.2, 8.2, 8.4**
|
||||
|
||||
### Property 2: Content list sorting
|
||||
|
||||
*For any* list of content objects displayed within a content area (posts, kniepunkt issues, podcast episodes, or any other content type), the items must be ordered by publication date in descending order (newest first).
|
||||
|
||||
**Validates: Requirements 3.3, 7.3, 8.2, 14.5**
|
||||
|
||||
### Property 3: Form validation rejects invalid input
|
||||
|
||||
*For any* form submission (contact, talk request, newsletter, resource download) where one or more required fields are empty or the email field does not conform to a valid email format, the system must reject the submission, display error indications for each invalid field, and retain all previously entered valid data.
|
||||
|
||||
**Validates: Requirements 4.4, 10.5, 11.5, 12.5**
|
||||
|
||||
### Property 4: Confirmation token expiration
|
||||
|
||||
*For any* confirmation token (contact: 24h, talk request: 48h, newsletter: 48h) that has exceeded its expiration period, attempting to confirm that token must be rejected and the associated pending record must be discarded without forwarding to the stakeholder or activating the subscription.
|
||||
|
||||
**Validates: Requirements 10.4, 11.4, 12.4**
|
||||
|
||||
### Property 5: Valid submission triggers confirmation email
|
||||
|
||||
*For any* valid form submission (contact, talk request, or newsletter signup) with all required fields present and a correctly formatted email address, the system must generate a unique confirmation token, store the pending request, and send exactly one confirmation email to the provided address.
|
||||
|
||||
**Validates: Requirements 10.3, 11.2, 12.2**
|
||||
|
||||
### Property 6: Valid token confirmation forwards to stakeholder
|
||||
|
||||
*For any* confirmation token that is valid and unexpired, confirming that token must result in the associated message/request being forwarded to the stakeholder via email notification, and the pending record being marked as confirmed.
|
||||
|
||||
**Validates: Requirements 11.3, 12.3**
|
||||
|
||||
### Property 7: Content filtering returns only matching items
|
||||
|
||||
*For any* tag-based filter criterion and any set of content objects, applying the filter must return exactly those content objects whose metadata tags include the selected criterion — no more, no less.
|
||||
|
||||
**Validates: Requirements 13.1, 13.2**
|
||||
|
||||
### Property 8: Dynamic filter options derived from content
|
||||
|
||||
*For any* set of content objects with tags, the available filter options presented to the visitor must equal the complete set of distinct tags present across all content objects (up to the 20-tag maximum).
|
||||
|
||||
**Validates: Requirements 13.4, 13.5**
|
||||
|
||||
### Property 9: Event chronological grouping
|
||||
|
||||
*For any* set of events containing both upcoming (date ≥ today) and past (date < today) entries, the display must show all upcoming events before all past events, with each group internally sorted by date descending, and events grouped by year.
|
||||
|
||||
**Validates: Requirements 9.2**
|
||||
|
||||
### Property 10: Content file validation
|
||||
|
||||
*For any* content file, if it is missing a required attribute (title, date, content-area for general content; title, date, location, event_type for events) or specifies an invalid content-area value, the validation system must reject it with an error message indicating the specific missing or invalid fields.
|
||||
|
||||
**Validates: Requirements 9.4, 14.1, 14.4**
|
||||
|
||||
### Property 11: Lead data storage integrity
|
||||
|
||||
*For any* successful resource download lead capture (valid name, email, company, resource_id), the system must store a record containing all submitted fields plus the resource identifier and a timestamp, and that record must be retrievable.
|
||||
|
||||
**Validates: Requirements 4.5**
|
||||
|
||||
### Property 12: Pagination constraint
|
||||
|
||||
*For any* list of podcast episodes (or other paginated content) with more than 10 items, each page must contain at most 10 items, and pagination controls must be present to access all items.
|
||||
|
||||
**Validates: Requirements 8.3**
|
||||
|
||||
### Property 13: Duplicate subscription detection
|
||||
|
||||
*For any* email address that is already associated with an active newsletter subscription, submitting that email again must inform the visitor that the email is already subscribed and must not create a duplicate entry.
|
||||
|
||||
**Validates: Requirements 10.6**
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Client-Side Errors
|
||||
|
||||
| Scenario | Handling |
|
||||
|----------|----------|
|
||||
| Form validation failure | Inline error messages next to invalid fields; valid data retained |
|
||||
| Content not found (404 route) | Friendly error page with navigation back to home |
|
||||
| Network error on form submit | Toast notification with retry option |
|
||||
| Content loading failure | Graceful fallback message per section |
|
||||
|
||||
### Server-Side Errors
|
||||
|
||||
| Scenario | Handling |
|
||||
|----------|----------|
|
||||
| Invalid request body | 400 response with field-level error details |
|
||||
| Rate limit exceeded | 429 response with retry-after header |
|
||||
| SMTP failure | Log error, return 500 with user-friendly message; store submission for retry |
|
||||
| Invalid confirmation token | 404 response, display "link expired or invalid" page |
|
||||
| Expired confirmation token | 410 Gone response, display "link expired" message |
|
||||
| Server crash | Express error middleware catches unhandled errors, returns 500 |
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
- Contact form: 3 submissions per IP per hour
|
||||
- Talk request: 3 submissions per IP per hour
|
||||
- Newsletter signup: 5 submissions per IP per hour
|
||||
- Resource download: 10 submissions per IP per hour
|
||||
|
||||
### Data Persistence
|
||||
|
||||
- Pending confirmations stored in JSON files in `server/data/`
|
||||
- Confirmed leads stored in `server/data/leads.json`
|
||||
- Expired tokens cleaned up by a scheduled interval (every hour)
|
||||
- All data files are volume-mounted in Docker for persistence across container restarts
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Property-Based Testing
|
||||
|
||||
This feature is suitable for property-based testing in the following areas:
|
||||
- **Content transformation logic** (Vite plugin parsing, validation, sorting)
|
||||
- **Form validation logic** (email validation, field presence, length constraints)
|
||||
- **Token management** (generation, expiration, confirmation)
|
||||
- **Content filtering** (tag matching, dynamic filter derivation)
|
||||
- **Pagination logic** (page size constraints, offset calculation)
|
||||
|
||||
**Library:** [fast-check](https://github.com/dubzzz/fast-check) (JavaScript property-based testing library)
|
||||
|
||||
**Configuration:**
|
||||
- Minimum 100 iterations per property test
|
||||
- Each test tagged with: `Feature: personal-homepage, Property {number}: {property_text}`
|
||||
|
||||
### Unit Tests (Example-Based)
|
||||
|
||||
| Area | What to Test |
|
||||
|------|-------------|
|
||||
| Component rendering | Hero shows name, photo, summary; Audience section shows all segments |
|
||||
| Form UI | All required fields present; submit button disabled when invalid |
|
||||
| Empty states | Sections hidden when no content; appropriate messages shown |
|
||||
| dHive link | Correct href, target="_blank", role text present |
|
||||
| Routing | All pages accessible, 404 for unknown routes |
|
||||
|
||||
### Integration Tests
|
||||
|
||||
| Area | What to Test |
|
||||
|------|-------------|
|
||||
| Content pipeline | Add content file → build → verify in output |
|
||||
| Form submission flow | Submit → confirmation email → confirm → stakeholder notification |
|
||||
| Lead capture flow | Submit lead data → receive download URL |
|
||||
| Build validation | Invalid content files produce warnings |
|
||||
|
||||
### End-to-End Tests
|
||||
|
||||
- Full contact form flow (submit → email → confirm → delivered)
|
||||
- Full talk request flow
|
||||
- Newsletter double opt-in flow
|
||||
- Resource download with lead capture
|
||||
- Content browsing and filtering
|
||||
|
||||
### Test Tools
|
||||
|
||||
- **fast-check** for property-based tests
|
||||
- **Vitest** for unit and integration tests
|
||||
- **React Testing Library** for component tests
|
||||
- **Playwright** for E2E tests (optional, for critical flows)
|
||||
|
||||
@@ -43,11 +43,11 @@ A personal homepage at andreknie.de for Dr. Andre Knie that serves as a central
|
||||
|
||||
### Requirement 2: Target Audience Orientation
|
||||
|
||||
**User Story:** As a visitor from a mid-sized company, HR, OD, IT leadership, or event organization, I want the homepage to address my context, so that I can evaluate the stakeholder's relevance for my needs.
|
||||
**User Story:** As a visitor from a mid-sized company, public administration, social institution, university, HR, OD, IT leadership, or event organization, I want the homepage to address my context, so that I can evaluate the stakeholder's relevance for my needs.
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
1. THE Homepage SHALL explicitly name mid-sized companies, HR and people development, organizational development, IT leaders, and managing directors as target audiences in visible page content so that each audience segment can identify itself within 1 scroll of the landing view
|
||||
1. THE Homepage SHALL explicitly name mid-sized companies, HR and people development, organizational development, IT leaders, managing directors, public administration, social institutions, and universities as target audiences in visible page content so that each audience segment can identify itself within 1 scroll of the landing view
|
||||
2. THE Homepage SHALL present at least 3 speaking examples including topic title, event name, and format (keynote, workshop, panel) to enable event organizers to evaluate the Stakeholder as a potential speaker
|
||||
3. THE Homepage SHALL present AI-related content using factual statements, concrete project references, or verifiable data points rather than unsubstantiated superlatives or subjective claims
|
||||
4. THE Homepage SHALL avoid superlative adjectives (e.g., "best", "leading", "unmatched"), unsubstantiated success claims without reference, and call-to-action language that urges immediate purchase or booking without prior context
|
||||
|
||||
@@ -0,0 +1,418 @@
|
||||
# Implementation Plan: personal-homepage (andreknie.de)
|
||||
|
||||
## Overview
|
||||
|
||||
Build a personal homepage for Dr. Andre Knie at andreknie.de using Vite 8 + React 19 (JSX), react-router-dom v7, Express 5 backend, CSS custom properties theming, file-based CMS (content/ directory with markdown + YAML), and Docker + Caddy deployment. The Projekt-KIQ-HP reference (cloned at reference/Projekt-KIQ-HP/) serves as a code reference for visual patterns (glass morphism, animated background, bottom dock, bento grid).
|
||||
|
||||
## Tasks
|
||||
|
||||
- [x] 1. Project scaffolding and core infrastructure
|
||||
- [x] 1.1 Initialize project with Vite 8 + React 19 and configure tooling
|
||||
- Create new repository `andreknie.de/` with `npm create vite@latest` (React template)
|
||||
- Install dependencies: react-router-dom v7, vite (v8), vitest, fast-check, @testing-library/react
|
||||
- Configure `vite.config.js` with plugin slot for content loader
|
||||
- Create `.env.example` with SMTP and server config placeholders
|
||||
- Set up `vitest.config.js` with jsdom environment
|
||||
- _Requirements: 18.1, 18.2_
|
||||
|
||||
- [x] 1.2 Create CSS theming system with custom properties
|
||||
- Create `src/index.css` with full `:root` variable block (colors, spacing, typography, transitions, radii)
|
||||
- Use placeholder neutral blue (`#4a9eff`) as accent color
|
||||
- Define glass morphism utility classes (`.glass-panel`, `.text-gradient`, `.bento-glow`)
|
||||
- Import Inter font from Google Fonts
|
||||
- Implement dark theme base (`--bg-color: #0a0a0a`)
|
||||
- _Requirements: 17.1, 17.2_
|
||||
|
||||
- [x] 1.3 Create directory structure for content, server, and plugins
|
||||
- Create `content/` with subdirectories: posts/, kniepunkt/, podcast/, talks/, consulting/, resources/, events/, meta/
|
||||
- Create `public/` with subdirectories: images/, logos/, resources/, patterns/
|
||||
- Create `server/` with subdirectories: routes/, middleware/, services/, data/
|
||||
- Create `plugins/` directory
|
||||
- Create `src/` with subdirectories: components/, pages/, hooks/, utils/, data/
|
||||
- _Requirements: 14.1, 14.3_
|
||||
|
||||
- [x] 1.4 Create sample content files for all content types
|
||||
- Create `content/meta/profile.yaml` with stakeholder profile data (name, roles, summary, positioning, dHive info)
|
||||
- Create `content/meta/stats.yaml` with key facts (50+ Projekte, 13+ Jahre KI, 60+ Publikationen, 100% DSGVO & AI-Act)
|
||||
- Create `content/meta/audiences.yaml` with target audience descriptions
|
||||
- Create at least 1 sample file per content type (post, kniepunkt issue, podcast episode, talk, consulting example, resource, event)
|
||||
- Follow exact YAML/frontmatter schemas from design document
|
||||
- _Requirements: 1.1, 1.2, 1.4, 2.1, 14.1, 15.1_
|
||||
|
||||
- [x] 2. Vite content plugin and content utilities
|
||||
- [x] 2.1 Implement `plugins/vite-plugin-content.js` for build-time content transformation
|
||||
- Read all files from `content/` subdirectories at build time
|
||||
- Parse markdown files with frontmatter (gray-matter) into JSON (title, date, tags, summary, body as HTML via marked/remark)
|
||||
- Parse YAML files into JSON objects
|
||||
- Validate required fields per content type (warn on missing, don't fail build)
|
||||
- Export transformed content as virtual modules importable by React components
|
||||
- _Requirements: 14.1, 14.2, 14.3_
|
||||
|
||||
- [x] 2.2 Implement `src/utils/contentLoader.js` for runtime content access
|
||||
- Implemented as `src/hooks/useContent.js` (loading by type, sorting by date desc, tag filtering)
|
||||
- Create utility functions to load content by type (getPosts, getTalks, getConsultingExamples, etc.)
|
||||
- Implement sorting by publication date descending (newest first)
|
||||
- Implement tag extraction for dynamic filter options (max 20 distinct tags)
|
||||
- Implement content-area validation (reject invalid content-area values with error message)
|
||||
- _Requirements: 13.4, 13.5, 14.4, 14.5_
|
||||
|
||||
- [ ]* 2.3 Write property tests for content transformation and validation (Property 10)
|
||||
- **Property 10: Content file validation**
|
||||
- Generate arbitrary content objects with random missing/invalid fields
|
||||
- Assert: missing required attributes → rejection with error indicating missing fields
|
||||
- Assert: invalid content-area → rejection with error listing valid areas
|
||||
- **Validates: Requirements 9.4, 14.1, 14.4**
|
||||
|
||||
- [ ]* 2.4 Write property tests for content list sorting (Property 2)
|
||||
- **Property 2: Content list sorting**
|
||||
- Generate arbitrary lists of content objects with random dates
|
||||
- Assert: output is always sorted by publication date descending
|
||||
- **Validates: Requirements 3.3, 7.3, 8.2, 14.5**
|
||||
|
||||
- [ ]* 2.5 Write property tests for dynamic filter derivation (Property 8)
|
||||
- **Property 8: Dynamic filter options derived from content**
|
||||
- Generate arbitrary sets of content objects with random tags
|
||||
- Assert: available filter options = complete set of distinct tags (up to 20)
|
||||
- **Validates: Requirements 13.4, 13.5**
|
||||
|
||||
- [ ] 3. Checkpoint — Content pipeline verified
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
- [x] 4. Shared UI components
|
||||
- [x] 4.1 Implement BackgroundPattern, GlassCard, and BentoGrid components
|
||||
- BackgroundPattern implemented (canvas particle field); glass/grid realized via `.glass-panel` utility + per-page grids
|
||||
- `BackgroundPattern.jsx` + `.css`: animated drifting SVG background (mirrored-quadrant technique from reference)
|
||||
- `GlassCard.jsx` + `.css`: reusable glass-morphism card with backdrop-filter blur
|
||||
- `BentoGrid.jsx` + `.css`: responsive grid layout for content cards with hover glow effect
|
||||
- Reference: Projekt-KIQ-HP patterns for visual implementation
|
||||
- _Requirements: 17.1, 17.2_
|
||||
|
||||
- [x] 4.2 Implement Navigation and BottomDock components
|
||||
- Navigation uses written-out top-bar links (no hamburger overlay, per stakeholder preference)
|
||||
- `Navigation.jsx` + `.css`: top bar with logo + hamburger overlay menu (same overlay pattern from reference)
|
||||
- `BottomDock.jsx` + `.css`: fixed bottom navigation pill with rotating conic-gradient light border
|
||||
- Navigation items: Home, Kniepunkt, Podcast, Speaking, Consulting, Resources, Kontakt
|
||||
- _Requirements: 1.1_
|
||||
|
||||
- [x] 4.3 Implement Footer with dHive cross-reference and newsletter form slot
|
||||
- `Footer.jsx` + `.css`: CTA section + newsletter form slot + legal links (Impressum, Datenschutz)
|
||||
- Include visible link to d-hive.de with stakeholder role (Founder & Managing Director)
|
||||
- dHive branding ≤ 20% of section area, positioned after personal name/title
|
||||
- Link opens in new tab (`target="_blank"`)
|
||||
- _Requirements: 16.1, 16.2, 16.3_
|
||||
|
||||
- [ ] 4.4 Implement ContentFilter and Pagination components
|
||||
- OPEN: dedicated ContentFilter, Pagination, SEOHead components not yet implemented (ScrollToTop exists)
|
||||
- `ContentFilter.jsx` + `.css`: tag-based filtering UI, derives options dynamically from content metadata
|
||||
- `Pagination.jsx` + `.css`: page navigation for lists (10 items per page)
|
||||
- `ScrollToTop.jsx`: scroll restoration on route change
|
||||
- `SEOHead.jsx`: meta tags per page
|
||||
- _Requirements: 13.1, 13.2, 13.3, 8.3_
|
||||
|
||||
- [ ]* 4.5 Write property tests for content filtering (Property 7)
|
||||
- **Property 7: Content filtering returns only matching items**
|
||||
- Generate arbitrary content sets with random tags and arbitrary filter selections
|
||||
- Assert: filtered result contains exactly those items whose tags include the selected criterion
|
||||
- **Validates: Requirements 13.1, 13.2**
|
||||
|
||||
- [ ]* 4.6 Write property tests for pagination (Property 12)
|
||||
- **Property 12: Pagination constraint**
|
||||
- Generate arbitrary lists of items (length 0–100)
|
||||
- Assert: each page contains at most 10 items; pagination controls present when items > 10
|
||||
- **Validates: Requirements 8.3**
|
||||
|
||||
- [x] 5. App shell and routing
|
||||
- [x] 5.1 Implement App.jsx with react-router-dom v7 routing
|
||||
- Set up `App.jsx` with BackgroundPattern, Navigation, Routes, Footer, BottomDock
|
||||
- Configure routes: `/`, `/kniepunkt`, `/kniepunkt/:slug`, `/podcast`, `/speaking`, `/consulting`, `/resources`, `/kontakt`, `/impressum`, `/datenschutz`
|
||||
- Add 404 fallback route with friendly error page
|
||||
- Implement `main.jsx` entry point with BrowserRouter
|
||||
- _Requirements: 3.4, 18.4_
|
||||
|
||||
- [x] 5.2 Implement custom hooks (useContent, useFormSubmit)
|
||||
- useContent, useFormSubmit, validation.js, formatDate.js all present
|
||||
- `src/hooks/useContent.js`: content loading, filtering by tag, sorting, pagination
|
||||
- `src/hooks/useFormSubmit.js`: form submission with validation, error handling, loading state
|
||||
- `src/utils/validation.js`: email format validation, field presence checks, length constraints
|
||||
- `src/utils/formatDate.js`: German locale date formatting
|
||||
- _Requirements: 13.1, 13.2, 4.4, 11.5, 12.5_
|
||||
|
||||
- [x] 6. Page implementations — Home and content pages
|
||||
- [x] 6.1 Implement HomePage with Hero, Stats, ContentHighlights, AudienceSection, CTASection
|
||||
- Home includes Hero, StatsCube, photo-band, Mein-Ansatz (positioning), Audiences, LogoMarquee, Testimonials, UpcomingEvents
|
||||
- `Hero.jsx`: stakeholder name, professional photo, summary (≤150 words), all roles listed
|
||||
- `Stats.jsx`: key facts from stats.yaml in animated counters
|
||||
- `AudienceSection.jsx`: explicitly name target audiences (mid-sized companies, HR, OD, IT leaders, managing directors)
|
||||
- `ContentHighlights.jsx`: latest content from each area
|
||||
- `CTASection.jsx`: call-to-action for contact/speaking
|
||||
- Profile visible without scrolling on 1280×720 viewport
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 2.1, 2.3, 2.4, 17.3, 17.4, 17.5_
|
||||
|
||||
- [x] 6.2 Implement KniepunktPage and KniepunktIssuePage
|
||||
- `Kniepunkt.jsx`: distinct labeled section, list of all newsletter issues (title + date), sorted newest first, with ContentFilter
|
||||
- `KniepunktIssue.jsx`: full rendered markdown content of selected issue
|
||||
- Visually separated from other homepage content
|
||||
- _Requirements: 7.1, 7.2, 7.3, 7.4_
|
||||
|
||||
- [x] 6.3 Implement PodcastPage
|
||||
- 12 episodes with Spotify/Apple/YouTube/Castro links
|
||||
- `Podcast.jsx`: visually distinct section with podcast name, description, episode listing
|
||||
- Episodes: title, date, summary, sorted reverse-chronological
|
||||
- Max 10 episodes per page with Pagination component
|
||||
- Each episode links to audio on hosting platform or embedded player
|
||||
- Empty state message when no episodes available
|
||||
- _Requirements: 8.1, 8.2, 8.3, 8.4, 8.5_
|
||||
|
||||
- [x] 6.4 Implement SpeakingPage with talk list and talk request form
|
||||
- `Speaking.jsx`: list of talks with topic title, description, format (keynote/workshop/panel), past events
|
||||
- Show at least 3 speaking examples with topic title, event name, and format
|
||||
- Include TalkRequestForm component (implemented in task 7)
|
||||
- Hide section if no talks available
|
||||
- _Requirements: 2.2, 5.1, 5.2, 5.3, 5.4_
|
||||
|
||||
- [x] 6.5 Implement ConsultingPage
|
||||
- ServiceAccordion + 3 consulting examples (OPEN: more examples per MATERIAL-LISTE)
|
||||
- `Consulting.jsx`: BentoGrid of ExampleCards (3–10 examples)
|
||||
- Each card: title, industry tag, org-size category (50–5000 employees), problem domain, approach, outcomes
|
||||
- No client names, revenue figures, or internal identifiers exposed
|
||||
- Informational message if no examples available
|
||||
- _Requirements: 6.1, 6.2, 6.3, 6.4_
|
||||
|
||||
- [x] 6.6 Implement ResourcesPage with lead capture
|
||||
- OPEN: no resource content files yet (content/resources/ empty)
|
||||
- `Resources.jsx`: list of resources with title, short description (≤200 chars), download indicator
|
||||
- `LeadCaptureForm.jsx`: modal form requiring name (max 100), email (validated), company (max 150)
|
||||
- Display required fields and purpose before submission
|
||||
- Inline validation errors with data retention on failure
|
||||
- _Requirements: 4.1, 4.2, 4.3, 4.4, 4.6_
|
||||
|
||||
- [x] 6.7 Implement ContactPage, Impressum, and Datenschutz
|
||||
- OPEN: Impressum/Datenschutz contain placeholder legal data (needs real address, USt-IdNr)
|
||||
- `Contact.jsx`: contact form with name, email, message (max 2000 chars), validation errors per field
|
||||
- `Impressum.jsx`: static legal content
|
||||
- `Datenschutz.jsx`: static privacy policy content
|
||||
- _Requirements: 11.1, 11.5_
|
||||
|
||||
- [ ] 6.8 Implement Events display (within relevant pages or as section)
|
||||
- UpcomingEvents component exists, but content/events/ is empty — OPEN: add event content
|
||||
- Display events grouped by year, upcoming before past, each group sorted by date descending
|
||||
- Each event: title, date (ISO 8601), location, event type, description (≤300 chars)
|
||||
- _Requirements: 9.1, 9.2, 9.3_
|
||||
|
||||
- [ ]* 6.9 Write property tests for content rendering completeness (Property 1)
|
||||
- **Property 1: Content rendering completeness**
|
||||
- Generate arbitrary content objects per type with all required fields
|
||||
- Assert: rendered output contains all required fields for that type
|
||||
- **Validates: Requirements 3.1, 3.2, 3.5, 4.1, 5.2, 6.1, 6.3, 7.2, 8.2, 8.4**
|
||||
|
||||
- [ ]* 6.10 Write property tests for event chronological grouping (Property 9)
|
||||
- **Property 9: Event chronological grouping**
|
||||
- Generate arbitrary event sets with dates spanning past and future
|
||||
- Assert: upcoming events appear before past events; each group sorted by date descending; grouped by year
|
||||
- **Validates: Requirements 9.2**
|
||||
|
||||
- [ ] 7. Checkpoint — Frontend pages complete
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
- [x] 8. Express 5 backend — form handling and confirmation flow
|
||||
- [x] 8.1 Set up Express 5 server with middleware
|
||||
- `server/index.js`: Express 5 entry point with JSON body parsing, CORS, error middleware
|
||||
- `server/middleware/rateLimiter.js`: rate limiting per endpoint (contact: 3/h, talk: 3/h, newsletter: 5/h, resource: 10/h)
|
||||
- `server/middleware/validator.js`: input validation middleware (field presence, email format, length constraints)
|
||||
- Health check endpoint: `GET /api/health`
|
||||
- _Requirements: 11.1, 12.1_
|
||||
|
||||
- [x] 8.2 Implement confirmation token service
|
||||
- `server/services/confirmationToken.js`: generate unique tokens (crypto.randomUUID), store with expiration, verify, cleanup expired
|
||||
- Token storage in JSON files under `server/data/`
|
||||
- Contact tokens expire after 24h, talk request and newsletter tokens after 48h
|
||||
- Scheduled cleanup interval (every hour) for expired tokens
|
||||
- _Requirements: 10.3, 10.4, 11.2, 11.4, 12.2, 12.4_
|
||||
|
||||
- [x] 8.3 Implement mailer service with nodemailer
|
||||
- `server/services/mailer.js`: nodemailer configuration from environment variables
|
||||
- Email templates: confirmation email (with unique link), stakeholder notification email
|
||||
- Send exactly one confirmation email per valid submission
|
||||
- _Requirements: 10.3, 11.2, 12.2_
|
||||
|
||||
- [x] 8.4 Implement contact form route with double opt-in
|
||||
- `server/routes/contact.js`: POST `/api/contact` — validate, store pending, send confirmation email
|
||||
- POST `/api/contact/confirm/:token` — verify token, forward message to stakeholder, mark confirmed
|
||||
- Reject expired/invalid tokens with appropriate status codes (404/410)
|
||||
- _Requirements: 11.1, 11.2, 11.3, 11.4, 11.5_
|
||||
|
||||
- [x] 8.5 Implement talk request route with double opt-in
|
||||
- `server/routes/talk-request.js`: POST `/api/talk-request` — validate (name, email, event_name, topic, message), store pending, send confirmation
|
||||
- POST `/api/talk-request/confirm/:token` — verify token (48h), forward to stakeholder
|
||||
- _Requirements: 12.1, 12.2, 12.3, 12.4, 12.5_
|
||||
|
||||
- [x] 8.6 Implement newsletter signup route with double opt-in
|
||||
- `server/routes/newsletter.js`: POST `/api/newsletter` — validate email, check for existing subscription, store pending, send confirmation
|
||||
- GET `/api/newsletter/confirm/:token` — verify token (48h), activate subscription, create Sales_Funnel entry
|
||||
- Detect and reject duplicate subscriptions with informational message
|
||||
- Discard unconfirmed subscriptions after 48h
|
||||
- _Requirements: 10.1, 10.2, 10.3, 10.4, 10.5, 10.6_
|
||||
|
||||
- [x] 8.7 Implement resource download route with lead capture
|
||||
- `server/routes/resource-download.js`: POST `/api/resource-download` — validate (name, email, company, resource_id), store lead record with timestamp, return download URL
|
||||
- Lead record must be retrievable for Sales_Funnel development
|
||||
- _Requirements: 4.2, 4.3, 4.5_
|
||||
|
||||
- [ ]* 8.8 Write property tests for form validation (Property 3)
|
||||
- **Property 3: Form validation rejects invalid input**
|
||||
- Generate arbitrary form payloads with random missing/invalid fields
|
||||
- Assert: invalid submissions are rejected with field-level errors; valid data retained
|
||||
- **Validates: Requirements 4.4, 10.5, 11.5, 12.5**
|
||||
|
||||
- [ ]* 8.9 Write property tests for token expiration (Property 4)
|
||||
- **Property 4: Confirmation token expiration**
|
||||
- Generate tokens with arbitrary creation times and verify against expiration windows
|
||||
- Assert: expired tokens are rejected; associated records discarded
|
||||
- **Validates: Requirements 10.4, 11.4, 12.4**
|
||||
|
||||
- [ ]* 8.10 Write property tests for valid submission triggering confirmation (Property 5)
|
||||
- **Property 5: Valid submission triggers confirmation email**
|
||||
- Generate arbitrary valid form submissions (all fields present, valid email)
|
||||
- Assert: unique token generated, pending request stored, exactly one confirmation email sent
|
||||
- **Validates: Requirements 10.3, 11.2, 12.2**
|
||||
|
||||
- [ ]* 8.11 Write property tests for valid token confirmation (Property 6)
|
||||
- **Property 6: Valid token confirmation forwards to stakeholder**
|
||||
- Generate valid unexpired tokens with associated pending records
|
||||
- Assert: confirmation results in forwarding to stakeholder and record marked confirmed
|
||||
- **Validates: Requirements 11.3, 12.3**
|
||||
|
||||
- [ ]* 8.12 Write property tests for lead data storage (Property 11)
|
||||
- **Property 11: Lead data storage integrity**
|
||||
- Generate arbitrary valid lead capture submissions
|
||||
- Assert: stored record contains all submitted fields + resource_id + timestamp; record is retrievable
|
||||
- **Validates: Requirements 4.5**
|
||||
|
||||
- [ ]* 8.13 Write property tests for duplicate subscription detection (Property 13)
|
||||
- **Property 13: Duplicate subscription detection**
|
||||
- Generate arbitrary email addresses, some already in active subscriptions
|
||||
- Assert: duplicate emails are rejected with informational message; no duplicate entry created
|
||||
- **Validates: Requirements 10.6**
|
||||
|
||||
- [ ] 9. Checkpoint — Backend API complete
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
- [ ] 10. Integration and wiring
|
||||
- [x] 10.1 Wire frontend forms to backend API endpoints
|
||||
- useFormSubmit posts to /api endpoints with success/error handling
|
||||
- Connect ContactForm to POST `/api/contact`
|
||||
- Connect TalkRequestForm to POST `/api/talk-request`
|
||||
- Connect NewsletterForm to POST `/api/newsletter`
|
||||
- Connect LeadCaptureForm to POST `/api/resource-download`
|
||||
- Handle success/error responses with appropriate UI feedback (toast notifications, success messages)
|
||||
- _Requirements: 10.1, 11.1, 12.1, 4.2_
|
||||
|
||||
- [ ] 10.2 Create confirmation landing pages
|
||||
- OPEN: inline success messages exist, but dedicated email-confirmation landing pages / token-confirm routes in the SPA are not yet built
|
||||
- Success page shown after email confirmation (contact, talk request)
|
||||
- Newsletter confirmation success page
|
||||
- Expired/invalid token error page
|
||||
- _Requirements: 11.2, 11.3, 12.2, 12.3_
|
||||
|
||||
- [ ]* 10.3 Write integration tests for form submission flows
|
||||
- Test contact form: submit → pending stored → confirmation email triggered
|
||||
- Test talk request: submit → pending stored → confirmation email triggered
|
||||
- Test newsletter: submit → pending stored → double opt-in email triggered
|
||||
- Test resource download: submit lead data → lead stored → download URL returned
|
||||
- _Requirements: 10.2, 10.3, 11.2, 11.3, 12.2, 12.3, 4.5_
|
||||
|
||||
- [ ] 11. Deployment configuration
|
||||
- [x] 11.1 Create Dockerfile and docker-compose.yml for backend
|
||||
- Dockerfile: Node.js image, install dependencies, run Express server on port 3003
|
||||
- docker-compose.yml: single `backend` service, volume mount for `server/data/`, env file reference
|
||||
- _Requirements: 18.1_
|
||||
|
||||
- [x] 11.2 Create Caddyfile for production serving
|
||||
- Auto HTTPS via Let's Encrypt for andreknie.de
|
||||
- Reverse proxy `/api/*` to Express backend on port 3003
|
||||
- Serve static SPA files with `try_files {path} /index.html`
|
||||
- Security headers (HSTS, X-Content-Type-Options, CSP)
|
||||
- Gzip/zstd compression
|
||||
- Immutable cache for `/assets/*`
|
||||
- _Requirements: 18.4_
|
||||
|
||||
- [x] 11.3 Configure Vite build with content plugin integration
|
||||
- vite-plugin-content registered; `npm run build` produces complete dist/ (verified)
|
||||
- Register `vite-plugin-content` in `vite.config.js`
|
||||
- Verify `npm run build` produces complete `dist/` with all content embedded
|
||||
- Ensure build output is deployable as static files to `/opt/andreknie/site/`
|
||||
- _Requirements: 14.2_
|
||||
|
||||
- [ ] 12. Final checkpoint — Full integration verified
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
## Notes
|
||||
|
||||
- Tasks marked with `*` are optional and can be skipped for faster MVP
|
||||
- Each task references specific requirements for traceability
|
||||
- Checkpoints ensure incremental validation
|
||||
- Property tests validate universal correctness properties from the design document (13 properties total)
|
||||
- Unit tests validate specific examples and edge cases
|
||||
- The project is created as a NEW repository — not inside the current CV workspace
|
||||
- Use `reference/Projekt-KIQ-HP/` as visual pattern reference (glass morphism, animated background, bottom dock, bento grid)
|
||||
- Colors are placeholder (neutral blue #4a9eff) — easily changeable via CSS custom properties in `:root`
|
||||
- Content files in `content/` ARE the CMS — no database needed for content
|
||||
- Backend only handles forms (contact, talk request, newsletter, resource download)
|
||||
- All forms use double opt-in (confirmation email with token)
|
||||
- fast-check is the property-based testing library; Vitest is the test runner
|
||||
|
||||
## Task Dependency Graph
|
||||
|
||||
```json
|
||||
{
|
||||
"waves": [
|
||||
{ "id": 0, "tasks": ["1.1", "1.3"] },
|
||||
{ "id": 1, "tasks": ["1.2", "1.4"] },
|
||||
{ "id": 2, "tasks": ["2.1"] },
|
||||
{ "id": 3, "tasks": ["2.2", "2.3", "2.4", "2.5"] },
|
||||
{ "id": 4, "tasks": ["4.1", "4.2", "4.3", "4.4", "5.1"] },
|
||||
{ "id": 5, "tasks": ["4.5", "4.6", "5.2"] },
|
||||
{ "id": 6, "tasks": ["6.1", "6.2", "6.3", "6.4", "6.5", "6.6", "6.7", "6.8"] },
|
||||
{ "id": 7, "tasks": ["6.9", "6.10", "8.1"] },
|
||||
{ "id": 8, "tasks": ["8.2", "8.3"] },
|
||||
{ "id": 9, "tasks": ["8.4", "8.5", "8.6", "8.7"] },
|
||||
{ "id": 10, "tasks": ["8.8", "8.9", "8.10", "8.11", "8.12", "8.13"] },
|
||||
{ "id": 11, "tasks": ["10.1", "10.2"] },
|
||||
{ "id": 12, "tasks": ["10.3", "11.1", "11.2", "11.3"] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Offene Themen (Stand-Abgleich)
|
||||
|
||||
Die Kernimplementierung (Frontend-Seiten, Backend-Formulare mit Double-Opt-In, Content-Pipeline, Deployment-Konfiguration) steht und baut fehlerfrei. Offen sind im Wesentlichen Inhalte, Verifikation und optionale Tests.
|
||||
|
||||
### Inhalte / Redaktion (blockiert Go-Live)
|
||||
- [x] Impressum mit echten Daten füllen — Telefon entfernt, E-Mail kontakt@d-hive.de (Task 6.7)
|
||||
- [ ] Datenschutzerklärung final juristisch prüfen — Inhalt vorhanden, Telefon entfernt, datenschutz@d-hive.de für Betroffenenrechte (Task 6.7)
|
||||
- [ ] Weitere Consulting-Beispiele (Ziel 5+, aktuell 3) inkl. Leistungsübersicht & Ergebnis-Zahlen (Task 6.5, MATERIAL-LISTE) — *in Arbeit beim Stakeholder*
|
||||
- [ ] Testimonials final freigeben (3 Entwürfe vorhanden, Freigabe der Personen ausstehend) — *in Arbeit beim Stakeholder*
|
||||
|
||||
### Nice to have (Inhalte, kein Go-Live-Blocker)
|
||||
- [ ] Event-Inhalte ergänzen — `content/events/` ist leer (Task 6.8)
|
||||
- [ ] Resource-Inhalte ergänzen — `content/resources/` ist leer (Task 6.6)
|
||||
|
||||
### Funktionale Lücken
|
||||
- [ ] Dedizierte ContentFilter-, Pagination- und SEOHead-Komponenten (Task 4.4)
|
||||
- [ ] Confirmation-Landingpages im SPA (Token-Confirm-Routen, Erfolg/Abgelaufen) — bisher nur Inline-Erfolgsmeldungen (Task 10.2)
|
||||
|
||||
### Verifikation & Deployment
|
||||
- [ ] Backend lokal end-to-end testen (Formular → Bestätigungsmail → Weiterleitung); SMTP-Konfiguration in `.env` erforderlich
|
||||
- [ ] Produktiv-Deployment durchführen (dist/ auf Server, Docker-Backend starten, Caddy)
|
||||
|
||||
### Optionale Property-/Integrationstests (mit `*` markiert, MVP-übersprungen)
|
||||
- [ ] 2.3, 2.4, 2.5 — Content-Validierung, Sortierung, Filter-Ableitung
|
||||
- [ ] 4.5, 4.6 — Filterung, Pagination
|
||||
- [ ] 6.9, 6.10 — Rendering-Vollständigkeit, Event-Gruppierung
|
||||
- [ ] 8.8–8.13 — Formularvalidierung, Token-Ablauf, Bestätigungsmail, Lead-Speicherung, Duplikat-Erkennung
|
||||
- [ ] 10.3 — Integrationstests der Formular-Flows
|
||||
|
||||
### Material (siehe `andreknie.de/MATERIAL-LISTE.md`)
|
||||
- [ ] Professionelles Hero-/Profilbild (freigestellt) — aktuell Platzhalter `andre-knie.jpg`
|
||||
- [ ] Open-Graph-Image, Favicon
|
||||
- [ ] Optional: Hero-Video, Testimonial-Videos, Podcast-Cover
|
||||
@@ -1,47 +0,0 @@
|
||||
---
|
||||
inclusion: auto
|
||||
---
|
||||
|
||||
# CV Project Standards
|
||||
|
||||
## Quality Gates (before every commit)
|
||||
|
||||
1. `gitleaks detect --source . --no-git` — no secrets in code
|
||||
2. `npm run typecheck` — TypeScript passes
|
||||
3. `npm test` — all vitest tests pass (252 tests, 80%+ coverage)
|
||||
4. `npm audit` — 0 vulnerabilities
|
||||
|
||||
## Conventional Commits
|
||||
|
||||
```
|
||||
feat(cv): add tandem CV template
|
||||
fix(graph): handle circular references in index
|
||||
test(interview): add property test for gap detection
|
||||
docs(readme): update entity schema section
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
- Vitest + fast-check for property-based testing
|
||||
- 15 formal correctness properties defined in SPEC.md
|
||||
- Given-When-Then structure
|
||||
- 80% coverage minimum
|
||||
|
||||
## Autonomous Mode
|
||||
|
||||
- Never push directly to main — use feature branches
|
||||
- Create PRs on GitHub targeting main
|
||||
- If stuck after 3 attempts: stop and document
|
||||
- Report: `PROGRESS: X%` / `HELP: description`
|
||||
|
||||
## Architecture
|
||||
|
||||
- File-based knowledge graph (YAML entities, one file per entity)
|
||||
- TypeScript for validation and logic
|
||||
- Kiro skills for workflows (interview, cv-generate, talk-intro, kb-review)
|
||||
- No build step, no server — file system is the database
|
||||
|
||||
## File Naming
|
||||
|
||||
- Entity files: kebab-case (`andre-knie.yaml`, `db-cargo-innovation-lead.yaml`)
|
||||
- All filenames OneDrive-safe (no brackets, no special chars)
|
||||
Reference in New Issue
Block a user