feat: Add Directus setup scripts for collections, fields, and relations

- Created setup-directus-collections.js to automate the creation of tech stack collections, fields, and relations in Directus.
- Created setup-directus-hobbies.js for setting up hobbies collection with translations.
- Created setup-directus-projects.js for establishing projects collection with comprehensive fields and translations.
- Added setup-tech-stack-directus.js to populate tech_stack_items with predefined data.
This commit is contained in:
2026-01-23 02:53:31 +01:00
parent 7604e00e0f
commit e431ff50fc
28 changed files with 5253 additions and 23 deletions

View File

@@ -0,0 +1,253 @@
# Directus Collections Struktur - Vollständige Portfolio Integration
## 🎯 Übersicht
Diese Struktur bildet **alles** aus deinem Portfolio in Directus ab, ohne Features zu verlieren.
## 📦 Collections
### 1. **tech_stack_categories** (Tech Stack Kategorien)
**Felder:**
- `id` - UUID (Primary Key)
- `key` - String (unique) - z.B. "frontend", "backend"
- `icon` - String - Icon-Name (z.B. "Globe", "Server")
- `sort` - Integer - Reihenfolge der Anzeige
- `status` - String (draft/published/archived)
- `translations` - O2M zu `tech_stack_categories_translations`
**Translations (`tech_stack_categories_translations`):**
- `id` - UUID
- `tech_stack_categories_id` - M2O zu `tech_stack_categories`
- `languages_code` - M2O zu `languages` (de-DE, en-US)
- `name` - String - z.B. "Frontend & Mobile"
---
### 2. **tech_stack_items** (Tech Stack Items)
**Felder:**
- `id` - UUID (Primary Key)
- `category_id` - M2O zu `tech_stack_categories`
- `name` - String - z.B. "Next.js", "Docker", "Tailwind CSS"
- `sort` - Integer - Reihenfolge innerhalb der Kategorie
- `url` - String (optional) - Link zur Technologie-Website
- `icon_url` - String (optional) - Custom Icon/Logo URL
**Keine Translations nötig** - Technologie-Namen bleiben gleich in allen Sprachen
---
### 3. **projects** (Projekte - Vollständig)
**Felder:**
- `id` - UUID (Primary Key)
- `slug` - String (unique) - URL-freundlicher Identifier
- `status` - String (draft/published/archived)
- `featured` - Boolean - Hervorgehobenes Projekt
- `category` - String - z.B. "Web Application", "Mobile App"
- `date` - String - Projektzeitraum (z.B. "2024", "2023-2024")
- `github` - String (optional) - GitHub Repository URL
- `live` - String (optional) - Live Demo URL
- `image_url` - String (optional) - Hauptbild des Projekts
- `demo_video` - String (optional) - Video URL
- `screenshots` - JSON - Array von Screenshot-URLs
- `color_scheme` - String - Farbschema des Projekts
- `accessibility` - Boolean - Barrierefreiheit vorhanden
- `difficulty` - String (Beginner/Intermediate/Advanced/Expert)
- `time_to_complete` - String - z.B. "4-6 weeks"
- `technologies` - JSON - Array von Technologien
- `challenges` - JSON - Array von Herausforderungen
- `lessons_learned` - JSON - Array von Learnings
- `future_improvements` - JSON - Array von geplanten Verbesserungen
- `performance` - JSON - `{"lighthouse": 90, "bundleSize": "50KB", "loadTime": "1.5s"}`
- `analytics` - JSON - `{"views": 0, "likes": 0, "shares": 0}` (read-only, kommt aus PostgreSQL)
- `sort` - Integer
- `date_created` - DateTime
- `date_updated` - DateTime
- `translations` - O2M zu `projects_translations`
**Translations (`projects_translations`):**
- `id` - UUID
- `projects_id` - M2O zu `projects`
- `languages_code` - M2O zu `languages`
- `title` - String - Projekttitel
- `description` - Text - Kurzbeschreibung
- `content` - WYSIWYG/Markdown - Vollständiger Projektinhalt
- `meta_description` - String - SEO Meta-Description
- `keywords` - String - SEO Keywords
- `og_image` - String - Open Graph Image URL
---
### 4. **content_pages** (Bereits vorhanden, erweitern)
**Aktuell:**
- Für statische Inhalte wie "home-about", "privacy-policy", etc.
**Erweitern um:**
- `key` - Eindeutiger Identifier
- `page_type` - String (home_section/legal/about/custom)
- `status` - draft/published
- `translations` - O2M zu `content_pages_translations`
---
### 5. **hobbies** (NEU - für "When I'm Not Coding")
**Felder:**
- `id` - UUID
- `key` - String (unique) - z.B. "self_hosting", "gaming"
- `icon` - String - Icon-Name
- `sort` - Integer
- `status` - String
- `translations` - O2M zu `hobbies_translations`
**Translations:**
- `id` - UUID
- `hobbies_id` - M2O zu `hobbies`
- `languages_code` - M2O zu `languages`
- `title` - String - z.B. "Self-Hosting & DevOps"
- `description` - Text - Beschreibung des Hobbys
---
### 6. **messages** (Bereits vorhanden via Directus Native Translations)
**Struktur:**
- Collection: `messages`
- Felder:
- `key` - String - z.B. "nav.home", "common.loading"
- `translations` - Native Directus Translations
- `value` - String - Übersetzter Text
---
## 🔄 Datenfluss
### Aktuell (Hybrid):
```
PostgreSQL (Projects, Analytics) ←→ Next.js ←→ Messages (JSON Files)
Directus (Content Pages)
```
### Nach Migration (Unified):
```
Directus (Projects, Tech Stack, Content, Messages, Hobbies)
GraphQL API
Next.js (mit Fallback Cache)
PostgreSQL (nur für Analytics: PageViews, UserInteractions)
```
---
## 📊 Was bleibt in PostgreSQL?
**Nur echte Analytics-Daten:**
- `PageView` - Seitenaufrufe
- `UserInteraction` - Likes, Shares, Bookmarks
- `Contact` - Kontaktformular-Einträge
- `ActivityStatus` - Live-Status (Coding, Gaming, Music)
**Warum?**
- Hohe Frequenz von Updates
- Komplexe Aggregations-Queries
- Privacy/GDPR (keine Content-vermischung)
---
## 🎨 Directus UI Benefits
### Was du gewinnst:
1.**WYSIWYG Editor** für Projekt-Content
2.**Media Library** für Bilder/Screenshots
3.**Bulk Operations** (mehrere Projekte gleichzeitig bearbeiten)
4.**Revision History** (Änderungen nachverfolgen)
5.**Workflows** (Draft → Review → Publish)
6.**Access Control** (verschiedene User-Rollen)
7.**REST + GraphQL API** automatisch generiert
8.**Real-time Updates** via WebSockets
---
## 🚀 Migration Plan
### Phase 1: Tech Stack
1. Collections erstellen in Directus
2. Daten aus `messages/en.json` & `messages/de.json` migrieren
3. `About.tsx` auf Directus umstellen
### Phase 2: Hobbies
1. Collection erstellen
2. Daten migrieren
3. `About.tsx` erweitern
### Phase 3: Projects
1. Collection mit allen Feldern erstellen
2. Migration-Script: PostgreSQL → Directus
3. API Routes anpassen (oder Directus direkt nutzen)
4. `/manage` Dashboard optional behalten oder durch Directus ersetzen
### Phase 4: Messages (Optional)
1. Alle keys aus `messages/*.json` nach Directus
2. `next-intl` Config anpassen für Directus-Loader
3. JSON-Files als Fallback behalten
---
## 💾 Migration Scripts
Ich erstelle dir:
1. `scripts/migrate-to-directus.ts` - Automatische Migration
2. `scripts/sync-from-directus.ts` - Backup zurück zu PostgreSQL
3. `lib/directus-extended.ts` - Alle GraphQL Queries
---
## ⚡ Performance
**Caching-Strategie:**
```typescript
// 1. Versuch: Directus laden
// 2. Fallback: Redis Cache (5min TTL)
// 3. Fallback: Static JSON Files
// 4. Fallback: Hardcoded Defaults
```
**ISR (Incremental Static Regeneration):**
- Projects: Revalidate alle 5 Minuten
- Tech Stack: Revalidate alle 1 Stunde
- Content Pages: On-Demand Revalidation via Webhook
---
## 🔐 Security
**Directus Access:**
- Public Read (via Token) für Frontend
- Admin Write (via Admin Panel)
- Role-based für verschiedene Content-Types
**Was public bleibt:**
- Published Projects
- Published Content Pages
- Tech Stack
- Messages
**Was protected bleibt:**
- Drafts
- Analytics
- Admin Settings
---
## 📝 Nächste Schritte
Sag mir einfach:
1. **"Erstell mir die Collections"** → Ich generiere JSON zum Import in Directus
2. **"Bau die Migration"** → Ich schreibe Scripts zum Daten übertragen
3. **"Update den Code"** → Ich passe alle Components & APIs an

View File

@@ -0,0 +1,118 @@
# Directus Integration Status
## ✅ Vollständig integriert
### Tech Stack
- **Collection**: `tech_stack_categories` + `tech_stack_items`
- **Data Migration**: 4 Kategorien, ~16 Items (EN + DE) ✅
- **API**: `/api/tech-stack`
- **Component**: `About.tsx` lädt aus Directus mit Fallback ✅
- **Status**: ✅ **PRODUCTION READY**
### Hobbies
- **Collection**: `hobbies`
- **Data Migration**: 4 Hobbies (EN + DE) ✅
- **API**: `/api/hobbies`
- **Component**: `About.tsx` lädt aus Directus mit Fallback ✅
- **Status**: ✅ **PRODUCTION READY**
### Content Pages
- **Collection**: Bereits existierend ✅
- **Data**: Home-About Page ✅
- **API**: `/api/content/page`
- **Component**: `About.tsx` lädt aus Directus ✅
- **Status**: ✅ **PRODUCTION READY**
---
## ⚠️ Teilweise integriert
### Projects
- **Collection**: `projects` ✅ (30+ Felder mit Translations)
- **Data Migration**: Script vorhanden, PostgreSQL benötigt ⚠️
- **API**: `/api/projects` mit **Hybrid-System**
- Primär: PostgreSQL (wenn verfügbar)
- Fallback: Directus (wenn PostgreSQL offline)
- Response enthält `source` field (`postgresql`, `directus`, `directus-empty`, `error`)
- **Components**: Verwenden weiterhin `/api/projects`
- `Projects.tsx`
- `ProjectsPageClient.tsx`
- `ProjectCard.tsx`
- Admin: `ProjectManager.tsx`
- **Status**: ⚠️ **HYBRID MODE** - Funktioniert mit beiden Datenquellen
**Migration durchführen:**
```bash
# 1. PostgreSQL starten
docker-compose up -d postgres
# 2. Migration ausführen
node scripts/migrate-projects-to-directus.js
# 3. Optional: PostgreSQL deaktivieren
# → /api/projects nutzt automatisch Directus
```
---
## 📊 Verwendung nach Quelle
| Content | Source | Load Location |
|---------|--------|---------------|
| Tech Stack | Directus | `About.tsx` via `/api/tech-stack` |
| Hobbies | Directus | `About.tsx` via `/api/hobbies` |
| Projects | PostgreSQL → Directus Fallback | `Projects.tsx` via `/api/projects` |
| Content Pages | Directus | `About.tsx` via `/api/content/page` |
| Messages/i18n | `messages/*.json` | next-intl loader |
| Analytics | PostgreSQL | Admin Dashboard |
| Users/Auth | PostgreSQL | Admin System |
---
## 🔄 Hybrid System für Projects
Die `/api/projects` Route nutzt ein intelligentes Fallback-System:
1. **PostgreSQL prüfen** via `prisma.$queryRaw`
2. **Bei Erfolg**: Daten aus PostgreSQL laden (`source: 'postgresql'`)
3. **Bei Fehler**: Automatisch zu Directus wechseln (`source: 'directus'`)
4. **Bei beiden offline**: Error Response (`source: 'error'`, Status 503)
**Vorteile:**
- ✅ Zero Downtime bei DB-Migration
- ✅ Lokale Entwicklung ohne PostgreSQL möglich
- ✅ Bestehende Components funktionieren unverändert
- ✅ Graduelle Migration möglich
---
## 🎯 Nächste Schritte
### Option 1: Vollständige Directus-Migration
```bash
# Projects nach Directus migrieren
node scripts/migrate-projects-to-directus.js
# PostgreSQL optional deaktivieren
# → /api/projects nutzt automatisch Directus
```
### Option 2: Hybrid-Betrieb
```bash
# Nichts tun - System funktioniert bereits!
# PostgreSQL = Primary, Directus = Fallback
```
---
## 📝 Zusammenfassung
| Status | Count | Components |
|--------|-------|------------|
| ✅ Vollständig in Directus | 3 | Tech Stack, Hobbies, Content Pages |
| ⚠️ Hybrid (PostgreSQL + Directus) | 1 | Projects |
| ❌ Noch in JSON | 1 | Messages (next-intl) |
**Ergebnis**: Fast alle User-sichtbaren Inhalte sind bereits über Directus editierbar! 🎉
**Einzige Ausnahme**: System-Messages (`messages/en.json`, `messages/de.json`) für UI-Texte wie Buttons, Labels, etc.

View File

@@ -0,0 +1,410 @@
# 🎛️ Dynamic Activity System - Custom Fields ohne Deployment
## 🚀 Problem gelöst
**Vorher:**
- Neue Activity = Schema-Änderung + Code-Update + Deployment
- Hardcoded fields wie `reading_book`, `working_out_activity`, etc.
**Jetzt:**
- Neue Activity = Nur n8n Workflow anpassen
- JSON field `custom_activities` für alles
- ✅ Zero Downtime
- ✅ Kein Deployment nötig
---
## 📊 Schema
```sql
ALTER TABLE activity_status
ADD COLUMN custom_activities JSONB DEFAULT '{}';
```
**Struktur:**
```json
{
"reading": {
"enabled": true,
"book_title": "Clean Code",
"author": "Robert C. Martin",
"progress": 65,
"platform": "hardcover",
"cover_url": "https://..."
},
"working_out": {
"enabled": true,
"activity": "Running",
"duration_minutes": 45,
"calories": 350
},
"learning": {
"enabled": true,
"course": "Docker Deep Dive",
"platform": "Udemy",
"progress": 23
},
"streaming": {
"enabled": true,
"platform": "Twitch",
"viewers": 42,
"game": "Minecraft"
}
}
```
---
## 🔧 n8n Workflow Beispiel
### Workflow: "Update Custom Activity"
**Node 1: Webhook (POST)**
```
URL: /webhook/custom-activity
Method: POST
Body: {
"type": "reading",
"data": {
"enabled": true,
"book_title": "Clean Code",
"author": "Robert C. Martin",
"progress": 65
}
}
```
**Node 2: Function - Build JSON**
```javascript
const { type, data } = items[0].json;
return [{
json: {
type,
data,
query: `
UPDATE activity_status
SET custom_activities = jsonb_set(
COALESCE(custom_activities, '{}'::jsonb),
'{${type}}',
$1::jsonb
),
updated_at = NOW()
WHERE id = 1
`,
params: [JSON.stringify(data)]
}
}];
```
**Node 3: PostgreSQL**
- Query: `={{$json.query}}`
- Parameters: `={{$json.params}}`
---
## 🎨 Frontend Integration
### TypeScript Interface
```typescript
interface CustomActivity {
enabled: boolean;
[key: string]: any; // Dynamisch!
}
interface StatusData {
// ... existing fields
customActivities?: Record<string, CustomActivity>;
}
```
### API Route Update
```typescript
// app/api/n8n/status/route.ts
export async function GET() {
const statusData = await fetch(n8nWebhookUrl);
return NextResponse.json({
// ... existing fields
customActivities: statusData.custom_activities || {}
});
}
```
### Component Rendering
```tsx
// app/components/ActivityFeed.tsx
{Object.entries(data.customActivities || {}).map(([type, activity]) => {
if (!activity.enabled) return null;
return (
<motion.div key={type} className="custom-activity-card">
<h3>{type.charAt(0).toUpperCase() + type.slice(1)}</h3>
{/* Generic renderer basierend auf Feldern */}
{Object.entries(activity).map(([key, value]) => {
if (key === 'enabled') return null;
return (
<div key={key}>
<span>{key.replace(/_/g, ' ')}: </span>
<strong>{value}</strong>
</div>
);
})}
</motion.div>
);
})}
```
---
## 📱 Beispiele
### 1. Reading Activity (Hardcover Integration)
**n8n Workflow:**
```
Hardcover API → Get Currently Reading → Update Database
```
**Webhook Body:**
```json
{
"type": "reading",
"data": {
"enabled": true,
"book_title": "Clean Architecture",
"author": "Robert C. Martin",
"progress": 45,
"platform": "hardcover",
"cover_url": "https://covers.openlibrary.org/...",
"started_at": "2025-01-20"
}
}
```
**Frontend zeigt:**
```
📖 Reading
Clean Architecture by Robert C. Martin
Progress: 45%
[Progress Bar]
```
---
### 2. Workout Activity (Strava/Apple Health)
**Webhook Body:**
```json
{
"type": "working_out",
"data": {
"enabled": true,
"activity": "Running",
"duration_minutes": 45,
"distance_km": 7.2,
"calories": 350,
"avg_pace": "6:15 /km",
"started_at": "2025-01-23T06:30:00Z"
}
}
```
**Frontend zeigt:**
```
🏃 Working Out
Running - 7.2 km in 45 minutes
350 calories burned
```
---
### 3. Learning Activity (Udemy/Coursera)
**Webhook Body:**
```json
{
"type": "learning",
"data": {
"enabled": true,
"course": "Docker Deep Dive",
"platform": "Udemy",
"instructor": "Nigel Poulton",
"progress": 67,
"time_spent_hours": 8.5
}
}
```
**Frontend zeigt:**
```
🎓 Learning
Docker Deep Dive on Udemy
Progress: 67% (8.5 hours)
```
---
### 4. Live Streaming
**Webhook Body:**
```json
{
"type": "streaming",
"data": {
"enabled": true,
"platform": "Twitch",
"title": "Building a Portfolio with Next.js",
"viewers": 42,
"game": "Software Development",
"url": "https://twitch.tv/yourname"
}
}
```
**Frontend zeigt:**
```
📺 LIVE on Twitch
Building a Portfolio with Next.js
👥 42 viewers
[Watch Stream →]
```
---
## 🔥 Clear Activity
**Webhook zum Deaktivieren:**
```bash
curl -X POST https://n8n.example.com/webhook/custom-activity \
-H "Content-Type: application/json" \
-d '{
"type": "reading",
"data": {
"enabled": false
}
}'
```
**Alle Custom Activities clearen:**
```sql
UPDATE activity_status
SET custom_activities = '{}'::jsonb
WHERE id = 1;
```
---
## 🎯 Vorteile
| Feature | Vorher | Nachher |
|---------|--------|---------|
| **Neue Activity** | Schema + Code + Deploy | Nur n8n Workflow |
| **Activity entfernen** | Schema + Code + Deploy | Webhook mit `enabled: false` |
| **Deployment** | Ja | Nein |
| **Downtime** | Ja | Nein |
| **Flexibilität** | Starr | Komplett dynamisch |
---
## 🚀 Migration
```bash
# 1. Schema erweitern
psql -d portfolio_dev -f prisma/migrations/add_custom_activities.sql
# 2. Prisma Schema updaten
# prisma/schema.prisma
# customActivities Json? @map("custom_activities")
# 3. Prisma Generate
npx prisma generate
# 4. Fertig! Keine weiteren Code-Änderungen nötig
```
---
## 🎨 Smart Renderer Component
```tsx
// components/CustomActivityCard.tsx
interface CustomActivityCardProps {
type: string;
data: Record<string, any>;
}
export function CustomActivityCard({ type, data }: CustomActivityCardProps) {
const icon = getIconForType(type); // Mapping: reading → 📖, working_out → 🏃
const title = type.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
return (
<motion.div className="bg-gradient-to-br from-purple-500/10 to-blue-500/5 rounded-xl p-4">
<div className="flex items-center gap-2 mb-2">
<span className="text-2xl">{icon}</span>
<h3 className="font-bold">{title}</h3>
</div>
{/* Render fields dynamically */}
<div className="space-y-1">
{Object.entries(data).map(([key, value]) => {
if (key === 'enabled') return null;
// Special handling for specific fields
if (key === 'progress' && typeof value === 'number') {
return (
<div key={key}>
<div className="h-2 bg-gray-200 rounded-full overflow-hidden">
<div
className="h-full bg-blue-500 transition-all"
style={{ width: `${value}%` }}
/>
</div>
<span className="text-xs text-gray-500">{value}%</span>
</div>
);
}
// Default: key-value pair
return (
<div key={key} className="text-sm">
<span className="text-gray-500">{formatKey(key)}: </span>
<span className="font-medium">{formatValue(value)}</span>
</div>
);
})}
</div>
</motion.div>
);
}
function getIconForType(type: string): string {
const icons: Record<string, string> = {
reading: '📖',
working_out: '🏃',
learning: '🎓',
streaming: '📺',
cooking: '👨‍🍳',
traveling: '✈️',
};
return icons[type] || '✨';
}
```
---
## 🎯 Zusammenfassung
Mit dem `custom_activities` JSONB Field kannst du:
- ✅ Beliebig viele Activity-Typen hinzufügen
- ✅ Ohne Schema-Änderungen
- ✅ Ohne Code-Deployments
- ✅ Nur über n8n Webhooks steuern
- ✅ Frontend rendert automatisch alles
**Das ist TRUE DYNAMIC! 🚀**

View File

@@ -0,0 +1,229 @@
# 🎨 Dynamisches Activity System - Setup
## ✅ Was jetzt funktioniert:
**Ohne Code-Änderungen kannst du jetzt beliebige Activities hinzufügen!**
### n8n sendet:
```json
{
"status": { "text": "online", "color": "green" },
"music": { ... },
"gaming": { ... },
"coding": { ... },
"customActivities": {
"reading": {
"enabled": true,
"title": "Clean Architecture",
"author": "Robert C. Martin",
"progress": 65,
"coverUrl": "https://..."
},
"working_out": {
"enabled": true,
"activity": "Running",
"duration_minutes": 45,
"distance_km": 7.2,
"calories": 350
},
"learning": {
"enabled": true,
"course": "Docker Deep Dive",
"platform": "Udemy",
"progress": 67
}
}
}
```
### Frontend rendert automatisch:
- ✅ Erkennt alle Activities in `customActivities`
- ✅ Generiert Cards mit passenden Farben
- ✅ Zeigt Icons (📖 🏃 🎓 📺 etc.)
- ✅ Progress Bars für `progress` Felder
- ✅ Bilder für `coverUrl`, `image_url`, `albumArt`
- ✅ Alle zusätzlichen Felder werden gerendert
---
## 🔧 n8n Setup
### 1. Code Node updaten
Ersetze den Code in deinem "Code in JavaScript" Node mit:
`scripts/n8n-workflow-code-updated.js`
### 2. Custom Activity hinzufügen
**Im n8n Code:**
```javascript
// Nach der Coding Logic, vor dem OUTPUT:
customActivities.reading = {
enabled: true,
title: "Clean Code",
author: "Robert C. Martin",
progress: 65,
coverUrl: "https://covers.openlibrary.org/..."
};
// Oder mehrere:
customActivities.working_out = {
enabled: true,
activity: "Running",
duration_minutes: 45
};
```
### 3. Automatische Integration (Hardcover Beispiel)
Bereits im Code enthalten:
```javascript
if (hardcoverData && hardcoverData.user_book) {
const book = hardcoverData.user_book;
customActivities.reading = {
enabled: true,
title: book.book?.title,
author: book.book?.contributions?.[0]?.author?.name,
progress: book.progress_pages && book.book?.pages
? Math.round((book.progress_pages / book.book.pages) * 100)
: undefined,
coverUrl: book.book?.image_url
};
}
```
---
## 🎯 Unterstützte Felder
Das System erkennt automatisch:
| Feld | Verwendung |
|------|------------|
| `enabled` | Zeigt/versteckt die Activity (required!) |
| `title`, `name`, `book_title` | Haupttitel (fett) |
| `author`, `artist`, `platform` | Untertitel |
| `progress` (0-100) | Progress Bar mit Animation |
| `progress_label` | Text neben Progress (default: "complete") |
| `coverUrl`, `image_url`, `albumArt` | Bild/Cover (40x56px) |
| **Alle anderen** | Werden als kleine Text-Zeilen gerendert |
---
## 🌈 Verfügbare Typen & Icons
Vordefinierte Styling:
| Type | Icon | Farben |
|------|------|--------|
| `reading` | 📖 | Amber/Orange |
| `working_out` | 🏃 | Red/Orange |
| `learning` | 🎓 | Purple/Pink |
| `streaming` | 📺 | Violet/Purple |
| `cooking` | 👨‍🍳 | Gray (default) |
| `traveling` | ✈️ | Gray (default) |
| `meditation` | 🧘 | Gray (default) |
| `podcast` | 🎙️ | Gray (default) |
*Alle anderen Typen bekommen Standard-Styling (grau) und ✨ Icon*
---
## 📝 Beispiele
### Reading (mit Cover & Progress)
```javascript
customActivities.reading = {
enabled: true,
title: "Clean Architecture",
author: "Robert C. Martin",
progress: 65,
coverUrl: "https://covers.openlibrary.org/b/id/12345-M.jpg"
};
```
### Workout (mit Details)
```javascript
customActivities.working_out = {
enabled: true,
activity: "Running",
duration_minutes: 45,
distance_km: 7.2,
calories: 350,
avg_pace: "6:15 /km"
};
```
### Learning (mit Progress)
```javascript
customActivities.learning = {
enabled: true,
course: "Docker Deep Dive",
platform: "Udemy",
instructor: "Nigel Poulton",
progress: 67,
time_spent_hours: 8.5
};
```
### Streaming (Live)
```javascript
customActivities.streaming = {
enabled: true,
platform: "Twitch",
title: "Building a Portfolio",
viewers: 42,
url: "https://twitch.tv/yourname"
};
```
### Activity deaktivieren
```javascript
customActivities.reading = {
enabled: false // Verschwindet komplett
};
// Oder einfach nicht hinzufügen
```
---
## 🚀 Testing
**1. n8n Workflow testen:**
```bash
curl https://your-n8n.com/webhook/denshooter-71242/status
```
**2. Response checken:**
```json
{
"customActivities": {
"reading": { "enabled": true, "title": "..." }
}
}
```
**3. Frontend checken:**
- Dev Server: `npm run dev`
- Browser: http://localhost:3000
- Activity Feed sollte automatisch neue Card zeigen
**4. Mehrere Activities gleichzeitig:**
```javascript
customActivities.reading = { enabled: true, ... };
customActivities.learning = { enabled: true, ... };
customActivities.working_out = { enabled: true, ... };
// Alle 3 werden nebeneinander gezeigt (Grid Layout)
```
---
## ✨ Das ist ECHTE Dynamik!
-**Keine Code-Änderungen** - Nur n8n Workflow anpassen
-**Keine Deployments** - Änderungen sofort sichtbar
-**Beliebig erweiterbar** - Neue Activity-Typen jederzeit
-**Zero Downtime** - Alles läuft live
-**Responsive** - Grid passt sich automatisch an
**Genau das was du wolltest!** 🎉

View File

@@ -0,0 +1,165 @@
# 📚 Reading Activity zu n8n hinzufügen
## ✅ Was du bereits hast:
- ✅ Frontend ist bereit (ActivityFeed.tsx updated)
- ✅ TypeScript Interfaces erweitert
- ✅ Grid Layout (horizontal auf Desktop, vertikal auf Mobile)
- ✅ Conditional Rendering (nur zeigen wenn `isReading: true`)
## 🔧 n8n Workflow anpassen
### Option 1: Hardcover Integration (automatisch)
**1. Neuer Node in n8n: "Hardcover"**
```
Type: HTTP Request
Method: GET
URL: https://cms.dk0.dev/api/n8n/hardcover/currently-reading
```
**2. Mit Webhook verbinden**
```
Webhook → Hardcover (parallel zu Spotify/Lanyard)
Merge (Node mit 5 Inputs statt 4)
Code in JavaScript
```
**3. Code Node updaten**
Ersetze den gesamten Code in deinem "Code in JavaScript" Node mit dem Code aus:
`scripts/n8n-workflow-code-updated.js`
---
### Option 2: Manueller Webhook (für Tests)
**Neuer Workflow: "Set Reading Status"**
**Node 1: Webhook (POST)**
```
Path: /set-reading
Method: POST
```
**Node 2: PostgreSQL/Set Variable**
```javascript
// Speichere reading Status in einer Variablen
// Oder direkt in Database wenn du willst
const { title, author, progress, coverUrl, isReading } = items[0].json.body;
return [{
json: {
reading: {
isReading: isReading !== false, // default true
title,
author,
progress,
coverUrl
}
}
}];
```
**Usage:**
```bash
curl -X POST https://your-n8n.com/webhook/set-reading \
-H "Content-Type: application/json" \
-d '{
"isReading": true,
"title": "Clean Architecture",
"author": "Robert C. Martin",
"progress": 65,
"coverUrl": "https://example.com/cover.jpg"
}'
# Clear reading:
curl -X POST https://your-n8n.com/webhook/set-reading \
-d '{"isReading": false}'
```
---
## 🎨 Wie es aussieht
### Desktop (breiter Bildschirm):
```
┌────────────┬────────────┬────────────┬────────────┐
│ Coding │ Gaming │ Music │ Reading │
│ (RIGHT │ (RIGHT │ │ │
│ NOW) │ NOW) │ │ │
└────────────┴────────────┴────────────┴────────────┘
```
### Tablet:
```
┌────────────┬────────────┐
│ Coding │ Gaming │
└────────────┴────────────┘
┌────────────┬────────────┐
│ Music │ Reading │
└────────────┴────────────┘
```
### Mobile:
```
┌────────────┐
│ Coding │
│ (RIGHT │
│ NOW) │
└────────────┘
┌────────────┐
│ Gaming │
└────────────┘
┌────────────┐
│ Music │
└────────────┘
┌────────────┐
│ Reading │
└────────────┘
```
---
## 🔥 Features
**Nur zeigen wenn aktiv** - Wenn `isReading: false`, verschwindet die Card komplett
**Progress Bar** - Visueller Fortschritt mit Animation
**Book Cover** - Kleines Cover (40x56px)
**Responsive Grid** - 1 Spalte (Mobile), 2 Spalten (Tablet), 3 Spalten (Desktop)
**Smooth Animations** - Fade in/out mit Framer Motion
**Amber Theme** - Passt zu "Reading" 📖
---
## 🚀 Testing
**1. Hardcover Endpoint testen:**
```bash
curl https://cms.dk0.dev/api/n8n/hardcover/currently-reading
```
**2. n8n Webhook testen:**
```bash
curl https://your-n8n.com/webhook/denshooter-71242/status
```
**3. Frontend testen:**
```bash
# Dev Server starten
npm run dev
# In Browser Console:
fetch('/api/n8n/status').then(r => r.json()).then(console.log)
```
---
## 📝 Nächste Schritte
1. ✅ Frontend Code ist bereits angepasst
2. ⏳ n8n Workflow Code updaten (siehe `scripts/n8n-workflow-code-updated.js`)
3. ⏳ Optional: Hardcover Node hinzufügen
4. ⏳ Testen und Deploy
**Alles ready! Nur noch n8n Code austauschen.** 🎉