refactor: restructure

monorepo with apps/ and services/
  directories
This commit is contained in:
Wuesteon 2025-11-26 03:03:24 +01:00
parent 25824ed0ac
commit ff80aeec1f
4062 changed files with 2592 additions and 1278 deletions

View file

@ -0,0 +1,91 @@
# Card System Dokumentation
## Übersicht
Das Card System in uload ist eine flexible, modulare Architektur für die Erstellung und Verwaltung von wiederverwendbaren UI-Karten. Es ermöglicht die Erstellung von dynamischen, themebaren und vollständig konfigurierbaren Karten für verschiedene Anwendungsfälle.
## Inhaltsverzeichnis
1. [Architektur](./architecture.md)
2. [Komponenten](./components.md)
3. [Module](./modules.md)
4. [Themes](./themes.md)
5. [Templates](./templates.md)
6. [API Reference](./api-reference.md)
7. [Beispiele](./examples.md)
## Schnellstart
### Basis-Verwendung
```svelte
<script>
import BaseCard from '$lib/components/cards/BaseCard.svelte';
const cardConfig = {
variant: 'default',
modules: [
{
type: 'header',
props: {
title: 'Meine Karte',
subtitle: 'Eine Beispielkarte'
}
}
]
};
</script>
<BaseCard {...cardConfig} />
```
## Hauptkonzepte
### 1. BaseCard
Die zentrale Komponente, die als Container für alle Kartentypen dient.
### 2. Module
Wiederverwendbare Bausteine, aus denen Karten zusammengesetzt werden:
- HeaderModule
- ContentModule
- LinksModule
- MediaModule
- StatsModule
- ActionsModule
- FooterModule
### 3. Themes
Anpassbare Designsysteme mit Farben, Typografie, Abständen und Animationen.
### 4. Templates
Vordefinierte Kartenkonfigurationen für häufige Anwendungsfälle.
## Features
- 🎨 **Vollständig themebare Komponenten**
- 📦 **Modularer Aufbau**
- 💾 **Datenbankgestützte Konfiguration**
- 🎭 **Mehrere Varianten** (default, compact, hero, minimal, glass, gradient)
- 📱 **Responsive Design**
- ⚡ **Optimierte Performance**
- ♿ **Barrierefreiheit**
## Verwendung in der App
Das Card System wird in verschiedenen Bereichen der uload-App verwendet:
1. **Profilseiten** - Anzeige von Benutzerinformationen
2. **Link-Verwaltung** - Darstellung von Link-Sammlungen
3. **Dashboard** - Statistiken und Übersichten
4. **Template Store** - Marktplatz für Kartenvorlagen
## Nächste Schritte
- [Architektur verstehen](./architecture.md)
- [Erste eigene Karte erstellen](./examples.md#erste-karte)
- [Template Store erkunden](./templates.md#template-store)

View file

@ -0,0 +1,539 @@
# Card System API Reference
## CardTemplateService
Der zentrale Service für die Verwaltung von Cards, Templates und Themes.
### Import
```javascript
import { cardTemplateService } from '$lib/services/cardTemplates';
```
## Methoden
### Theme-Verwaltung
#### `getPublicThemes()`
Lädt alle öffentlichen Themes.
**Returns:** `Promise<DBTheme[]>`
**Beispiel:**
```javascript
const themes = await cardTemplateService.getPublicThemes();
```
#### `getTheme(id)`
Lädt ein spezifisches Theme.
**Parameters:**
- `id: string` - Theme-ID
**Returns:** `Promise<DBTheme | null>`
**Beispiel:**
```javascript
const theme = await cardTemplateService.getTheme('theme_123');
```
#### `createTheme(theme)`
Erstellt ein neues Theme.
**Parameters:**
- `theme: Partial<DBTheme>` - Theme-Daten
**Returns:** `Promise<DBTheme | null>`
**Beispiel:**
```javascript
const newTheme = await cardTemplateService.createTheme({
name: 'My Theme',
colors: {
primary: '#ff0000'
}
});
```
#### `updateTheme(id, updates)`
Aktualisiert ein existierendes Theme.
**Parameters:**
- `id: string` - Theme-ID
- `updates: Partial<DBTheme>` - Zu aktualisierende Felder
**Returns:** `Promise<DBTheme | null>`
**Beispiel:**
```javascript
await cardTemplateService.updateTheme('theme_123', {
colors: { primary: '#00ff00' }
});
```
### Template-Verwaltung
#### `getPublicTemplates(category?)`
Lädt öffentliche Templates, optional gefiltert nach Kategorie.
**Parameters:**
- `category?: string` - Optional: Kategorie-Filter
**Returns:** `Promise<DBCardTemplate[]>`
**Beispiel:**
```javascript
// Alle Templates
const allTemplates = await cardTemplateService.getPublicTemplates();
// Nur Profile-Templates
const profileTemplates = await cardTemplateService.getPublicTemplates('profile');
```
#### `getTemplate(id)`
Lädt ein spezifisches Template.
**Parameters:**
- `id: string` - Template-ID
**Returns:** `Promise<DBCardTemplate | null>`
**Beispiel:**
```javascript
const template = await cardTemplateService.getTemplate('template_123');
```
#### `createTemplate(template)`
Erstellt ein neues Template.
**Parameters:**
- `template: Partial<DBCardTemplate>` - Template-Daten
**Returns:** `Promise<DBCardTemplate | null>`
**Beispiel:**
```javascript
const newTemplate = await cardTemplateService.createTemplate({
name: 'My Template',
slug: 'my-template',
modules: [
/* ... */
]
});
```
#### `updateTemplate(id, updates)`
Aktualisiert ein Template.
**Parameters:**
- `id: string` - Template-ID
- `updates: Partial<DBCardTemplate>` - Updates
**Returns:** `Promise<DBCardTemplate | null>`
**Beispiel:**
```javascript
await cardTemplateService.updateTemplate('template_123', {
name: 'Updated Name'
});
```
### User Cards
#### `getUserCards(page)`
Lädt die Karten eines Benutzers für eine bestimmte Seite.
**Parameters:**
- `page: string` - Seitenname (z.B. 'profile', 'dashboard')
**Returns:** `Promise<DBUserCard[]>`
**Beispiel:**
```javascript
const profileCards = await cardTemplateService.getUserCards('profile');
```
#### `saveUserCard(card)`
Speichert oder aktualisiert eine Benutzerkarte.
**Parameters:**
- `card: Partial<DBUserCard>` - Kartendaten
**Returns:** `Promise<DBUserCard | null>`
**Beispiel:**
```javascript
const savedCard = await cardTemplateService.saveUserCard({
template_id: 'template_123',
page: 'profile',
position: 0,
is_active: true
});
```
#### `deleteUserCard(id)`
Löscht eine Benutzerkarte.
**Parameters:**
- `id: string` - Karten-ID
**Returns:** `Promise<boolean>`
**Beispiel:**
```javascript
const success = await cardTemplateService.deleteUserCard('card_123');
```
### Konvertierungsmethoden
#### `templateToCardConfig(template)`
Konvertiert ein Datenbank-Template zu einer CardConfig.
**Parameters:**
- `template: DBCardTemplate` - Template aus Datenbank
**Returns:** `CardConfig`
**Beispiel:**
```javascript
const config = cardTemplateService.templateToCardConfig(dbTemplate);
```
#### `dbThemeToThemeConfig(dbTheme)`
Konvertiert ein Datenbank-Theme zu einer ThemeConfig.
**Parameters:**
- `dbTheme: DBTheme` - Theme aus Datenbank
**Returns:** `ThemeConfig`
**Beispiel:**
```javascript
const themeConfig = cardTemplateService.dbThemeToThemeConfig(dbTheme);
```
#### `userCardToCardConfig(userCard)`
Konvertiert eine Benutzerkarte zu einer CardConfig.
**Parameters:**
- `userCard: DBUserCard` - Benutzerkarte
**Returns:** `CardConfig`
**Beispiel:**
```javascript
const config = cardTemplateService.userCardToCardConfig(userCard);
```
### Spezielle Methoden
#### `createStandardProfileCardTemplate(userData)`
Erstellt eine Standard-Profilkarten-Konfiguration.
**Parameters:**
```typescript
userData: {
username?: string;
email?: string;
bio?: string;
avatar?: string;
websiteUrl?: string;
socialLinks?: any[];
totalLinks?: number;
totalClicks?: number;
memberSince?: string;
showEmail?: boolean;
showStats?: boolean;
}
```
**Returns:** `CardConfig`
**Beispiel:**
```javascript
const profileConfig = cardTemplateService.createStandardProfileCardTemplate({
username: 'johndoe',
bio: 'Software Developer',
totalLinks: 42
});
```
#### `createOrUpdateStandardProfileCard(userData)`
Erstellt oder aktualisiert die Standard-Profilkarte eines Benutzers.
**Parameters:**
- `userData: any` - Benutzerdaten
**Returns:** `Promise<DBUserCard | null>`
**Beispiel:**
```javascript
const card = await cardTemplateService.createOrUpdateStandardProfileCard({
username: 'johndoe',
bio: 'Updated bio'
});
```
#### `incrementDownloads(templateId)`
Erhöht den Download-Zähler eines Templates.
**Parameters:**
- `templateId: string` - Template-ID
**Returns:** `Promise<void>`
**Beispiel:**
```javascript
await cardTemplateService.incrementDownloads('template_123');
```
#### `rateTemplate(templateId, rating)`
Bewertet ein Template.
**Parameters:**
- `templateId: string` - Template-ID
- `rating: number` - Bewertung (1-5)
**Returns:** `Promise<void>`
**Beispiel:**
```javascript
await cardTemplateService.rateTemplate('template_123', 5);
```
## Datentypen
### DBTheme
```typescript
interface DBTheme {
id: string;
name: string;
slug: string;
description?: string;
author?: string;
version?: string;
is_public?: boolean;
is_premium?: boolean;
price?: number;
colors?: any;
typography?: any;
spacing?: any;
borderRadius?: any;
shadows?: any;
animations?: any;
created: string;
updated: string;
}
```
### DBCardTemplate
```typescript
interface DBCardTemplate {
id: string;
name: string;
slug: string;
description?: string;
category?: string;
theme_id?: string;
is_public?: boolean;
modules?: any;
layout?: any;
responsive?: any;
preview_image?: string;
downloads?: number;
rating?: number;
author_id?: string;
created: string;
updated: string;
expand?: {
theme_id?: DBTheme;
author_id?: any;
};
}
```
### DBUserCard
```typescript
interface DBUserCard {
id: string;
user_id: string;
template_id?: string;
page?: string;
position?: number;
custom_config?: any;
is_active?: boolean;
created: string;
updated: string;
expand?: {
template_id?: DBCardTemplate;
};
}
```
### CardConfig
```typescript
interface CardConfig {
id?: string;
variant?: 'default' | 'compact' | 'hero' | 'minimal' | 'glass' | 'gradient';
theme?: ThemeConfig;
modules?: ModuleConfig[];
layout?: LayoutConfig;
animations?: AnimationConfig;
responsive?: ResponsiveConfig;
className?: string;
style?: string;
}
```
### ModuleConfig
```typescript
interface ModuleConfig {
type: 'header' | 'content' | 'footer' | 'media' | 'stats' | 'actions' | 'links' | 'custom';
component?: string;
props?: Record<string, any>;
order?: number;
visibility?: 'always' | 'desktop' | 'mobile' | 'conditional';
grid?: {
col?: number;
row?: number;
colSpan?: number;
rowSpan?: number;
};
className?: string;
}
```
### ThemeConfig
```typescript
interface ThemeConfig {
id?: string;
name?: string;
colors?: {
primary?: string;
secondary?: string;
accent?: string;
background?: string;
surface?: string;
text?: string;
textMuted?: string;
border?: string;
hover?: string;
[key: string]: string | undefined;
};
typography?: {
fontFamily?: string;
fontSize?: Record<string, string>;
fontWeight?: Record<string, number>;
lineHeight?: Record<string, string>;
};
spacing?: Record<string, string>;
borderRadius?: Record<string, string>;
shadows?: Record<string, string>;
}
```
## Error Handling
Alle Service-Methoden geben `null` zurück oder werfen Fehler, die abgefangen werden sollten:
```javascript
try {
const template = await cardTemplateService.getTemplate('invalid_id');
if (!template) {
console.log('Template nicht gefunden');
}
} catch (error) {
console.error('Fehler beim Laden des Templates:', error);
}
```
## Authentifizierung
Einige Methoden erfordern eine aktive Authentifizierung über PocketBase:
```javascript
import { pb } from '$lib/pocketbase';
// Prüfen ob Benutzer eingeloggt ist
if (pb.authStore.model) {
// Authentifizierte Aktionen
const userCards = await cardTemplateService.getUserCards('profile');
} else {
// Redirect zu Login
goto('/login');
}
```
## Performance-Tipps
1. **Caching**: Templates und Themes können gecached werden
2. **Lazy Loading**: Lade nur benötigte Daten
3. **Batch Operations**: Nutze Promise.all für parallele Anfragen
4. **Pagination**: Nutze Pagination für große Listen
```javascript
// Parallel laden
const [themes, templates] = await Promise.all([
cardTemplateService.getPublicThemes(),
cardTemplateService.getPublicTemplates()
]);
```

View file

@ -0,0 +1,276 @@
# Card System Architektur
## Überblick
Das Card System basiert auf einer modularen, komponentenbasierten Architektur, die maximale Flexibilität und Wiederverwendbarkeit bietet.
## Architektur-Diagramm
```
┌─────────────────────────────────────────┐
│ ThemeProvider │
│ (Globale Theme-Konfiguration) │
└────────────────┬────────────────────────┘
┌────────────────▼────────────────────────┐
│ BaseCard │
│ (Container-Komponente) │
│ │
│ Props: │
│ - variant │
│ - theme │
│ - modules[] │
│ - layout │
│ - animations │
└────────────────┬────────────────────────┘
┌────────────┼────────────┐
│ │ │
┌───▼───┐ ┌───▼───┐ ┌───▼───┐
│Module1│ │Module2│ │Module3│
└───────┘ └───────┘ └───────┘
```
## Komponenten-Hierarchie
### 1. BaseCard (`/src/lib/components/cards/BaseCard.svelte`)
Die Hauptkomponente, die als Container für alle Module dient.
**Eigenschaften:**
- **variant**: Visuelle Variante der Karte
- **theme**: Theme-Konfiguration
- **modules**: Array von Modul-Konfigurationen
- **layout**: Layout-Einstellungen
- **animations**: Animations-Konfiguration
- **responsive**: Responsive-Verhalten
**Verantwortlichkeiten:**
- Modul-Rendering
- Theme-Anwendung
- Layout-Management
- Animations-Steuerung
### 2. Module System
Module sind die Bausteine einer Karte. Jedes Modul ist eine eigenständige Komponente mit spezifischer Funktionalität.
**Verfügbare Module:**
#### HeaderModule
```typescript
interface HeaderModuleProps {
title?: string;
subtitle?: string;
avatar?: string;
badge?: string;
icon?: string;
actions?: Array<{
label: string;
action: () => void;
icon?: string;
}>;
}
```
#### ContentModule
```typescript
interface ContentModuleProps {
text?: string;
html?: string;
items?: Array<{
label: string;
value: string | number;
icon?: string;
}>;
truncate?: boolean;
maxLines?: number;
}
```
#### LinksModule
```typescript
interface LinksModuleProps {
links: Array<{
label: string;
href: string;
icon?: string;
description?: string;
disabled?: boolean;
}>;
style?: 'button' | 'list' | 'card';
columns?: 1 | 2;
showDescription?: boolean;
showIcon?: boolean;
target?: '_blank' | '_self';
buttonVariant?: 'primary' | 'secondary' | 'ghost' | 'outline';
gap?: 'sm' | 'md' | 'lg';
}
```
## Datenfluss
```
Datenbank (PocketBase)
Service Layer
(cardTemplates.ts)
Store/State
UI Components
(BaseCard + Module)
Rendering
```
## Service Layer
### CardTemplateService (`/src/lib/services/cardTemplates.ts`)
Der Service verwaltet die Kommunikation mit der Datenbank und die Transformation von Daten.
**Hauptmethoden:**
- `getPublicTemplates()` - Lädt öffentliche Templates
- `getTemplate(id)` - Lädt ein spezifisches Template
- `createTemplate(template)` - Erstellt neues Template
- `updateTemplate(id, updates)` - Aktualisiert Template
- `getUserCards(page)` - Lädt Benutzerkarten
- `saveUserCard(card)` - Speichert Benutzerkarte
- `templateToCardConfig(template)` - Konvertiert DB-Template zu CardConfig
- `dbThemeToThemeConfig(dbTheme)` - Konvertiert DB-Theme zu ThemeConfig
## Datenbank-Schema
### Collections in PocketBase
#### `themes`
Speichert Theme-Konfigurationen
- Farben, Typografie, Abstände
- Öffentlich/Privat
- Premium/Free
#### `card_templates`
Vordefinierte Kartenkonfigurationen
- Module-Array
- Layout-Einstellungen
- Kategorie und Tags
#### `user_cards`
Benutzerspezifische Karteninstanzen
- Verknüpfung zu Templates
- Custom-Konfiguration
- Position und Sichtbarkeit
## Konfigurationsstruktur
### CardConfig
```typescript
interface CardConfig {
id?: string;
variant?: 'default' | 'compact' | 'hero' | 'minimal' | 'glass' | 'gradient';
theme?: ThemeConfig;
modules?: ModuleConfig[];
layout?: LayoutConfig;
animations?: AnimationConfig;
responsive?: ResponsiveConfig;
className?: string;
style?: string;
}
```
### ModuleConfig
```typescript
interface ModuleConfig {
type: 'header' | 'content' | 'footer' | 'media' | 'stats' | 'actions' | 'links' | 'custom';
component?: string;
props?: Record<string, any>;
order?: number;
visibility?: 'always' | 'desktop' | 'mobile' | 'conditional';
grid?: {
col?: number;
row?: number;
colSpan?: number;
rowSpan?: number;
};
className?: string;
}
```
### ThemeConfig
```typescript
interface ThemeConfig {
id?: string;
name?: string;
colors?: {
primary?: string;
secondary?: string;
accent?: string;
background?: string;
surface?: string;
text?: string;
textMuted?: string;
border?: string;
hover?: string;
};
typography?: {
fontFamily?: string;
fontSize?: Record<string, string>;
fontWeight?: Record<string, number>;
lineHeight?: Record<string, string>;
};
spacing?: Record<string, string>;
borderRadius?: Record<string, string>;
shadows?: Record<string, string>;
}
```
## Performance-Optimierungen
1. **Lazy Loading**: Module werden nur bei Bedarf geladen
2. **Memoization**: Berechnete Werte werden gecached
3. **Virtual Scrolling**: Bei langen Listen
4. **Bundle Splitting**: Separate Bundles für Module
5. **CSS-in-JS**: Nur benötigte Styles werden geladen
## Erweiterbarkeit
### Neue Module hinzufügen
1. Komponente in `/src/lib/components/cards/modules/` erstellen
2. Props-Interface in `types.ts` definieren
3. Module in `BaseCard.svelte` registrieren
4. Dokumentation aktualisieren
### Custom Themes erstellen
1. Theme-Objekt nach ThemeConfig-Interface erstellen
2. In Datenbank speichern oder lokal verwenden
3. Mit ThemeProvider anwenden
## Best Practices
1. **Modularität**: Halte Module klein und fokussiert
2. **Typsicherheit**: Verwende TypeScript-Interfaces
3. **Responsive**: Teste auf verschiedenen Bildschirmgrößen
4. **Performance**: Minimiere Re-Renders
5. **Barrierefreiheit**: ARIA-Labels und Keyboard-Navigation

View file

@ -0,0 +1,350 @@
# Card System Komponenten
## BaseCard
Die zentrale Komponente des Card Systems.
### Import
```svelte
<script>
import BaseCard from '$lib/components/cards/BaseCard.svelte';
</script>
```
### Props
| Prop | Type | Default | Beschreibung |
| ------------ | ------------------ | ----------- | ------------------------------------------------------------------------------- |
| `variant` | `string` | `'default'` | Visuelle Variante: `default`, `compact`, `hero`, `minimal`, `glass`, `gradient` |
| `theme` | `ThemeConfig` | `{}` | Theme-Konfiguration |
| `modules` | `ModuleConfig[]` | `[]` | Array von Modul-Konfigurationen |
| `layout` | `LayoutConfig` | `{}` | Layout-Einstellungen |
| `animations` | `AnimationConfig` | `{}` | Animations-Konfiguration |
| `responsive` | `ResponsiveConfig` | `{}` | Responsive-Einstellungen |
| `className` | `string` | `''` | Zusätzliche CSS-Klassen |
| `style` | `string` | `''` | Inline-Styles |
### Varianten
#### Default
```svelte
<BaseCard variant="default">
<!-- Standard-Karte mit Border und Shadow -->
</BaseCard>
```
#### Compact
```svelte
<BaseCard variant="compact">
<!-- Kompakte Karte mit reduziertem Padding -->
</BaseCard>
```
#### Hero
```svelte
<BaseCard variant="hero">
<!-- Große Karte mit Gradient-Hintergrund -->
</BaseCard>
```
#### Minimal
```svelte
<BaseCard variant="minimal">
<!-- Minimalistische Karte ohne Border -->
</BaseCard>
```
#### Glass
```svelte
<BaseCard variant="glass">
<!-- Glasmorphismus-Effekt -->
</BaseCard>
```
#### Gradient
```svelte
<BaseCard variant="gradient">
<!-- Gradient-Hintergrund -->
</BaseCard>
```
## CardBuilder
Interaktiver Builder zum Erstellen von Karten.
### Import
```svelte
<script>
import CardBuilder from '$lib/components/builder/CardBuilder.svelte';
</script>
```
### Props
| Prop | Type | Default | Beschreibung |
| --------------- | ------------- | ------- | ----------------------- |
| `initialConfig` | `CardConfig` | `{}` | Initiale Konfiguration |
| `theme` | `ThemeConfig` | `{}` | Theme für Preview |
| `onSave` | `Function` | - | Callback beim Speichern |
| `onCancel` | `Function` | - | Callback beim Abbrechen |
### Beispiel
```svelte
<CardBuilder
initialConfig={myCardConfig}
theme={myTheme}
onSave={(config) => saveCard(config)}
onCancel={() => goto('/cards')}
/>
```
### Features
- Drag & Drop für Module
- Live-Preview
- Modul-Editor
- Export als JSON oder Svelte-Code
- Theme-Auswahl
## ThemeProvider
Stellt Theme-Kontext für Kinder-Komponenten bereit.
### Import
```svelte
<script>
import ThemeProvider from '$lib/components/cards/ThemeProvider.svelte';
</script>
```
### Verwendung
```svelte
<ThemeProvider theme={myTheme}>
<BaseCard {...cardConfig} />
</ThemeProvider>
```
### Theme-Struktur
```javascript
const theme = {
colors: {
primary: '#3b82f6',
secondary: '#8b5cf6',
accent: '#ec4899',
background: '#ffffff',
surface: '#f9fafb',
text: '#111827',
textMuted: '#6b7280',
border: '#e5e7eb',
hover: '#f3f4f6'
},
typography: {
fontFamily: 'Inter, sans-serif',
fontSize: {
xs: '0.75rem',
sm: '0.875rem',
md: '1rem',
lg: '1.125rem',
xl: '1.25rem'
}
},
spacing: {
xs: '0.25rem',
sm: '0.5rem',
md: '1rem',
lg: '1.5rem',
xl: '2rem'
},
borderRadius: {
sm: '0.25rem',
md: '0.5rem',
lg: '0.75rem',
xl: '1rem',
full: '9999px'
}
};
```
## ModuleEditor
Editor für einzelne Module innerhalb des CardBuilders.
### Features
- Prop-Editor für jedes Modul
- Visuelle Konfiguration
- Echtzeit-Vorschau
- Validierung
## ProfileInfoCard
Spezialisierte Karte für Benutzerprofile.
### Import
```svelte
<script>
import ProfileInfoCard from '$lib/components/profile/ProfileInfoCard.svelte';
</script>
```
### Props
```typescript
interface ProfileInfoCardProps {
user: {
username?: string;
name?: string;
avatar?: string;
bio?: string;
location?: string;
website?: string;
github?: string;
twitter?: string;
linkedin?: string;
instagram?: string;
showClickStats?: boolean;
created?: string;
};
totalLinks?: number;
totalFolders?: number;
totalClicks?: number;
memberSince?: string;
}
```
### Beispiel
```svelte
<ProfileInfoCard
user={userData}
totalLinks={25}
totalFolders={5}
totalClicks={1337}
memberSince="January 2024"
/>
```
## LinksCard
Karte zur Anzeige von Link-Sammlungen.
### Import
```svelte
<script>
import LinksCard from '$lib/components/profile/LinksCard.svelte';
</script>
```
### Props
```typescript
interface LinksCardProps {
links: Link[];
folders: Folder[];
username: string;
showClickStats?: boolean;
oneLinkQR?: string;
}
```
### Features
- Suche und Filter
- Sortierung (Recent, Clicks, Title)
- Ordner-Filter
- QR-Code-Anzeige
- Click-Statistiken
## Komposition
### Beispiel: Dashboard-Karte
```svelte
<BaseCard
variant="default"
layout={{
padding: '1.5rem',
columns: 2,
gap: '1rem'
}}
modules={[
{
type: 'header',
props: {
title: 'Dashboard',
subtitle: 'Übersicht deiner Aktivitäten',
icon: '📊'
}
},
{
type: 'stats',
props: {
stats: [
{ label: 'Links', value: 42, icon: '🔗' },
{ label: 'Clicks', value: '1.2k', icon: '👆' },
{ label: 'Conversion', value: '24%', icon: '📈' }
],
layout: 'grid'
}
},
{
type: 'actions',
props: {
actions: [
{ label: 'Neuer Link', variant: 'primary' },
{ label: 'Statistiken', variant: 'secondary' }
],
layout: 'horizontal'
}
}
]}
/>
```
## Styling
### CSS-Variablen
Alle Komponenten nutzen CSS-Variablen für Theming:
```css
--card-primary: #3b82f6;
--card-secondary: #8b5cf6;
--card-accent: #ec4899;
--card-background: #ffffff;
--card-surface: #f9fafb;
--card-text: #111827;
--card-text-muted: #6b7280;
--card-border: #e5e7eb;
```
### Tailwind-Integration
Die Komponenten verwenden Tailwind-Klassen mit Theme-Präfix:
- `bg-theme-primary`
- `text-theme-text`
- `border-theme-border`
- etc.
## Best Practices
1. **Verwende die richtige Variante** für deinen Use-Case
2. **Halte Module fokussiert** - Ein Modul, eine Aufgabe
3. **Nutze Theme-Provider** für konsistentes Styling
4. **Teste Responsive-Verhalten** auf verschiedenen Geräten
5. **Optimiere Performance** durch lazy loading großer Module

View file

@ -0,0 +1,579 @@
# Card System Beispiele
## Erste Karte
### Einfache Karte mit Header
```svelte
<script>
import BaseCard from '$lib/components/cards/BaseCard.svelte';
</script>
<BaseCard
variant="default"
modules={[
{
type: 'header',
props: {
title: 'Meine erste Karte',
subtitle: 'Ein einfaches Beispiel',
icon: '🎉'
}
}
]}
/>
```
### Karte mit mehreren Modulen
```svelte
<BaseCard
variant="default"
modules={[
{
type: 'header',
order: 0,
props: {
title: 'Komplexere Karte',
subtitle: 'Mit mehreren Modulen'
}
},
{
type: 'content',
order: 1,
props: {
text: 'Dies ist der Hauptinhalt der Karte. Hier kann beliebiger Text stehen.'
}
},
{
type: 'actions',
order: 2,
props: {
actions: [
{ label: 'Mehr erfahren', variant: 'primary' },
{ label: 'Schließen', variant: 'ghost' }
]
}
}
]}
/>
```
## Profil-Karten
### Basis-Profil
```svelte
<script>
import BaseCard from '$lib/components/cards/BaseCard.svelte';
const userData = {
name: 'Max Mustermann',
role: 'Software Developer',
avatar: '/avatars/max.jpg',
bio: 'Passionate about creating amazing web experiences.'
};
</script>
<BaseCard
variant="default"
modules={[
{
type: 'header',
props: {
title: userData.name,
subtitle: userData.role,
avatar: userData.avatar
}
},
{
type: 'content',
props: {
text: userData.bio
}
}
]}
/>
```
### Erweitertes Profil mit Stats
```svelte
<script>
const profileConfig = {
variant: 'hero',
modules: [
{
type: 'header',
props: {
title: 'Jane Doe',
subtitle: 'UX Designer',
avatar: '/avatars/jane.jpg',
badge: 'PRO'
}
},
{
type: 'content',
props: {
text: 'Creating beautiful and functional user experiences since 2015.'
}
},
{
type: 'stats',
props: {
stats: [
{ label: 'Projects', value: 127, icon: '📁' },
{ label: 'Clients', value: 45, icon: '👥' },
{ label: 'Awards', value: 8, icon: '🏆' }
],
layout: 'grid'
}
},
{
type: 'links',
props: {
links: [
{ label: 'Portfolio', href: 'https://portfolio.com', icon: '🎨' },
{ label: 'LinkedIn', href: 'https://linkedin.com', icon: '💼' },
{ label: 'GitHub', href: 'https://github.com', icon: '💻' }
],
style: 'button',
showIcon: true
}
}
]
};
</script>
<BaseCard {...profileConfig} />
```
## Dashboard-Karten
### Statistik-Karte
```svelte
<BaseCard
variant="compact"
modules={[
{
type: 'header',
props: {
title: 'Verkaufsstatistik',
subtitle: 'Letzten 30 Tage'
}
},
{
type: 'stats',
props: {
stats: [
{ label: 'Umsatz', value: '€12.4k', change: 12, color: 'green' },
{ label: 'Bestellungen', value: 234, change: -5, color: 'red' },
{ label: 'Conversion', value: '3.2%', change: 8, color: 'blue' }
],
layout: 'list'
}
}
]}
/>
```
### Activity Feed
```svelte
<BaseCard
variant="default"
modules={[
{
type: 'header',
props: {
title: 'Letzte Aktivitäten',
icon: '📋'
}
},
{
type: 'content',
props: {
items: [
{ label: 'Neuer Benutzer', value: 'vor 5 Min.', icon: '👤' },
{ label: 'Bestellung #1234', value: 'vor 15 Min.', icon: '🛒' },
{ label: 'Kommentar erhalten', value: 'vor 1 Std.', icon: '💬' },
{ label: 'System-Update', value: 'vor 2 Std.', icon: '🔄' }
]
}
}
]}
/>
```
## Link-Sammlungen
### Social Media Links
```svelte
<BaseCard
variant="minimal"
modules={[
{
type: 'header',
props: {
title: 'Folge mir',
subtitle: 'auf Social Media'
}
},
{
type: 'links',
props: {
links: [
{ label: 'Instagram', href: 'https://instagram.com/user', icon: '📷' },
{ label: 'Twitter', href: 'https://twitter.com/user', icon: '🐦' },
{ label: 'YouTube', href: 'https://youtube.com/user', icon: '📺' },
{ label: 'TikTok', href: 'https://tiktok.com/@user', icon: '🎵' }
],
style: 'button',
columns: 2,
buttonVariant: 'secondary',
showIcon: true
}
}
]}
/>
```
### Ressourcen-Liste
```svelte
<BaseCard
variant="default"
modules={[
{
type: 'header',
props: {
title: 'Nützliche Ressourcen'
}
},
{
type: 'links',
props: {
links: [
{
label: 'Dokumentation',
href: '/docs',
icon: '📚',
description: 'Vollständige API-Dokumentation'
},
{
label: 'Tutorials',
href: '/tutorials',
icon: '🎓',
description: 'Schritt-für-Schritt Anleitungen'
},
{
label: 'Community Forum',
href: '/forum',
icon: '💬',
description: 'Hilfe von der Community'
}
],
style: 'card',
showDescription: true,
showIcon: true
}
}
]}
/>
```
## Media-Karten
### Bild-Galerie
```svelte
<BaseCard
variant="default"
modules={[
{
type: 'header',
props: {
title: 'Projekt Screenshots'
}
},
{
type: 'media',
props: {
type: 'image',
src: '/screenshots/dashboard.png',
alt: 'Dashboard Screenshot',
aspectRatio: '16/9'
}
},
{
type: 'content',
props: {
text: 'Das neue Dashboard-Design mit verbesserter Benutzerführung.'
}
}
]}
/>
```
### QR-Code Karte
```svelte
<BaseCard
variant="compact"
modules={[
{
type: 'header',
props: {
title: 'Mein QR-Code',
subtitle: 'Scanne für Kontaktdaten'
}
},
{
type: 'media',
props: {
type: 'qr',
qrData: 'https://example.com/contact',
qrSize: 200,
qrColor: '#000000'
}
}
]}
/>
```
## Mit Themes
### Dark Theme Karte
```svelte
<script>
import ThemeProvider from '$lib/components/cards/ThemeProvider.svelte';
const darkTheme = {
colors: {
primary: '#60a5fa',
secondary: '#a78bfa',
accent: '#f472b6',
background: '#111827',
surface: '#1f2937',
text: '#f9fafb',
textMuted: '#9ca3af',
border: '#374151',
hover: '#374151'
}
};
</script>
<ThemeProvider theme={darkTheme}>
<BaseCard
variant="default"
modules={[
{
type: 'header',
props: {
title: 'Dark Mode Karte',
subtitle: 'Mit custom Theme'
}
}
]}
/>
</ThemeProvider>
```
### Gradient Theme
```svelte
<script>
const gradientTheme = {
colors: {
primary: '#ff6b6b',
secondary: '#4ecdc4',
accent: '#45b7d1',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
text: '#ffffff'
}
};
</script>
<BaseCard
variant="gradient"
theme={gradientTheme}
modules={[
{
type: 'header',
props: {
title: 'Gradient Card',
subtitle: 'Mit Farbverlauf'
}
}
]}
/>
```
## Dynamische Karten
### Karte aus Datenbank laden
```svelte
<script>
import { cardTemplateService } from '$lib/services/cardTemplates';
import { onMount } from 'svelte';
let cardConfig = null;
onMount(async () => {
const template = await cardTemplateService.getTemplate('template_123');
if (template) {
cardConfig = cardTemplateService.templateToCardConfig(template);
}
});
</script>
{#if cardConfig}
<BaseCard {...cardConfig} />
{/if}
```
### Benutzer-spezifische Karte
```svelte
<script>
import { pb } from '$lib/pocketbase';
let userCards = [];
async function loadUserCards() {
userCards = await cardTemplateService.getUserCards('profile');
}
onMount(loadUserCards);
</script>
{#each userCards as userCard}
<BaseCard {...cardTemplateService.userCardToCardConfig(userCard)} />
{/each}
```
## Card Builder Integration
### Karte bearbeiten
```svelte
<script>
import CardBuilder from '$lib/components/builder/CardBuilder.svelte';
let editMode = false;
let cardConfig = {
/* ... */
};
function handleSave(newConfig) {
cardConfig = newConfig;
editMode = false;
// Speichern in Datenbank
}
</script>
{#if editMode}
<CardBuilder initialConfig={cardConfig} onSave={handleSave} onCancel={() => (editMode = false)} />
{:else}
<BaseCard {...cardConfig} />
<button onclick={() => (editMode = true)}>Bearbeiten</button>
{/if}
```
## Responsive Karten
### Mobile-optimierte Karte
```svelte
<BaseCard
variant="default"
responsive={{
breakpoints: {
sm: '640px',
md: '768px',
lg: '1024px'
},
mobileLayout: 'stack'
}}
modules={[
{
type: 'header',
visibility: 'always',
props: { title: 'Responsive Karte' }
},
{
type: 'content',
visibility: 'desktop', // Nur auf Desktop
props: { text: 'Dieser Text ist nur auf Desktop sichtbar.' }
},
{
type: 'actions',
visibility: 'mobile', // Nur auf Mobile
props: {
actions: [{ label: 'Mobile Action', variant: 'primary' }]
}
}
]}
/>
```
## Animierte Karten
### Mit Eingangs-Animation
```svelte
<BaseCard
variant="default"
animations={{
hover: true,
entrance: 'slide',
duration: 300,
delay: 100
}}
modules={[
{
type: 'header',
props: {
title: 'Animierte Karte',
subtitle: 'Mit Slide-In Effekt'
}
}
]}
/>
```
## Fehlerbehandlung
### Mit Fallback
```svelte
<script>
let cardConfig = null;
let error = null;
async function loadCard() {
try {
const template = await cardTemplateService.getTemplate('id');
cardConfig = cardTemplateService.templateToCardConfig(template);
} catch (e) {
error = e.message;
}
}
</script>
{#if error}
<BaseCard variant="minimal">
<p>Fehler: {error}</p>
</BaseCard>
{:else if cardConfig}
<BaseCard {...cardConfig} />
{:else}
<BaseCard variant="minimal">
<p>Lädt...</p>
</BaseCard>
{/if}
```

View file

@ -0,0 +1,304 @@
# Implementierungs-Vergleich: Module vs. HTML Cards
## Zusammenfassung für Entscheidungsfindung
### 🎯 Die Kernfrage
Soll das Card-System komplett auf HTML/CSS umgestellt werden oder ein Hybrid-Ansatz verfolgt werden?
## Option 1: Vollständige HTML/CSS Migration
### Wie es funktioniert
```html
<!-- Nutzer schreibt direkt HTML/CSS -->
<div class="my-custom-card">
<style>
.my-custom-card {
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
padding: 2rem;
border-radius: 1rem;
}
</style>
<h1>{{username}}</h1>
<p>{{bio}}</p>
</div>
```
### ✅ Vorteile
- **Maximale Freiheit** für kreative Nutzer
- **Einfachere Datenbank** (nur HTML/CSS Text speichern)
- **Universell portabel** (funktioniert überall)
- **Keine Framework-Abhängigkeit**
### ❌ Nachteile
- **Sicherheitsrisiko** (XSS, CSS-Injection)
- **Keine Garantie für Responsive Design**
- **Schwer für Anfänger**
- **Performance nicht kontrollierbar**
- **Wartbarkeit leidet**
### Datenbank-Schema
```sql
CREATE TABLE cards (
id TEXT PRIMARY KEY,
user_id TEXT,
html TEXT, -- Einfacher HTML String
css TEXT, -- Einfacher CSS String
variables JSON -- Template-Variablen
);
```
## Option 2: Beibehaltung Modulares System
### Wie es funktioniert
```javascript
// Nutzer konfiguriert Module
{
modules: [
{ type: 'header', props: { title: 'Titel' } },
{ type: 'content', props: { text: 'Inhalt' } }
];
}
```
### ✅ Vorteile
- **Konsistente Qualität**
- **Automatisch responsive**
- **Sicher** (kein Code-Injection)
- **Einfach für Anfänger**
- **Optimale Performance**
### ❌ Nachteile
- **Begrenzte Kreativität**
- **Komplexere Datenbank**
- **Framework-abhängig**
- **Mehr Entwicklungsaufwand**
## Option 3: Hybrid-Ansatz (EMPFEHLUNG) 🏆
### Die beste Lösung aus beiden Welten
```typescript
interface UnifiedCard {
renderMode: 'beginner' | 'advanced' | 'expert';
// Beginner: Visual Builder
modules?: ModuleConfig[];
// Advanced: Template mit Variablen
template?: string;
// Expert: Raw HTML/CSS
customHTML?: string;
customCSS?: string;
}
```
### Drei Stufen für verschiedene Nutzergruppen
#### 🟢 Stufe 1: Visual Builder (80% der Nutzer)
```javascript
// Einfach, sicher, schnell
{
renderMode: 'beginner',
modules: [
{ type: 'header', props: { title: 'Meine Karte' } }
]
}
```
#### 🟡 Stufe 2: Template Editor (15% der Nutzer)
```handlebars
// Flexibler, aber noch kontrolliert { renderMode: 'advanced', template: `
<div class='card'>
<h2>{{title}}</h2>
<p>{{description}}</p>
</div>
` }
```
#### 🔴 Stufe 3: Code Editor (5% der Nutzer)
```html
// Volle Kontrolle für Power-User { renderMode: 'expert', customHTML: '
<div>Komplett custom...</div>
', customCSS: '.custom { ... }' }
```
## 📊 Entscheidungsmatrix
| Kriterium | Nur Module | Nur HTML | Hybrid |
| ----------------------------------- | ---------- | ---------- | ---------- |
| **Nutzerfreundlichkeit (Anfänger)** | ⭐⭐⭐⭐⭐ | ⭐ | ⭐⭐⭐⭐⭐ |
| **Flexibilität (Experten)** | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| **Sicherheit** | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ |
| **Performance** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| **Wartbarkeit** | ⭐⭐⭐⭐⭐ | ⭐ | ⭐⭐⭐⭐ |
| **Entwicklungsaufwand** | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ |
| **Datenbank-Komplexität** | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| **Zukunftssicherheit** | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
## 🚀 Implementierungs-Roadmap für Hybrid-Ansatz
### Phase 1: Vorbereitung (Woche 1)
```typescript
// 1. Unified Card Interface definieren
interface UnifiedCard {
id: string;
renderMode: RenderMode;
config: ModularConfig | TemplateConfig | CustomConfig;
constraints: CardConstraints;
}
// 2. Renderer abstrahieren
class CardRenderer {
render(card: UnifiedCard): HTMLElement {
switch (card.renderMode) {
case 'beginner':
return this.renderModular(card);
case 'advanced':
return this.renderTemplate(card);
case 'expert':
return this.renderCustom(card);
}
}
}
```
### Phase 2: HTML/CSS Renderer (Woche 2-3)
```typescript
// Sicherer HTML Renderer mit Sandboxing
class SafeHTMLRenderer {
private sanitizer = new DOMPurify();
render(html: string, css: string): SafeHTML {
const safeHTML = this.sanitizer.sanitize(html);
const safeCSS = this.sanitizeCSS(css);
return this.wrapInIframe(safeHTML, safeCSS);
}
}
```
### Phase 3: Migrations-Tools (Woche 4)
```typescript
// Konverter zwischen Formaten
class CardConverter {
// Module → HTML
modulesToHTML(modules: ModuleConfig[]): string {
return modules.map((m) => this.moduleToHTML(m)).join('');
}
// HTML → Module (Best Effort)
htmlToModules(html: string): ModuleConfig[] {
// AI-unterstützte Konvertierung
return this.parseHTMLStructure(html);
}
}
```
### Phase 4: UI Integration (Woche 5-6)
```svelte
<!-- Unified Card Builder -->
<script>
let mode = 'beginner';
let card = createEmptyCard();
</script>
<div class="builder">
<!-- Mode Selector -->
<ModeSelector bind:mode />
<!-- Conditional Editors -->
{#if mode === 'beginner'}
<VisualBuilder bind:card />
{:else if mode === 'advanced'}
<TemplateEditor bind:card />
{:else}
<CodeEditor bind:card />
{/if}
<!-- Universal Preview -->
<CardPreview {card} />
</div>
```
## 💰 Kosten-Nutzen-Analyse
### Entwicklungskosten
- **Nur Module**: 2 Wochen (bereits fertig)
- **Nur HTML**: 4 Wochen (Neuimplementierung)
- **Hybrid**: 6 Wochen (beide Systeme)
### Langfristiger Nutzen
- **Nur Module**: Begrenzte Zielgruppe
- **Nur HTML**: Sicherheitsrisiken, Support-Aufwand
- **Hybrid**: Maximale Reichweite, zukunftssicher
## 🎯 Finale Empfehlung
### Implementiere den Hybrid-Ansatz mit folgender Priorität:
1. **Behalte das modulare System** als Hauptfeature
2. **Füge Template-Editor hinzu** für fortgeschrittene Nutzer
3. **HTML/CSS als "Beta-Feature"** für Power-User
4. **Schrittweise Migration** basierend auf Nutzer-Feedback
### Warum Hybrid?
- ✅ **Keine Breaking Changes** - Existierende Cards funktionieren weiter
- ✅ **Progressive Enhancement** - Nutzer können wachsen
- ✅ **Marktdifferenzierung** - Einzigartige Features für alle Nutzergruppen
- ✅ **Risikominimierung** - Sicherheitsprobleme nur bei Opt-in
- ✅ **Lernkurve** - Anfänger werden nicht überfordert
### Datenbank bleibt flexibel:
```sql
-- Einheitliche Struktur für alle Modi
CREATE TABLE unified_cards (
id TEXT PRIMARY KEY,
user_id TEXT,
render_mode TEXT CHECK(render_mode IN ('beginner', 'advanced', 'expert')),
-- Für modulare Cards
modules JSON,
-- Für Template Cards
template TEXT,
template_vars JSON,
-- Für Custom HTML
custom_html TEXT,
custom_css TEXT,
-- Gemeinsam
constraints JSON,
theme_id TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
```
## 🏁 Nächste Schritte
1. **User Research**: Umfrage welche Features gewünscht sind
2. **Proof of Concept**: HTML-Renderer mit Sandbox
3. **Security Audit**: Externe Überprüfung
4. **Incremental Rollout**: Erst für Premium-User
5. **Monitoring**: Welcher Modus wird wie genutzt?

View file

@ -0,0 +1,408 @@
# Card System Module
Module sind die Bausteine des Card Systems. Jedes Modul hat eine spezifische Funktion und kann in verschiedenen Kombinationen verwendet werden.
## Verfügbare Module
### HeaderModule
Zeigt Titel, Untertitel, Avatar und optionale Actions an.
#### Props
```typescript
interface HeaderModuleProps {
title?: string; // Haupttitel
subtitle?: string; // Untertitel
avatar?: string; // Avatar-URL
avatarAlt?: string; // Alt-Text für Avatar
badge?: string; // Badge-Text
icon?: string; // Icon (Emoji oder Symbol)
actions?: Array<{
// Action-Buttons
label: string;
action: () => void;
icon?: string;
}>;
}
```
#### Beispiel
```javascript
{
type: 'header',
props: {
title: 'John Doe',
subtitle: 'Software Developer',
avatar: '/avatars/john.jpg',
badge: 'PRO',
actions: [
{ label: 'Edit', action: () => editProfile(), icon: '✏️' }
]
}
}
```
### ContentModule
Zeigt Text, HTML oder Listen an.
#### Props
```typescript
interface ContentModuleProps {
text?: string; // Plaintext
html?: string; // HTML-Inhalt
items?: Array<{
// Liste von Items
label: string;
value: string | number;
icon?: string;
}>;
truncate?: boolean; // Text abschneiden
maxLines?: number; // Max. Zeilen
}
```
#### Beispiel
```javascript
{
type: 'content',
props: {
text: 'Dies ist eine Beschreibung...',
truncate: true,
maxLines: 3
}
}
```
### LinksModule
Zeigt eine Liste von Links in verschiedenen Stilen an.
#### Props
```typescript
interface LinksModuleProps {
links: Array<{
label: string;
href: string;
icon?: string;
description?: string;
disabled?: boolean;
}>;
style?: 'button' | 'list' | 'card'; // Darstellungsstil
columns?: 1 | 2; // Anzahl Spalten
showDescription?: boolean; // Beschreibung anzeigen
showIcon?: boolean; // Icons anzeigen
target?: '_blank' | '_self'; // Link-Target
buttonVariant?: 'primary' | 'secondary' | 'ghost' | 'outline';
gap?: 'sm' | 'md' | 'lg'; // Abstand zwischen Links
}
```
#### Beispiel
```javascript
{
type: 'links',
props: {
links: [
{ label: 'Website', href: 'https://example.com', icon: '🌐' },
{ label: 'GitHub', href: 'https://github.com/user', icon: '💻' },
{ label: 'LinkedIn', href: 'https://linkedin.com/in/user', icon: '💼' }
],
style: 'button',
columns: 1,
showIcon: true,
buttonVariant: 'secondary'
}
}
```
### MediaModule
Zeigt Bilder, Videos, QR-Codes oder Icons an.
#### Props
```typescript
interface MediaModuleProps {
type: 'image' | 'video' | 'qr' | 'icon'; // Medientyp
src?: string; // Source-URL
alt?: string; // Alt-Text
aspectRatio?: string; // Seitenverhältnis (z.B. '16/9')
objectFit?: 'cover' | 'contain' | 'fill'; // Objekt-Anpassung
qrData?: string; // Daten für QR-Code
qrSize?: number; // QR-Code Größe
qrColor?: string; // QR-Code Farbe
icon?: string; // Icon (für type='icon')
iconSize?: string; // Icon-Größe
}
```
#### Beispiel
```javascript
{
type: 'media',
props: {
type: 'image',
src: '/images/hero.jpg',
alt: 'Hero Image',
aspectRatio: '16/9',
objectFit: 'cover'
}
}
```
### StatsModule
Zeigt Statistiken in verschiedenen Layouts an.
#### Props
```typescript
interface StatsModuleProps {
stats: Array<{
label: string;
value: string | number;
change?: number; // Prozentuale Änderung
icon?: string;
color?: string; // Custom Farbe
}>;
layout?: 'grid' | 'list' | 'compact'; // Layout-Stil
}
```
#### Beispiel
```javascript
{
type: 'stats',
props: {
stats: [
{ label: 'Besucher', value: '1.2k', change: 12, icon: '👥' },
{ label: 'Umsatz', value: '€5.4k', change: -3, icon: '💰' },
{ label: 'Conversion', value: '24%', icon: '📈' }
],
layout: 'grid'
}
}
```
### ActionsModule
Zeigt Action-Buttons oder Links an.
#### Props
```typescript
interface ActionsModuleProps {
actions: Array<{
label: string;
action?: () => void; // Callback-Funktion
href?: string; // Alternativ: Link
variant?: 'primary' | 'secondary' | 'ghost' | 'link';
icon?: string;
disabled?: boolean;
}>;
layout?: 'horizontal' | 'vertical' | 'grid';
alignment?: 'left' | 'center' | 'right' | 'between';
}
```
#### Beispiel
```javascript
{
type: 'actions',
props: {
actions: [
{ label: 'Speichern', variant: 'primary', action: () => save() },
{ label: 'Abbrechen', variant: 'ghost', action: () => cancel() }
],
layout: 'horizontal',
alignment: 'right'
}
}
```
### FooterModule
Zeigt Footer-Informationen mit Links und Social Media an.
#### Props
```typescript
interface FooterModuleProps {
text?: string; // Footer-Text
links?: Array<{
// Footer-Links
label: string;
href: string;
icon?: string;
}>;
copyright?: string; // Copyright-Text
socialLinks?: Array<{
// Social Media Links
platform: string;
url: string;
icon?: string;
}>;
}
```
#### Beispiel
```javascript
{
type: 'footer',
props: {
text: 'Powered by uload',
copyright: '© 2024 Company Name',
socialLinks: [
{ platform: 'Twitter', url: 'https://twitter.com/company', icon: '🐦' },
{ platform: 'Facebook', url: 'https://facebook.com/company', icon: '📘' }
]
}
}
```
## Module kombinieren
### Beispiel: Profil-Karte
```javascript
const profileCard = {
variant: 'default',
modules: [
{
type: 'header',
order: 0,
props: {
title: 'Jane Smith',
subtitle: 'UX Designer',
avatar: '/avatars/jane.jpg'
}
},
{
type: 'content',
order: 1,
props: {
text: 'Passionate about creating beautiful and functional user experiences.'
}
},
{
type: 'stats',
order: 2,
props: {
stats: [
{ label: 'Projects', value: 42 },
{ label: 'Clients', value: 18 },
{ label: 'Awards', value: 3 }
]
}
},
{
type: 'links',
order: 3,
props: {
links: [
{ label: 'Portfolio', href: 'https://portfolio.com' },
{ label: 'Contact', href: 'mailto:jane@example.com' }
],
style: 'button'
}
}
]
};
```
## Eigene Module erstellen
### 1. Komponente erstellen
```svelte
<!-- src/lib/components/cards/modules/CustomModule.svelte -->
<script lang="ts">
import type { CustomModuleProps } from '../types';
let { customProp = 'default' }: CustomModuleProps = $props();
</script>
<div class="custom-module">
<!-- Dein Module-Inhalt -->
</div>
```
### 2. Types definieren
```typescript
// In types.ts hinzufügen
export interface CustomModuleProps {
customProp?: string;
// Weitere Props...
}
```
### 3. In BaseCard registrieren
```svelte
// In BaseCard.svelte
import CustomModule from './modules/CustomModule.svelte';
const moduleComponents = {
// ... andere Module
custom: CustomModule
};
```
## Module-Konfiguration
### Sichtbarkeit
```javascript
{
type: 'content',
visibility: 'desktop', // Nur auf Desktop anzeigen
props: { /* ... */ }
}
```
### Grid-Layout
```javascript
{
type: 'media',
grid: {
col: 1, // Spalte 1
row: 1, // Zeile 1
colSpan: 2, // Über 2 Spalten
rowSpan: 1 // Über 1 Zeile
},
props: { /* ... */ }
}
```
### Custom Styling
```javascript
{
type: 'header',
className: 'custom-header-class',
props: { /* ... */ }
}
```
## Best Practices
1. **Halte Module klein und fokussiert** - Ein Modul sollte nur eine Aufgabe erfüllen
2. **Verwende TypeScript** für Type-Safety
3. **Nutze Props sparsam** - Zu viele Optionen machen Module komplex
4. **Dokumentiere neue Module** ausführlich
5. **Teste Module isoliert** bevor du sie in Karten verwendest
6. **Beachte Barrierefreiheit** - ARIA-Labels, Keyboard-Navigation
7. **Optimiere Performance** - Lazy Loading für schwere Module

View file

@ -0,0 +1,551 @@
# Server-Side HTML Cards - Konzept & Analyse
## Executive Summary
Die Idee, Cards als reinen HTML/CSS-Code serverseitig zu rendern, bietet maximale Flexibilität für Nutzer und könnte das bestehende modulare System ergänzen oder ersetzen. Dieser Ansatz würde es ermöglichen, dass Nutzer komplett eigene Designs erstellen können, während gleichzeitig die Aspect Ratio und Container-Constraints eingehalten werden.
## 🎯 Konzept-Übersicht
### Grundidee
```html
<!-- Nutzer definiert HTML/CSS -->
<div class="custom-card">
<style>
.custom-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 2rem;
color: white;
}
.custom-card h2 {
font-size: 2rem;
}
</style>
<h2>{{username}}</h2>
<p>{{bio}}</p>
</div>
```
### Server rendert zu:
```html
<div class="card-container" style="aspect-ratio: 16/9;">
<iframe srcdoc="..." sandbox="allow-same-origin" style="width: 100%; height: 100%; border: none;">
<!-- Nutzer HTML/CSS isoliert in iframe -->
</iframe>
</div>
```
## 📊 Vergleich der Ansätze
| Aspekt | Modulares System (aktuell) | HTML/CSS System | Hybrid-Ansatz |
| ---------------------- | -------------------------- | --------------- | ------------- |
| **Flexibilität** | Mittel | Sehr hoch | Hoch |
| **Sicherheit** | Hoch | Niedrig-Mittel | Mittel-Hoch |
| **Performance** | Sehr gut | Gut | Gut |
| **Nutzer-Komplexität** | Niedrig | Hoch | Variabel |
| **Wartbarkeit** | Sehr gut | Schlecht | Mittel |
| **Datenbank** | Komplex | Einfach | Mittel |
## ✅ Vorteile Server-Side HTML Cards
### 1. **Maximale Kreativität**
- Nutzer können JEDES Design umsetzen
- Keine Einschränkungen durch vordefinierte Module
- Custom Animationen und Effekte möglich
- Einzigartige Layouts
### 2. **Einfachere Datenbank**
```sql
-- Statt komplexer JSON-Strukturen
CREATE TABLE cards (
id TEXT PRIMARY KEY,
user_id TEXT,
html_content TEXT,
css_content TEXT,
variables JSON, -- Für Template-Variablen
created_at TIMESTAMP
);
```
### 3. **Portabilität**
- HTML/CSS ist universell
- Kann in jede Plattform exportiert werden
- Keine Framework-Abhängigkeiten
### 4. **Learning Opportunity**
- Nutzer lernen HTML/CSS
- Community kann Code teilen
- Inspiration durch andere Designs
## ❌ Nachteile & Risiken
### 1. **Sicherheitsrisiken**
```javascript
// XSS-Gefahr
<script>alert('XSS')</script>
// CSS-Injection
<style>
body { display: none !important; }
</style>
// Clickjacking
<a href="javascript:void(0)" onclick="stealData()">
```
### 2. **Performance-Probleme**
- Unkontrollierte CSS-Animationen
- Große Bilder/Assets
- Ineffiziente Selektoren
- Memory Leaks durch JavaScript
### 3. **Responsive Design**
- Nutzer müssen selbst Media Queries schreiben
- Inkonsistente Mobile-Ansichten
- Aspect Ratio schwer zu garantieren
### 4. **Wartbarkeit**
- Kein Type-Checking
- Schwer zu debuggen
- Updates kompliziert
- Keine einheitliche Code-Qualität
## 🔒 Sicherheitskonzept
### 1. **Sandboxing mit iframes**
```html
<iframe
srcdoc="{{sanitized_html}}"
sandbox="allow-same-origin"
csp="default-src 'self'; script-src 'none';"
loading="lazy"
/>
```
### 2. **HTML/CSS Sanitization**
```javascript
import DOMPurify from 'isomorphic-dompurify';
import { sanitizeCSS } from 'css-tree';
function sanitizeCardContent(html, css) {
// HTML säubern
const cleanHTML = DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['div', 'span', 'p', 'h1', 'h2', 'h3', 'img', 'a'],
ALLOWED_ATTR: ['class', 'id', 'href', 'src', 'alt', 'style'],
FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'form'],
FORBID_ATTR: ['onclick', 'onload', 'onerror']
});
// CSS säubern
const cleanCSS = sanitizeCSS(css, {
removeImports: true,
removeJavaScript: true,
limitSelectors: true,
maxNesting: 3
});
return { html: cleanHTML, css: cleanCSS };
}
```
### 3. **Content Security Policy**
```javascript
// Server-Header
response.headers.set(
'Content-Security-Policy',
"default-src 'self'; " +
"style-src 'self' 'unsafe-inline'; " +
"script-src 'none'; " +
"img-src 'self' data: https:;"
);
```
## 🎨 Hybrid-Ansatz (EMPFEHLUNG)
### Konzept: "Progressive Enhancement"
```typescript
interface CardDefinition {
type: 'modular' | 'template' | 'custom-html';
// Für modulare Cards (wie bisher)
modules?: ModuleConfig[];
// Für Template-basierte Cards
template?: string;
templateVars?: Record<string, any>;
// Für Custom HTML
customHTML?: string;
customCSS?: string;
// Gemeinsame Properties
constraints?: {
aspectRatio?: string;
maxWidth?: string;
minHeight?: string;
};
}
```
### Drei Ebenen der Anpassung:
#### Level 1: Module Builder (Anfänger)
```javascript
{
type: 'modular',
modules: [
{ type: 'header', props: { title: 'Meine Karte' } }
]
}
```
#### Level 2: Template Editor (Fortgeschritten)
```handlebars
{ type: 'template', template: `
<div class='card-template'>
<h2>{{title}}</h2>
<p>{{description}}</p>
</div>
`, templateVars: { title: 'Titel', description: 'Text' } }
```
#### Level 3: Custom HTML/CSS (Experten)
```javascript
{
type: 'custom-html',
customHTML: '<div class="my-card">...</div>',
customCSS: '.my-card { ... }'
}
```
## 💻 Implementierungsplan
### Phase 1: Basis-Infrastruktur (Woche 1-2)
#### 1.1 Sanitization Layer
```typescript
// src/lib/services/cardSanitizer.ts
export class CardSanitizer {
sanitizeHTML(html: string): string;
sanitizeCSS(css: string): string;
validateConstraints(html: string, constraints: Constraints): boolean;
extractVariables(html: string): string[];
}
```
#### 1.2 Rendering Engine
```svelte
<!-- src/lib/components/cards/CustomHTMLCard.svelte -->
<script lang="ts">
import { sanitizeCard } from '$lib/services/cardSanitizer';
export let html: string;
export let css: string;
export let variables: Record<string, any> = {};
export let aspectRatio: string = '16/9';
$: sanitized = sanitizeCard(html, css);
$: rendered = replaceVariables(sanitized.html, variables);
</script>
<div class="custom-card-container" style="aspect-ratio: {aspectRatio}">
<iframe
srcdoc={`
<!DOCTYPE html>
<html>
<head>
<style>${sanitized.css}</style>
<style>
body { margin: 0; padding: 0; height: 100vh; display: flex; }
.card-content { flex: 1; }
</style>
</head>
<body>
<div class="card-content">${rendered}</div>
</body>
</html>
`}
sandbox="allow-same-origin"
loading="lazy"
title="Custom Card"
/>
</div>
```
### Phase 2: Editor & Preview (Woche 3-4)
#### 2.1 HTML/CSS Editor
```svelte
<!-- src/lib/components/builder/HTMLCardEditor.svelte -->
<script>
import CodeMirror from 'codemirror';
import 'codemirror/mode/htmlmixed/htmlmixed';
import 'codemirror/mode/css/css';
export let html = '';
export let css = '';
export let onUpdate;
// Live Preview
$: preview = generatePreview(html, css);
</script>
<div class="editor-container">
<div class="editors">
<div class="html-editor">
<CodeMirror bind:value={html} mode="htmlmixed" />
</div>
<div class="css-editor">
<CodeMirror bind:value={css} mode="css" />
</div>
</div>
<div class="preview">
<CustomHTMLCard {html} {css} />
</div>
</div>
```
#### 2.2 Template Variables
```typescript
interface TemplateVariable {
name: string;
type: 'text' | 'number' | 'image' | 'link' | 'list';
default?: any;
required?: boolean;
}
// Variable extraction
function extractVariables(html: string): TemplateVariable[] {
const regex = /\{\{(\w+)(?::(\w+))?\}\}/g;
const variables: TemplateVariable[] = [];
let match;
while ((match = regex.exec(html)) !== null) {
variables.push({
name: match[1],
type: match[2] || 'text',
required: true
});
}
return variables;
}
```
### Phase 3: Datenbank-Migration (Woche 5)
#### Neue Datenbank-Struktur
```sql
-- Erweiterte cards Tabelle
ALTER TABLE cards ADD COLUMN render_type TEXT DEFAULT 'modular';
ALTER TABLE cards ADD COLUMN html_content TEXT;
ALTER TABLE cards ADD COLUMN css_content TEXT;
ALTER TABLE cards ADD COLUMN template_variables JSON;
ALTER TABLE cards ADD COLUMN constraints JSON;
-- Versionierung für Custom Cards
CREATE TABLE card_versions (
id TEXT PRIMARY KEY,
card_id TEXT REFERENCES cards(id),
version INTEGER,
html_content TEXT,
css_content TEXT,
created_at TIMESTAMP,
change_note TEXT
);
```
### Phase 4: Builder Integration (Woche 6)
```svelte
<!-- Erweiterter Card Builder -->
<script>
let builderMode: 'visual' | 'template' | 'code' = 'visual';
</script>
<div class="builder-modes">
<button class:active={builderMode === 'visual'} on:click={() => (builderMode = 'visual')}>
Visual Builder (Module)
</button>
<button class:active={builderMode === 'template'} on:click={() => (builderMode = 'template')}>
Template Editor
</button>
<button class:active={builderMode === 'code'} on:click={() => (builderMode = 'code')}>
HTML/CSS Code
</button>
</div>
{#if builderMode === 'visual'}
<CardBuilder />
{:else if builderMode === 'template'}
<TemplateEditor />
{:else}
<HTMLCardEditor />
{/if}
```
## 🏗️ Technische Architektur
### Rendering Pipeline
```
User Input (HTML/CSS)
Sanitization Layer
Variable Replacement
Constraint Validation
iframe Sandboxing
Final Render
```
### Aspect Ratio Enforcement
```css
/* Container styles */
.card-container {
position: relative;
width: 100%;
aspect-ratio: var(--card-aspect-ratio, 16/9);
overflow: hidden;
}
.card-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
}
/* Inside iframe */
body {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
overflow: auto;
}
```
## 📈 Migration Strategy
### Schritt 1: Parallel-Betrieb
- Bestehendes System bleibt
- HTML-Cards als "Advanced Mode"
- Opt-in für Power-User
### Schritt 2: Feature Parity
- Card Builder unterstützt beide Modi
- Import/Export zwischen Formaten
- Template Converter
### Schritt 3: Unified System
```typescript
// Universeller Card Renderer
function renderCard(card: UnifiedCard): HTMLElement {
switch (card.type) {
case 'modular':
return renderModularCard(card);
case 'template':
return renderTemplateCard(card);
case 'custom-html':
return renderCustomHTMLCard(card);
}
}
```
## 🎯 Empfehlung
### Hybrid-Ansatz implementieren mit:
1. **Beibehaltung des modularen Systems** für 80% der Nutzer
2. **Template-System** für fortgeschrittene Anpassungen
3. **HTML/CSS-Editor** für Power-User und Entwickler
4. **Strikte Sandboxing** für Sicherheit
5. **Progressive Enhancement** - Nutzer können zwischen Modi wechseln
### Vorteile dieses Ansatzes:
- ✅ Backward Compatibility
- ✅ Verschiedene Skill-Level bedient
- ✅ Sicherheit gewährleistet
- ✅ Performance optimiert
- ✅ Wartbarkeit erhalten
### Timeline:
- **Woche 1-2**: Proof of Concept
- **Woche 3-4**: Editor & Tools
- **Woche 5**: Datenbank-Migration
- **Woche 6**: Integration
- **Woche 7-8**: Testing & Rollout
## 🚀 Nächste Schritte
1. **Proof of Concept** mit einfachem HTML-Renderer
2. **Sicherheitsaudit** der Sanitization
3. **Performance-Tests** mit komplexen Cards
4. **User Research** - Welcher Ansatz wird bevorzugt?
5. **Schrittweise Migration** beginnen
## 💡 Zusätzliche Überlegungen
### AI-Unterstützung
```typescript
// KI generiert HTML/CSS basierend auf Beschreibung
async function generateCardFromPrompt(prompt: string): Promise<CardHTML> {
const response = await ai.generate({
prompt: `Create a card with: ${prompt}`,
constraints: ['responsive', 'aspect-ratio: 16/9', 'no-javascript']
});
return sanitizeCard(response);
}
```
### Marketplace Evolution
- Verkauf von HTML/CSS Templates
- Code-Snippets Library
- Community Challenges
- Template Converter Tools
### Monitoring & Analytics
```typescript
// Track welcher Modus am meisten genutzt wird
analytics.track('card_created', {
type: 'modular' | 'template' | 'custom-html',
complexity: calculateComplexity(card),
render_time: measureRenderTime(card)
});
```

View file

@ -0,0 +1,458 @@
# Card Templates
## Übersicht
Templates sind vordefinierte Kartenkonfigurationen, die als Ausgangspunkt für eigene Karten dienen. Sie kombinieren Module, Layouts und Themes zu wiederverwendbaren Vorlagen.
## Template-Struktur
### CardTemplateConfig Interface
```typescript
interface CardTemplateConfig extends CardConfig {
name: string; // Template-Name
slug: string; // URL-freundlicher Name
description?: string; // Beschreibung
category?: string; // Kategorie
isPublic?: boolean; // Öffentlich verfügbar
previewImage?: string; // Vorschaubild-URL
}
```
## Vordefinierte Templates
### Profile Card Template
```javascript
const profileTemplate = {
name: 'Standard Profile',
slug: 'standard-profile',
description: 'Vollständige Profilkarte mit Avatar, Bio und Social Links',
category: 'profile',
variant: 'default',
modules: [
{
type: 'header',
order: 0,
props: {
title: '{{username}}',
subtitle: '{{bio}}',
avatar: '{{avatar}}'
}
},
{
type: 'stats',
order: 1,
props: {
stats: [
{ label: 'Links', value: '{{totalLinks}}', icon: '🔗' },
{ label: 'Clicks', value: '{{totalClicks}}', icon: '👆' }
]
}
},
{
type: 'links',
order: 2,
props: {
links: '{{socialLinks}}',
style: 'button',
showIcon: true
}
}
],
layout: {
padding: '1.5rem'
}
};
```
### Dashboard Stats Template
```javascript
const dashboardTemplate = {
name: 'Dashboard Stats',
slug: 'dashboard-stats',
description: 'Übersichtskarte mit wichtigen Metriken',
category: 'dashboard',
variant: 'compact',
modules: [
{
type: 'header',
props: {
title: 'Übersicht',
icon: '📊'
}
},
{
type: 'stats',
props: {
stats: [
{ label: 'Besucher', value: '{{visitors}}', change: '{{visitorChange}}' },
{ label: 'Umsatz', value: '{{revenue}}', change: '{{revenueChange}}' },
{ label: 'Conversion', value: '{{conversion}}', change: '{{conversionChange}}' }
],
layout: 'grid'
}
}
]
};
```
### Link Collection Template
```javascript
const linkCollectionTemplate = {
name: 'Link Collection',
slug: 'link-collection',
description: 'Sammlung von Links mit Beschreibungen',
category: 'links',
variant: 'default',
modules: [
{
type: 'header',
props: {
title: '{{collectionName}}',
subtitle: '{{collectionDescription}}'
}
},
{
type: 'links',
props: {
links: '{{links}}',
style: 'card',
columns: 2,
showDescription: true,
showIcon: true
}
}
]
};
```
### Media Gallery Template
```javascript
const mediaGalleryTemplate = {
name: 'Media Gallery',
slug: 'media-gallery',
description: 'Bildergalerie mit Titel und Beschreibung',
category: 'media',
variant: 'minimal',
modules: [
{
type: 'header',
props: {
title: '{{galleryTitle}}'
}
},
{
type: 'media',
props: {
type: 'image',
src: '{{featuredImage}}',
aspectRatio: '16/9'
}
},
{
type: 'content',
props: {
text: '{{description}}'
}
}
]
};
```
## Template Store
Der Template Store ist ein Marktplatz für Card Templates.
### Features
- **Browse & Filter**: Nach Kategorie, Tags, Beliebtheit
- **Preview**: Live-Vorschau mit eigenen Daten
- **Download**: Templates herunterladen und anpassen
- **Rating**: Bewertung durch Community
- **Sharing**: Eigene Templates teilen
### Template Store verwenden
```svelte
<script>
import { cardTemplateService } from '$lib/services/cardTemplates';
// Öffentliche Templates laden
const templates = await cardTemplateService.getPublicTemplates();
// Nach Kategorie filtern
const profileTemplates = await cardTemplateService.getPublicTemplates('profile');
// Template verwenden
async function useTemplate(template) {
await cardTemplateService.incrementDownloads(template.id);
// Template in Card Builder öffnen
goto(`/card-builder?template=${template.id}`);
}
</script>
```
## Templates verwalten
### Template erstellen
```javascript
const newTemplate = await cardTemplateService.createTemplate({
name: 'Mein Template',
slug: 'mein-template',
description: 'Beschreibung',
category: 'custom',
is_public: false,
modules: [
/* ... */
],
layout: {
/* ... */
}
});
```
### Template aktualisieren
```javascript
await cardTemplateService.updateTemplate(templateId, {
name: 'Neuer Name',
modules: updatedModules
});
```
### Template löschen
```javascript
await cardTemplateService.deleteTemplate(templateId);
```
## Template-Variablen
Templates können Platzhalter verwenden, die bei der Verwendung ersetzt werden:
```javascript
{
type: 'header',
props: {
title: '{{username}}', // Wird durch tatsächlichen Username ersetzt
subtitle: '{{bio}}' // Wird durch Bio ersetzt
}
}
```
### Verfügbare Variablen
- `{{username}}` - Benutzername
- `{{email}}` - E-Mail-Adresse
- `{{bio}}` - Biografie
- `{{avatar}}` - Avatar-URL
- `{{totalLinks}}` - Anzahl Links
- `{{totalClicks}}` - Anzahl Klicks
- `{{socialLinks}}` - Array von Social Links
- Custom-Variablen je nach Kontext
## Template zu Card konvertieren
```javascript
// Template laden
const template = await cardTemplateService.getTemplate(templateId);
// Zu CardConfig konvertieren
const cardConfig = cardTemplateService.templateToCardConfig(template);
// Mit eigenen Daten füllen
const filledConfig = fillTemplateVariables(cardConfig, {
username: 'John Doe',
bio: 'Software Developer'
// ...
});
// Card rendern
<BaseCard {...filledConfig} />;
```
## Template-Kategorien
### Profile
- Benutzerprofile
- Team-Mitglieder
- Autoren-Karten
### Dashboard
- Statistik-Übersichten
- KPI-Karten
- Activity-Feeds
### Links
- Link-Sammlungen
- Social Media Links
- Ressourcen-Listen
### Media
- Bildergalerien
- Video-Player
- QR-Code-Karten
### Content
- Blog-Post-Karten
- Produkt-Karten
- Service-Karten
### Custom
- Benutzerdefinierte Templates
- Spezial-Layouts
## Template-Service
### Methoden
```typescript
class CardTemplateService {
// Templates abrufen
async getPublicTemplates(category?: string): Promise<DBCardTemplate[]>;
async getTemplate(id: string): Promise<DBCardTemplate | null>;
// Templates verwalten
async createTemplate(template: Partial<DBCardTemplate>): Promise<DBCardTemplate | null>;
async updateTemplate(
id: string,
updates: Partial<DBCardTemplate>
): Promise<DBCardTemplate | null>;
async deleteTemplate(id: string): Promise<boolean>;
// Konvertierung
templateToCardConfig(template: DBCardTemplate): CardConfig;
// Statistiken
async incrementDownloads(templateId: string): Promise<void>;
async rateTemplate(templateId: string, rating: number): Promise<void>;
}
```
## Eigene Templates erstellen
### 1. Template definieren
```javascript
const myTemplate = {
name: 'Mein Custom Template',
slug: 'mein-custom-template',
description: 'Ein spezielles Template für meinen Use Case',
category: 'custom',
modules: [
// Module hier definieren
],
layout: {
columns: 2,
gap: '1rem',
padding: '1.5rem'
},
theme: {
// Optional: Custom Theme
}
};
```
### 2. Template speichern
```javascript
// In Datenbank
const saved = await cardTemplateService.createTemplate(myTemplate);
// Oder lokal verwenden
localStorage.setItem('myTemplate', JSON.stringify(myTemplate));
```
### 3. Template teilen
```javascript
// Öffentlich machen
await cardTemplateService.updateTemplate(templateId, {
is_public: true
});
// Export als JSON
const json = JSON.stringify(template, null, 2);
downloadAsFile('template.json', json);
```
## Template-Migration
### Von statischen Komponenten
```svelte
<!-- Alt: Statische Komponente -->
<div class="profile-card">
<h2>{user.name}</h2>
<p>{user.bio}</p>
</div>
<!-- Neu: Template-basiert -->
<BaseCard template="profile-basic" data={user} />
```
## Best Practices
1. **Modularität**: Erstelle kleine, wiederverwendbare Templates
2. **Flexibilität**: Verwende Variablen für dynamische Inhalte
3. **Kategorisierung**: Ordne Templates sinnvollen Kategorien zu
4. **Dokumentation**: Beschreibe Templates ausführlich
5. **Vorschau**: Stelle Vorschaubilder bereit
6. **Versionierung**: Versioniere Templates bei Änderungen
7. **Testing**: Teste Templates mit verschiedenen Datensätzen
## Template-Beispiele
### Minimal Profile
```javascript
{
name: 'Minimal Profile',
modules: [{
type: 'header',
props: { title: '{{name}}' }
}]
}
```
### Social Media Hub
```javascript
{
name: 'Social Media Hub',
modules: [{
type: 'links',
props: {
links: '{{socialLinks}}',
style: 'button',
columns: 2
}
}]
}
```
### Stats Dashboard
```javascript
{
name: 'Stats Dashboard',
modules: [{
type: 'stats',
props: {
stats: '{{metrics}}',
layout: 'grid'
}
}]
}
```

View file

@ -0,0 +1,420 @@
# Card System Themes
## Übersicht
Themes definieren das visuelle Erscheinungsbild von Karten. Sie umfassen Farben, Typografie, Abstände, Schatten und Animationen.
## Theme-Struktur
### ThemeConfig Interface
```typescript
interface ThemeConfig {
id?: string;
name?: string;
colors?: {
primary?: string; // Primärfarbe
secondary?: string; // Sekundärfarbe
accent?: string; // Akzentfarbe
background?: string; // Hintergrundfarbe
surface?: string; // Oberflächenfarbe
text?: string; // Textfarbe
textMuted?: string; // Gedämpfte Textfarbe
border?: string; // Rahmenfarbe
hover?: string; // Hover-Farbe
// Weitere custom Farben möglich
};
typography?: {
fontFamily?: string;
fontSize?: {
xs?: string;
sm?: string;
md?: string;
lg?: string;
xl?: string;
};
fontWeight?: {
light?: number;
normal?: number;
medium?: number;
semibold?: number;
bold?: number;
};
lineHeight?: {
tight?: string;
normal?: string;
relaxed?: string;
};
};
spacing?: {
xs?: string;
sm?: string;
md?: string;
lg?: string;
xl?: string;
};
borderRadius?: {
none?: string;
sm?: string;
md?: string;
lg?: string;
xl?: string;
full?: string;
};
shadows?: {
none?: string;
sm?: string;
md?: string;
lg?: string;
xl?: string;
};
}
```
## Vordefinierte Themes
### Default Theme
```javascript
const defaultTheme = {
name: 'Default',
colors: {
primary: '#3b82f6',
secondary: '#8b5cf6',
accent: '#ec4899',
background: '#ffffff',
surface: '#f9fafb',
text: '#111827',
textMuted: '#6b7280',
border: '#e5e7eb',
hover: '#f3f4f6'
},
typography: {
fontFamily: 'Inter, sans-serif',
fontSize: {
xs: '0.75rem',
sm: '0.875rem',
md: '1rem',
lg: '1.125rem',
xl: '1.25rem'
}
},
spacing: {
xs: '0.25rem',
sm: '0.5rem',
md: '1rem',
lg: '1.5rem',
xl: '2rem'
},
borderRadius: {
sm: '0.25rem',
md: '0.5rem',
lg: '0.75rem',
xl: '1rem',
full: '9999px'
}
};
```
### Dark Theme
```javascript
const darkTheme = {
name: 'Dark',
colors: {
primary: '#60a5fa',
secondary: '#a78bfa',
accent: '#f472b6',
background: '#111827',
surface: '#1f2937',
text: '#f9fafb',
textMuted: '#9ca3af',
border: '#374151',
hover: '#374151'
}
// ... weitere Eigenschaften
};
```
### Minimal Theme
```javascript
const minimalTheme = {
name: 'Minimal',
colors: {
primary: '#000000',
secondary: '#666666',
accent: '#000000',
background: '#ffffff',
surface: '#ffffff',
text: '#000000',
textMuted: '#666666',
border: '#e0e0e0',
hover: '#f5f5f5'
},
typography: {
fontFamily: 'Helvetica, Arial, sans-serif'
},
borderRadius: {
sm: '0',
md: '0',
lg: '0',
xl: '0'
},
shadows: {
sm: 'none',
md: 'none',
lg: 'none'
}
};
```
## Theme verwenden
### Mit ThemeProvider
```svelte
<script>
import ThemeProvider from '$lib/components/cards/ThemeProvider.svelte';
import BaseCard from '$lib/components/cards/BaseCard.svelte';
const myTheme = {
colors: {
primary: '#ff6b6b'
// ...
}
};
</script>
<ThemeProvider theme={myTheme}>
<BaseCard {...cardConfig} />
</ThemeProvider>
```
### Direkt an BaseCard
```svelte
<BaseCard theme={myTheme} variant="default" {modules} />
```
## Theme-Editor
Der Theme-Editor ermöglicht die visuelle Erstellung und Anpassung von Themes.
### Features
- **Live-Preview**: Änderungen sofort sichtbar
- **Color-Picker**: Farben visuell auswählen
- **Typography-Controls**: Schriftarten und Größen anpassen
- **Export/Import**: Themes als JSON ex-/importieren
- **Speichern**: In Datenbank speichern
### Verwendung
```svelte
<script>
import ThemeEditor from '$lib/components/theme/ThemeEditor.svelte';
</script>
<ThemeEditor initialTheme={currentTheme} onSave={(theme) => saveTheme(theme)} showPreview={true} />
```
## CSS-Variablen
Themes werden als CSS-Variablen im DOM gesetzt:
```css
:root {
--theme-primary: #3b82f6;
--theme-secondary: #8b5cf6;
--theme-accent: #ec4899;
--theme-background: #ffffff;
--theme-surface: #f9fafb;
--theme-text: #111827;
--theme-text-muted: #6b7280;
--theme-border: #e5e7eb;
--theme-hover: #f3f4f6;
}
```
### In Komponenten verwenden
```svelte
<style>
.my-component {
color: var(--theme-text);
background: var(--theme-background);
border: 1px solid var(--theme-border);
}
.my-component:hover {
background: var(--theme-hover);
}
</style>
```
## Theme Store
### Datenbank-Schema
```javascript
{
id: string,
name: string,
slug: string,
description: string,
author: string,
is_public: boolean,
is_premium: boolean,
price: number,
colors: object,
typography: object,
spacing: object,
borderRadius: object,
shadows: object,
animations: object,
downloads: number,
rating: number,
created: datetime,
updated: datetime
}
```
### Theme-Service Methoden
```typescript
// Öffentliche Themes laden
const themes = await cardTemplateService.getPublicThemes();
// Spezifisches Theme laden
const theme = await cardTemplateService.getTheme(themeId);
// Neues Theme erstellen
const newTheme = await cardTemplateService.createTheme({
name: 'My Theme',
colors: {
/* ... */
}
});
// Theme aktualisieren
const updated = await cardTemplateService.updateTheme(themeId, {
colors: { primary: '#ff0000' }
});
```
## Theme-Kategorien
### Business
- Professionell
- Seriös
- Klare Linien
- Gedeckte Farben
### Creative
- Bunt
- Verspielt
- Gradients
- Animationen
### Minimal
- Reduziert
- Viel Weißraum
- Keine Schatten
- Monochrom
### Dark Mode
- Dunkle Hintergründe
- Helle Texte
- Reduzierte Kontraste
- Augenschonend
## Eigene Themes erstellen
### 1. Theme-Objekt definieren
```javascript
const myCustomTheme = {
name: 'Ocean Blue',
colors: {
primary: '#006994',
secondary: '#00a8cc',
accent: '#fafafa',
background: '#f0f9ff',
surface: '#e0f2fe',
text: '#0c4a6e',
textMuted: '#0284c7',
border: '#bae6fd',
hover: '#dbeafe'
},
typography: {
fontFamily: '"Segoe UI", Tahoma, Geneva, Verdana, sans-serif'
},
borderRadius: {
sm: '0.5rem',
md: '0.75rem',
lg: '1rem',
xl: '1.5rem'
},
shadows: {
sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
md: '0 4px 6px -1px rgb(0 0 0 / 0.1)',
lg: '0 10px 15px -3px rgb(0 0 0 / 0.1)'
}
};
```
### 2. Theme registrieren
```javascript
// In Datenbank speichern
const saved = await cardTemplateService.createTheme(myCustomTheme);
// Oder lokal verwenden
<ThemeProvider theme={myCustomTheme}>
<!-- Karten hier -->
</ThemeProvider>
```
### 3. Theme testen
- Verschiedene Kartenvarianten
- Alle Module-Typen
- Light/Dark Umgebungen
- Verschiedene Bildschirmgrößen
## Best Practices
1. **Konsistenz**: Verwende ein einheitliches Farbschema
2. **Kontrast**: Stelle sicher, dass Text gut lesbar ist
3. **Hierarchie**: Nutze Farben zur visuellen Hierarchie
4. **Zugänglichkeit**: Beachte WCAG-Richtlinien für Kontraste
5. **Performance**: Vermeide zu viele CSS-Variablen
6. **Responsive**: Teste auf verschiedenen Geräten
7. **Dokumentation**: Dokumentiere Custom-Themes ausführlich
## Theme-Migration
### Von Tailwind zu Theme
```javascript
// Tailwind-Klassen
<div class="bg-blue-500 text-white p-4">
// Theme-System
<div style="background: var(--theme-primary); color: var(--theme-text); padding: var(--theme-spacing-md);">
```
### Theme-Versionierung
```javascript
const theme = {
name: 'My Theme',
version: '1.2.0' // Semantic Versioning
// ...
};
```

View file

@ -0,0 +1,278 @@
# Unified Cards - Datenbank Migration Guide
## PocketBase Collection Schema
### Neue Collection: `unified_cards`
```javascript
// Collection Konfiguration
{
name: 'unified_cards',
type: 'base',
schema: [
{
name: 'user_id',
type: 'relation',
required: true,
options: {
collectionId: 'users',
cascadeDelete: true
}
},
{
name: 'render_mode',
type: 'select',
required: true,
options: {
values: ['beginner', 'advanced', 'expert']
}
},
// Modular Mode Fields
{
name: 'modules',
type: 'json',
required: false
},
{
name: 'theme_id',
type: 'relation',
required: false,
options: {
collectionId: 'themes'
}
},
// Template Mode Fields
{
name: 'template',
type: 'text',
required: false
},
{
name: 'template_css',
type: 'text',
required: false
},
{
name: 'template_variables',
type: 'json',
required: false
},
{
name: 'template_values',
type: 'json',
required: false
},
// Custom HTML Mode Fields
{
name: 'custom_html',
type: 'text',
required: false
},
{
name: 'custom_css',
type: 'text',
required: false
},
{
name: 'custom_js',
type: 'text',
required: false
},
// Common Fields
{
name: 'variant',
type: 'select',
required: false,
options: {
values: ['default', 'compact', 'hero', 'minimal', 'glass', 'gradient']
}
},
{
name: 'constraints',
type: 'json',
required: false
},
{
name: 'metadata',
type: 'json',
required: false
},
{
name: 'page',
type: 'text',
required: false
},
{
name: 'position',
type: 'number',
required: false
},
{
name: 'is_active',
type: 'bool',
required: false,
options: {
default: true
}
},
{
name: 'is_public',
type: 'bool',
required: false,
options: {
default: false
}
}
]
}
```
## Migration Script
### 1. Erstelle die Collection in PocketBase Admin
1. Gehe zu PocketBase Admin UI
2. Klicke auf "New Collection"
3. Füge die Felder wie oben beschrieben hinzu
4. Setze die API Rules:
- List/View: `@request.auth.id = user_id || is_public = true`
- Create: `@request.auth.id != ""`
- Update: `@request.auth.id = user_id`
- Delete: `@request.auth.id = user_id`
### 2. Migriere bestehende Cards
```javascript
// Migration Script (in PocketBase Admin Console ausführen)
// ODER als separates Node.js Script
async function migrateExistingCards() {
// Hole alle existierenden user_cards
const oldCards = await pb.collection('user_cards').getFullList();
for (const oldCard of oldCards) {
const unifiedCard = {
user_id: oldCard.user_id,
render_mode: 'beginner', // Alle alten Cards sind modular
modules: oldCard.custom_config?.modules || [],
theme_id: oldCard.theme_id,
variant: oldCard.custom_config?.variant || 'default',
constraints: {
aspectRatio: '16/9'
},
metadata: {
name: oldCard.template_id || 'Migrated Card',
created: oldCard.created,
updated: oldCard.updated,
migrated_from: oldCard.id
},
page: oldCard.page,
position: oldCard.position,
is_active: oldCard.is_active
};
try {
await pb.collection('unified_cards').create(unifiedCard);
console.log(`Migrated card ${oldCard.id}`);
} catch (error) {
console.error(`Failed to migrate card ${oldCard.id}:`, error);
}
}
}
```
## Verwendung im Code
### Card erstellen/laden
```svelte
<script>
import { unifiedCardService } from '$lib/services/unifiedCardService';
import UnifiedCard from '$lib/components/cards/UnifiedCard.svelte';
// Neue Card erstellen
const newCard = {
renderMode: 'beginner',
modularConfig: {
modules: [{ type: 'header', props: { title: 'My Card' } }]
}
};
const cardId = await unifiedCardService.saveCard(newCard);
// Card laden
const loadedCard = await unifiedCardService.loadCard(cardId);
</script>
<!-- Card anzeigen -->
<UnifiedCard card={loadedCard} />
```
### Builder verwenden
```svelte
<script>
import UnifiedCardBuilder from '$lib/components/builder/UnifiedCardBuilder.svelte';
async function handleSave(card) {
const id = await unifiedCardService.saveCard(card);
if (id) {
console.log('Card saved:', id);
}
}
</script>
<UnifiedCardBuilder onSave={handleSave} onCancel={() => history.back()} />
```
## Backward Compatibility
Die alten `user_cards` und Module funktionieren weiterhin:
```svelte
<!-- Alt (funktioniert weiter) -->
<BaseCard {modules} />
<!-- Neu (unified) -->
<UnifiedCard card={unifiedCard} />
```
## Rollout Plan
### Phase 1: Parallel-Betrieb (Woche 1-2)
- Neue unified_cards Collection erstellen
- UnifiedCard Komponenten deployen
- Keine Breaking Changes
### Phase 2: Soft Migration (Woche 3-4)
- Neuer Builder als "Beta Feature"
- User können zwischen altem und neuem Builder wählen
- Automatische Migration bei erstem Speichern
### Phase 3: Full Migration (Woche 5-6)
- Alle Cards auf unified_cards migrieren
- Alter Builder deprecated
- user_cards Collection als Read-Only
### Phase 4: Cleanup (Woche 7-8)
- Alte Collections entfernen
- Code Cleanup
- Dokumentation finalisieren
## Testing Checklist
- [ ] Modular Cards (Beginner Mode) funktionieren
- [ ] Template Cards (Advanced Mode) funktionieren
- [ ] Custom HTML Cards (Expert Mode) funktionieren
- [ ] Migration von alten Cards
- [ ] Sicherheit: XSS Prevention
- [ ] Sicherheit: CSS Injection Prevention
- [ ] Performance: Große Cards
- [ ] Responsive Design
- [ ] Browser Kompatibilität
- [ ] Import/Export
- [ ] Converter zwischen Modi