XyNaP.crm — WYSIWYG Page-Designer, Theming & UI Components¶
Status: Konzept v1.0 (2026-03-06) Abhaengigkeit: modulares-produktsystem.md (Phase 2: CRM Core) Ziel: CRM mit echtem WYSIWYG In-Page-Designer — direkt auf der Live-Seite designen, Preview und Publish
1. Vision¶
Der XyNaP.crm Designer ist kein separater Editor — er IST die Seite. Der Nutzer arbeitet direkt auf der Live-Ansicht, kann Widgets hinzufuegen, verschieben, konfigurieren und sieht sofort das Ergebnis. Aendern. Sehen. Publishen.
┌──────────────────────────────────────────────────────────────────┐
│ WYSIWYG Page-Designer │
│ │
│ ┌─ Toolbar ──────────────────────────────────────────────────┐ │
│ │ [Bearbeiten] [Vorschau] [Publish] 💻 📱 [Undo] [Redo] │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ Live-Page (WYSIWYG) ─────────────────────────────────────┐ │
│ │ │ │
│ │ Design direkt auf der Seite. │ │
│ │ Klicke auf ein Widget → Inline-Config erscheint. │ │
│ │ Ziehe neue Widgets aus dem [+] Panel. │ │
│ │ Verschiebe per Drag & Drop. │ │
│ │ Resize per Edge-Handles. │ │
│ │ │ │
│ │ Was du siehst = was publisht wird. │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ Context Panel (rechts, ausklappbar) ──────────────────────┐ │
│ │ Widget-Einstellungen │ Theme │ Seiten-Settings │ │
│ └────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
Kern-Prinzipien:¶
- In-Place Editing — Kein separater "Designer-Modus". Die Live-Seite wird direkt bearbeitet.
- Preview = Reality — Was im Designer zu sehen ist, ist exakt das Published-Ergebnis.
- Publish-Workflow — Aenderungen sind erst nach explizitem "Publish" fuer alle sichtbar (Draft/Published).
- Responsive Live — Zwischen Desktop/Tablet/Mobile umschalten — direkt im Designer.
- Zero Code — Kein HTML/CSS/JS noetig. Alles visuell.
2. Page-Architektur¶
2.1 Seitentypen¶
| Typ | Beschreibung | Beispiele |
|---|---|---|
| dashboard | Uebersichtsseiten mit KPIs, Charts, Listen | Sales Dashboard, Support Dashboard, CEO View |
| detail | Entity-Detail mit dynamischen Feldern | Kontakt-Seite, Deal-Seite, Ticket-Seite |
| list | Gefilterte Listen/Tabellen | Offene Deals, Meine Aufgaben, Alle Kontakte |
| report | Auswertungen mit Datenquellen + Filter | MRR Report, Pipeline Report, Team Performance |
| form | Formulare (intern + extern) | Lead-Erfassung, Kontakt-Formular, Umfrage |
| custom | Freie Seiten | Onboarding-Wizard, Willkommens-Seite |
2.2 Page-Datenmodell¶
interface Page {
id: string
tenant_id: string
name: string // "Sales Dashboard"
slug: string // "sales-dashboard"
type: PageType
icon?: string // Lucide Icon fuer Navigation
entity_type?: string // Fuer detail/list: "deal", "contact", "ticket"
// Versioning
draft: PageContent // Aktueller Entwurf (wird im Designer bearbeitet)
published: PageContent | null // Veroeffentlichte Version (null = noch nie publisht)
published_at: string | null
published_by: string | null
// Meta
is_system: boolean // System-Seiten (nicht loeschbar)
is_default: boolean // Standard-Seite fuer diesen Typ
is_public: boolean // Fuer alle Tenant-User sichtbar
owner_id: string
permissions: string[] // Welche Rollen diese Seite sehen
sort_order: number // Position in der Navigation
// Navigation
nav_section?: string // "crm", "support", "reports", null (versteckt)
nav_parent_id?: string // Fuer verschachtelte Navigation
created_at: string
updated_at: string
}
interface PageContent {
version: number
rows: Row[]
settings: PageSettings
theme_overrides?: Partial<ThemeOverrides> // Seitenspezifische Theme-Anpassungen
}
interface PageSettings {
max_width: string // "1440px" | "100%" | "1200px"
padding: string // "24px"
row_gap: string // "24px"
background?: string // Seiten-Hintergrund (ueberschreibt Theme)
auto_refresh?: number // Sekunden (0 = aus)
filters?: PageFilter[] // Globale Seitenfilter (Zeitraum, Team, etc.)
}
interface PageFilter {
id: string
label: string
type: 'date_range' | 'select' | 'user' | 'pipeline' | 'custom'
options?: any
default_value?: any
affects_widgets: string[] // Widget-IDs die auf diesen Filter reagieren
}
2.3 Row & Cell Modell¶
interface Row {
id: string
order: number
cells: Cell[]
// Layout
columns: number // Spaltenanzahl: 1-12 (CSS Grid)
gap: string // "16px"
align_items: 'stretch' | 'start' | 'center' | 'end'
min_height?: string
max_height?: string
// Optik
background?: string
padding?: string
margin_top?: string
border_bottom?: boolean
// Responsive
responsive: {
desktop: { columns: number; visible: boolean }
tablet: { columns: number; visible: boolean }
mobile: { columns: number; visible: boolean }
}
// Bedingung (optional)
visible_condition?: {
field: string // "user.role", "page.filter.period"
operator: 'eq' | 'neq' | 'in' | 'gt' | 'lt'
value: any
}
}
interface Cell {
id: string
order: number
// Was wird angezeigt
widget_type: string // "kpi-number", "deal-kanban", "chart-bar", etc.
widget_config: Record<string, any> // Widget-spezifische Konfiguration
// Grid-Position (responsive)
span: {
desktop: number // 1-12 Spalten
tablet: number
mobile: number
}
offset?: {
desktop: number // 0-11 Spalten Einrueckung
tablet: number
mobile: number
}
// Optik
title?: string
show_title: boolean
card_style: 'elevated' | 'outlined' | 'flat' | 'none'
padding?: string
min_height?: string
max_height?: string
overflow: 'visible' | 'scroll' | 'hidden'
// Verhalten
collapsible: boolean // Einklappbar
collapsed_default: boolean
loading_skeleton: boolean // Skeleton-Loader waehrend Daten laden
refresh_interval?: number // Widget-spezifischer Refresh (Sekunden)
// Bedingung
visible_condition?: {
field: string
operator: string
value: any
}
}
3. WYSIWYG Designer — UX im Detail¶
3.1 Modi¶
┌─────────────────────────────────────────────────────────────────┐
│ │
│ [Bearbeiten] [Vorschau] [Publish ▾] │
│ ═══════════ │
│ Aktiver Modus │
│ │
│ Bearbeiten: Widgets auswaehlen, konfigurieren, verschieben │
│ Inline-Toolbar bei Hover, Drag-Handles sichtbar │
│ Click → Config-Panel oeffnet rechts │
│ │
│ Vorschau: Exakt wie fuer Endnutzer (keine Handles/Toolbar) │
│ Responsive-Toggle aktiv (Desktop/Tablet/Mobile) │
│ │
│ Publish: ▾ Dropdown: │
│ - "Jetzt veroeffentlichen" │
│ - "Zeitgesteuert" (Datum/Uhrzeit) │
│ - "Entwurf speichern" │
│ - "Aenderungen verwerfen" │
│ - "Versionshistorie" │
│ │
└─────────────────────────────────────────────────────────────────┘
3.2 In-Page Editing Flow¶
User oeffnet CRM-Seite (z.B. "Sales Dashboard")
│
▼
┌─ Normal-Ansicht (Published) ────────────────────────┐
│ │
│ Seite wird gerendert wie fuer alle User. │
│ Oben rechts: kleiner [✏ Bearbeiten] Button │
│ (nur fuer User mit "page.edit" Permission) │
│ │
└──────────────────────────────────────────────────────┘
│ Klick auf "Bearbeiten"
▼
┌─ Edit-Modus (WYSIWYG) ─────────────────────────────┐
│ │
│ ┌─ Floating Toolbar (oben) ──────────────────────┐ │
│ │ [+ Widget] [Undo] [Redo] 💻 📱 [Vorschau] │ │
│ │ [Speichern] [Publish] [✕ Schliessen] │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ Jedes Widget bekommt bei Hover: │
│ ┌────────────────────────────────────┐ │
│ │ ↕ Drag-Handle │ ⚙ Config │ ✕ │ ← Inline │
│ ├────────────────────────────────────┤ │
│ │ │ │
│ │ Widget-Inhalt (LIVE Daten) │ │
│ │ │ │
│ ├────────────────────────────────────┤ │
│ │ ←─────── Resize Handle ──────────→ │ ← Breite │
│ └────────────────────────────────────┘ │
│ │
│ Zwischen Rows: [+ Row hier einfuegen] Linie │
│ │
│ Klick auf Widget → Config-Panel klappt rechts auf: │
│ ┌──────────────────┐ │
│ │ Widget-Settings │ │
│ │ Datenquelle │ │
│ │ Darstellung │ │
│ │ Responsive │ │
│ │ Bedingungen │ │
│ └──────────────────┘ │
│ │
└──────────────────────────────────────────────────────┘
│ Klick auf "Publish"
▼
┌─ Publish-Dialog ────────────────────────────────────┐
│ │
│ Aenderungen seit letztem Publish: │
│ - 2 Widgets hinzugefuegt │
│ - 1 Widget verschoben │
│ - Theme-Farbe geaendert │
│ │
│ [Jetzt veroeffentlichen] [Abbrechen] │
│ │
└──────────────────────────────────────────────────────┘
3.3 Widget hinzufuegen (+ Panel)¶
Klick auf [+ Widget] oeffnet ein Overlay-Panel (kein separater Screen):
┌─ Widget hinzufuegen ────────────────────────────────────┐
│ │
│ 🔍 [Suche...] │
│ │
│ ── Kennzahlen (KPIs) ───────────────────────────────── │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ # │ │ ↗ │ │ ± │ │ ○ │ │
│ │ Zahl │ │Trend │ │Vgl. │ │Gauge │ │
│ └──────┘ └──────┘ └──────┘ └──────┘ │
│ │
│ ── Pipeline & Deals ────────────────────────────────── │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ ▯▯▯ │ │ ▽ │ │ ☰ │ │
│ │Kanban│ │Funnel│ │Deals │ │
│ └──────┘ └──────┘ └──────┘ │
│ │
│ ── Listen & Tabellen ───────────────────────────────── │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ ☰ │ │ ☰ │ │ ☰ │ │ ▤ │ │ ✓ │ │
│ │Kont. │ │Ticket│ │Aktiv.│ │Recent│ │Tasks │ │
│ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ │
│ │
│ ── Diagramme ───────────────────────────────────────── │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ ▌▌▌ │ │ ╱ │ │ ◔ │ │ 📊 │ │
│ │ Bar │ │ Line │ │Donut │ │Fore- │ │
│ │ │ │ │ │ │ │cast │ │
│ └──────┘ └──────┘ └──────┘ └──────┘ │
│ │
│ ── Detail-Felder ───────────────────────────────────── │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ 👤 │ │ ▦ │ │ 🔗 │ │ 📝 │ │
│ │Header│ │Felder│ │Relat.│ │Notes │ │
│ └──────┘ └──────┘ └──────┘ └──────┘ │
│ │
│ ── Sonstiges ───────────────────────────────────────── │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ ⚡ │ │ 🏆 │ │ 📅 │ │ </> │ │ ─ │ │
│ │Quick │ │Leader│ │Kalen.│ │HTML │ │Spacer│ │
│ │Act. │ │board │ │ │ │ │ │ │ │
│ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ │
│ │
│ Klicke oder ziehe ein Widget auf die Seite. │
└─────────────────────────────────────────────────────────┘
Workflow: Widget anklicken → wird an Ende der Seite eingefuegt → sofort per Drag an gewuenschte Position ziehen → Config-Panel oeffnet sich automatisch.
3.4 Inline-Konfiguration¶
Statt eines separaten Config-Screens oeffnet sich ein Slide-Over Panel rechts:
┌─ Widget-Einstellungen ────── [✕] ──┐
│ │
│ ┌─ Tabs ──────────────────────────┐ │
│ │ [Daten] [Darstellung] [Erweit.] │ │
│ └─────────────────────────────────┘ │
│ │
│ ── Tab: Daten ──────────────────── │
│ │
│ Datenquelle │
│ [Pipeline: Sales ▾] │
│ │
│ Filter │
│ Status: [● Offen ○ Gewonnen ○ Alle]│
│ Zeitraum: [🔗 Seitenfilter ▾] │
│ │
│ Sortierung │
│ [Wert ▾] [Absteigend ▾] │
│ │
│ Limit: [20] │
│ │
│ ── Tab: Darstellung ────────────── │
│ │
│ Titel: [Pipeline-Uebersicht] │
│ Titel anzeigen: [✓] │
│ │
│ Card-Stil: │
│ [● Erhoeht ○ Umrandet ○ Flach] │
│ │
│ Breite: │
│ 💻 Desktop: [═══════8═══════] 8/12 │
│ 📱 Tablet: [═══════════12══] 12/12 │
│ 📱 Mobile: [═══════════12══] 12/12 │
│ │
│ Hoehe: [Auto ▾] │
│ Padding: [16px] │
│ │
│ ── Tab: Erweitert ──────────────── │
│ │
│ Einklappbar: [✓] │
│ Eingeklappt bei Start: [ ] │
│ Auto-Refresh: [30s ▾] │
│ Skeleton-Loader: [✓] │
│ │
│ Sichtbarkeit: │
│ [Immer ▾] │
│ Oder: Bedingung definieren... │
│ │
└──────────────────────────────────────┘
3.5 Responsive Design im Designer¶
┌─ Toolbar ─────────────────────────────────────────────┐
│ │
│ Viewport: [💻 Desktop] [📱 Tablet] [📱 Mobile] │
│ ═══════════ │
│ │
│ Bei Viewport-Wechsel: │
│ - Canvas-Breite aendert sich (1440 → 768 → 375px) │
│ - Grid passt sich an (span-Werte pro Breakpoint) │
│ - Versteckte Rows/Cells werden ausgegraut │
│ - Aenderungen gelten NUR fuer den aktiven Breakpoint │
│ │
└────────────────────────────────────────────────────────┘
Desktop (1440px):
┌──3──┐ ┌──3──┐ ┌──3──┐ ┌──3──┐
│ KPI │ │ KPI │ │ KPI │ │ KPI │
└─────┘ └─────┘ └─────┘ └─────┘
┌──────────8──────────┐ ┌──4──┐
│ Kanban │ │Feed │
└─────────────────────┘ └─────┘
Tablet (768px):
┌──────6──────┐ ┌──────6──────┐
│ KPI │ │ KPI │
└─────────────┘ └─────────────┘
┌──────6──────┐ ┌──────6──────┐
│ KPI │ │ KPI │
└─────────────┘ └─────────────┘
┌────────────12────────────────┐
│ Kanban │
└──────────────────────────────┘
┌────────────12────────────────┐
│ Feed │
└──────────────────────────────┘
Mobile (375px):
┌────────────12────────────────┐
│ KPI │ × 4
└──────────────────────────────┘
┌────────────12────────────────┐
│ Kanban │
└──────────────────────────────┘
4. Theming-Engine¶
4.1 Theme-Architektur¶
Themes werden pro Tenant gespeichert und ueber CSS Custom Properties live angewendet. Der Theme-Editor ist Teil des WYSIWYG Designers.
interface TenantTheme {
id: string
tenant_id: string
name: string // "Corporate Blue"
is_active: boolean
colors: {
primary: string // Hauptfarbe
primary_hover: string
secondary: string
accent: string
success: string
warning: string
danger: string
info: string
surface: string // Card/Widget-Hintergrund
background: string // Seiten-Hintergrund
text: string
text_muted: string
border: string
neutral: Record<string, string> // 50-950 Graustufen
}
typography: {
font_family: string
font_family_mono: string
font_size_base: string
line_height: string
heading_weight: string
}
layout: {
border_radius: string
border_radius_sm: string
border_radius_lg: string
spacing_unit: string
sidebar_width: string
}
branding: {
logo_url: string | null
favicon_url: string | null
login_background_url: string | null
login_message: string | null
}
mode: 'light' | 'dark' | 'auto'
dark_overrides: Partial<TenantTheme['colors']>
}
4.2 Theme-Editor (im Designer integriert)¶
Der Theme-Editor ist ein Tab im Config-Panel des Designers — NICHT eine separate Seite:
┌─ Config Panel: Theme ────── [✕] ──┐
│ │
│ Aktives Theme: [Corporate Blue ▾] │
│ [Neues Theme] [Duplizieren] │
│ │
│ ── Schnellauswahl ──────────────── │
│ ┌────┐┌────┐┌────┐┌────┐┌────┐ │
│ │Blue││Grn ││Purp││Org ││Slat│ │
│ └────┘└────┘└────┘└────┘└────┘ │
│ (10 Preset-Themes als Kacheln) │
│ │
│ ── Farben ──────────────────────── │
│ Primary: [■ #2563eb] [🎨] │
│ Secondary: [■ #64748b] [🎨] │
│ Accent: [■ #f59e0b] [🎨] │
│ Success: [■ #10b981] │
│ Warning: [■ #f59e0b] │
│ Danger: [■ #ef4444] │
│ Background:[■ #f8fafc] │
│ Surface: [■ #ffffff] │
│ Text: [■ #0f172a] │
│ │
│ ── Typografie ──────────────────── │
│ Font: [Inter ▾] │
│ Groesse: [16px ▾] │
│ Heading-Gewicht: [600 ▾] │
│ │
│ ── Layout ──────────────────────── │
│ Ecken-Radius: [═══8px═══] │
│ (Slider: 0 → 20px) │
│ │
│ ── Branding ────────────────────── │
│ Logo: [📁 Hochladen] │
│ Favicon: [📁 Hochladen] │
│ │
│ ── Modus ───────────────────────── │
│ [● Auto ○ Hell ○ Dunkel] │
│ │
│ [Aenderungen live — Publish noetig] │
└─────────────────────────────────────┘
Theme-Aenderungen werden sofort auf der Live-Seite sichtbar (WYSIWYG), aber erst nach "Publish" fuer alle Nutzer aktiv.
4.3 Preset-Themes¶
| Theme | Primary | Stil |
|---|---|---|
| XyNaP Default | #2563eb | Modern, Clean |
| Corporate | #1e40af | Serioes, Business |
| Startup | #059669 | Frisch, Dynamisch |
| Creative | #7c3aed | Kreativ |
| Warm | #ea580c | Einladend |
| Minimal | #18181b | Reduziert |
| Ocean | #0891b2 | Professionell |
| Rose | #e11d48 | Auffaellig |
| Forest | #166534 | Vertrauensvoll |
| Slate | #475569 | Zeitlos |
4.4 CSS Custom Properties (Runtime)¶
:root {
--x-primary: #2563eb;
--x-primary-hover: #1d4ed8;
--x-secondary: #64748b;
--x-accent: #f59e0b;
--x-success: #10b981;
--x-warning: #f59e0b;
--x-danger: #ef4444;
--x-info: #3b82f6;
--x-surface: #ffffff;
--x-bg: #f8fafc;
--x-text: #0f172a;
--x-text-muted: #64748b;
--x-border: #e2e8f0;
--x-radius: 8px;
--x-radius-sm: 4px;
--x-radius-lg: 12px;
--x-font: 'Inter', system-ui, sans-serif;
--x-font-mono: 'JetBrains Mono', monospace;
}
[data-theme="dark"] {
--x-surface: #1e293b;
--x-bg: #0f172a;
--x-text: #f1f5f9;
--x-text-muted: #94a3b8;
--x-border: #334155;
}
5. UI Components (Widget-Bibliothek)¶
5.1 Architektur¶
Jedes Widget ist eine selbststaendige Vue-Komponente die:
- Ihre eigenen Daten per API laedt
- Ueber widget_config konfiguriert wird
- Theme-CSS-Variablen nutzt (kein Hardcoding)
- Im WYSIWYG Designer platzierbar ist
- Einen Edit-Overlay fuer den Designer bereitstellt
- Einen Config-Schema fuer das Config-Panel liefert
interface WidgetMeta {
type: string // Eindeutiger Bezeichner
name: string // Anzeigename (i18n key)
icon: string // Lucide Icon
category: 'kpi' | 'pipeline' | 'list' | 'chart' | 'detail' | 'misc'
description: string
default_span: { desktop: number; tablet: number; mobile: number }
min_span: number
config_schema: JSONSchema7 // Fuer automatische Config-UI-Generierung
default_config: Record<string, any>
supports_page_filters: boolean // Reagiert auf globale Seitenfilter
}
5.2 Widget-Katalog¶
Kennzahlen (KPIs)¶
| Widget | Beschreibung | Default Span |
|---|---|---|
kpi-number |
Einzelne grosse Zahl mit Label, Icon, Trend-Pfeil | 3 |
kpi-trend |
Zahl + Sparkline (7d/30d/90d) | 3 |
kpi-comparison |
Aktuell vs. Vorperiode mit Delta | 3 |
kpi-gauge |
Kreisfoermiger Fortschrittsbalken (0-100%) | 3 |
kpi-group |
2-4 KPIs in einer kompakten Card | 6 |
Pipeline & Deals¶
| Widget | Beschreibung | Default Span |
|---|---|---|
pipeline-kanban |
Kanban-Board mit Drag & Drop zwischen Phasen | 8 |
pipeline-funnel |
Trichter-Visualisierung mit Conversion-Raten | 6 |
deal-table |
Sortierbare, filterbare Deal-Tabelle | 12 |
deal-summary |
Kompakte Deal-Karte (fuer Detail-Pages) | 4 |
Listen & Tabellen¶
| Widget | Beschreibung | Default Span |
|---|---|---|
contact-table |
Kontakt-Liste mit Suche, Filter, Inline-Actions | 12 |
ticket-table |
Ticket-Liste mit Status-Badges, SLA-Countdown | 12 |
activity-timeline |
Chronologischer Feed (E-Mails, Anrufe, Notizen) | 4 |
task-list |
Aufgaben mit Checkbox, Faelligkeit, Prioritaet | 4 |
recent-items |
Zuletzt bearbeitete Entities (Deals, Kontakte, ...) | 4 |
Diagramme¶
| Widget | Beschreibung | Default Span |
|---|---|---|
chart-bar |
Balkendiagramm (vertikal/horizontal) | 6 |
chart-line |
Liniendiagramm mit optionalem Area-Fill | 6 |
chart-donut |
Donut/Pie mit optionalem Center-Total | 4 |
chart-forecast |
CRM-Forecast (gewichtet, historisch + Projektion) | 6 |
chart-stacked-bar |
Gestapelte Balken (z.B. Deals pro Phase pro Monat) | 6 |
Detail-Felder (fuer Entity-Pages)¶
| Widget | Beschreibung | Default Span |
|---|---|---|
entity-header |
Kopfbereich: Avatar, Name, Tags, Status, Actions | 12 |
entity-fields |
Editierbare Feldergruppe (Grid oder Liste) | 6 |
entity-relations |
Verknuepfte Entities (Deals, Tickets, Files, ...) | 6 |
notes-panel |
Notizen mit Pinned, Add, Timeline | 4 |
files-panel |
Angehaengte Dateien mit Upload | 4 |
Sonstiges¶
| Widget | Beschreibung | Default Span |
|---|---|---|
quick-actions |
Button-Leiste (Neuer Deal, Neuer Kontakt, ...) | 12 |
team-leaderboard |
Ranking nach Metrik (Deals, Umsatz, Tickets) | 4 |
calendar-mini |
Mini-Kalender mit CRM-Events | 4 |
html-embed |
Freier HTML-Block (sanitized) | 6 |
spacer |
Leerer Platzhalter mit konfigurierbarer Hoehe | 12 |
divider |
Horizontale Trennlinie | 12 |
heading |
Ueberschrift (H1-H4) | 12 |
text-block |
Rich-Text Block (WYSIWYG Editor) | 6 |
5.3 Widget Config-Beispiele¶
// kpi-number
{
label: "Offene Deals",
data_source: "deals",
aggregation: "count",
filter: { status: "open" },
format: "number",
icon: "TrendingUp",
color: "primary",
compare_period: "previous_month" // Zeigt +/- Delta
}
// pipeline-kanban
{
pipeline_id: "sales-pipeline",
show_deal_value: true,
show_contact_name: true,
show_owner_avatar: true,
max_cards_per_column: 20,
highlight_rotting: true,
rotting_days: 14,
compact_mode: false,
allow_drag: true // Drag & Drop zwischen Phasen
}
// chart-forecast
{
pipeline_id: null, // Alle Pipelines
period: "monthly",
months_ahead: 6,
show_weighted: true,
show_actual: true,
show_target: true,
target_values: { // Optionale Zielwerte
"2026-03": 50000,
"2026-04": 55000
}
}
// entity-fields (fuer Contact Detail Page)
{
sections: [
{
title: "Kontaktdaten",
columns: 2,
fields: [
{ key: "email", format: "email", editable: true },
{ key: "phone", format: "phone", editable: true },
{ key: "mobile", format: "phone", editable: true },
{ key: "website", format: "url", editable: true }
]
},
{
title: "Adresse",
columns: 2,
fields: [
{ key: "address.street" },
{ key: "address.city" },
{ key: "address.zip" },
{ key: "address.country" }
]
},
{
title: "Custom Fields",
columns: 2,
fields: [
{ key: "custom.branche", label: "Branche" },
{ key: "custom.mitarbeiter", label: "Mitarbeiter-Anzahl", format: "number" }
]
}
]
}
6. Publish-Workflow¶
6.1 Draft/Published System¶
┌──────────────┐
│ Erstellen │
└──────┬───────┘
▼
┌──────────────┐
┌──────│ Draft │◄─── Bearbeiten
│ └──────┬───────┘
│ ▼
│ ┌──────────────┐
│ │ Publish │──── Sichtbar fuer alle
│ └──────┬───────┘
│ │
│ ▼
│ ┌──────────────┐
└─────►│ Neuer Draft │──── Aenderungen nur fuer Editor
└──────┬───────┘
▼
┌──────────────┐
│ Publish │──── Ueberschreibt Published
└──────────────┘
6.2 Versionshistorie¶
Jeder Publish erstellt einen Snapshot. Rollback jederzeit moeglich.
interface PageVersion {
id: string
page_id: string
version: number // Auto-increment
content: PageContent // Vollstaendiger Snapshot
published_by: string
published_at: string
change_summary: string // Auto-generiert oder manuell
}
6.3 Berechtigungen¶
| Permission | Beschreibung |
|---|---|
page.view |
Seite sehen (Published-Version) |
page.edit |
Seite bearbeiten (Draft erstellen) |
page.publish |
Draft veroeffentlichen |
page.delete |
Seite loeschen |
page.create |
Neue Seiten erstellen |
theme.edit |
Theme-Einstellungen aendern |
theme.publish |
Theme veroeffentlichen |
7. API-Endpoints¶
Pages¶
GET /api/v1/crm/pages # Alle Seiten des Tenants
GET /api/v1/crm/pages/{id} # Seite (published oder draft)
GET /api/v1/crm/pages/{id}/draft # Nur Draft
GET /api/v1/crm/pages/{id}/published # Nur Published
POST /api/v1/crm/pages # Neue Seite erstellen
PUT /api/v1/crm/pages/{id}/draft # Draft speichern (Auto-Save)
POST /api/v1/crm/pages/{id}/publish # Draft veroeffentlichen
POST /api/v1/crm/pages/{id}/discard-draft # Draft verwerfen
DELETE /api/v1/crm/pages/{id} # Seite loeschen
POST /api/v1/crm/pages/{id}/duplicate # Seite klonen
GET /api/v1/crm/pages/{id}/versions # Versionshistorie
POST /api/v1/crm/pages/{id}/rollback/{version} # Zu Version zurueckkehren
POST /api/v1/crm/pages/{id}/set-default # Als Standard setzen
Themes¶
GET /api/v1/crm/themes # Alle Themes
GET /api/v1/crm/themes/active # Aktives Theme
POST /api/v1/crm/themes # Neues Theme
PUT /api/v1/crm/themes/{id} # Theme aktualisieren
DELETE /api/v1/crm/themes/{id} # Theme loeschen
POST /api/v1/crm/themes/{id}/activate # Theme aktivieren
GET /api/v1/crm/themes/presets # Preset-Themes
POST /api/v1/crm/themes/from-preset/{slug} # Aus Preset erstellen
POST /api/v1/crm/themes/{id}/export # Als JSON exportieren
POST /api/v1/crm/themes/import # Aus JSON importieren
Widgets¶
GET /api/v1/crm/widgets # Verfuegbare Widget-Typen
GET /api/v1/crm/widgets/{type}/schema # Config-Schema fuer Widget
GET /api/v1/crm/widgets/{type}/preview-data # Mock-Daten fuer Designer-Preview
Page Templates¶
GET /api/v1/crm/page-templates # Verfuegbare Templates
GET /api/v1/crm/page-templates/{id} # Template-Details
POST /api/v1/crm/pages/from-template/{id} # Seite aus Template erstellen
POST /api/v1/crm/page-templates # Seite als Template speichern
8. DB-Schema¶
-- CRM Pages (Seiten)
crm_pages (
id CHAR(36) PRIMARY KEY,
tenant_id CHAR(36) NOT NULL,
name VARCHAR(200) NOT NULL,
slug VARCHAR(200) NOT NULL,
type ENUM('dashboard','detail','list','report','form','custom') NOT NULL,
icon VARCHAR(50),
entity_type VARCHAR(50),
-- Versioning
draft JSON NOT NULL, -- PageContent (aktueller Entwurf)
published JSON, -- PageContent (veroeffentlicht, NULL = noch nie)
published_at TIMESTAMP,
published_by CHAR(36),
-- Meta
is_system BOOLEAN DEFAULT FALSE,
is_default BOOLEAN DEFAULT FALSE,
is_public BOOLEAN DEFAULT TRUE,
owner_id CHAR(36) NOT NULL,
permissions JSON, -- ["admin", "manager", "sales"]
sort_order INT DEFAULT 0,
-- Navigation
nav_section VARCHAR(50),
nav_parent_id CHAR(36),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE(tenant_id, slug),
INDEX(tenant_id, type),
INDEX(tenant_id, nav_section)
)
-- Page-Versionen (Publish-History)
crm_page_versions (
id CHAR(36) PRIMARY KEY,
page_id CHAR(36) NOT NULL,
version INT NOT NULL,
content JSON NOT NULL, -- PageContent Snapshot
published_by CHAR(36) NOT NULL,
published_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
change_summary VARCHAR(500),
INDEX(page_id, version),
FOREIGN KEY (page_id) REFERENCES crm_pages(id) ON DELETE CASCADE
)
-- Themes
crm_themes (
id CHAR(36) PRIMARY KEY,
tenant_id CHAR(36) NOT NULL,
name VARCHAR(100) NOT NULL,
is_active BOOLEAN DEFAULT FALSE,
colors JSON NOT NULL,
typography JSON NOT NULL,
layout_config JSON NOT NULL,
branding JSON,
mode ENUM('light','dark','auto') DEFAULT 'auto',
dark_overrides JSON,
preset_slug VARCHAR(50),
created_by CHAR(36),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE(tenant_id, name)
)
-- Page Templates (global, nicht tenant-spezifisch)
crm_page_templates (
id CHAR(36) PRIMARY KEY,
name VARCHAR(200) NOT NULL,
description TEXT,
type ENUM('dashboard','detail','list','report','form','custom') NOT NULL,
entity_type VARCHAR(50),
thumbnail_url VARCHAR(500),
content JSON NOT NULL, -- PageContent
category VARCHAR(50),
is_active BOOLEAN DEFAULT TRUE,
sort_order INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
-- User-Praeferenzen (welche Seite als Default)
crm_user_page_prefs (
id CHAR(36) PRIMARY KEY,
tenant_id CHAR(36) NOT NULL,
user_id CHAR(36) NOT NULL,
page_type VARCHAR(50) NOT NULL,
page_id CHAR(36),
widget_overrides JSON, -- User-spezifische Widget-Einstellungen
UNIQUE(tenant_id, user_id, page_type)
)
9. Frontend-Architektur¶
src/
crm/
designer/
PageDesigner.vue # WYSIWYG In-Page Designer (Haupt-Komponente)
DesignerToolbar.vue # Floating Toolbar (Edit/Preview/Publish)
DesignerCanvas.vue # Canvas-Wrapper (Viewport-Simulation)
WidgetPalette.vue # [+] Widget hinzufuegen Panel
ConfigPanel.vue # Slide-Over rechts (Widget/Theme/Page Settings)
RowEditor.vue # Row-Handles (+ ✕ ↑ ↓)
CellWrapper.vue # Cell mit Drag/Resize/Select Overlay
ResizeHandle.vue # Edge-Handle fuer Spaltenbreite
PublishDialog.vue # Publish-Bestaetigung mit Aenderungs-Diff
VersionHistory.vue # Versionshistorie + Rollback
renderer/
PageRenderer.vue # Rendert Published Page (fuer Endnutzer)
RowRenderer.vue # Rendert eine Row
CellRenderer.vue # Rendert eine Cell (dynamisches Widget)
theme/
ThemeConfig.vue # Theme-Tab im Config-Panel
ThemeProvider.vue # Injiziert CSS Custom Properties
ColorPicker.vue # Farbwahl
widgets/
kpi/
KpiNumber.vue
KpiTrend.vue
KpiComparison.vue
KpiGauge.vue
KpiGroup.vue
pipeline/
PipelineKanban.vue
PipelineFunnel.vue
DealTable.vue
DealSummary.vue
lists/
ContactTable.vue
TicketTable.vue
ActivityTimeline.vue
TaskList.vue
RecentItems.vue
charts/
ChartBar.vue
ChartLine.vue
ChartDonut.vue
ChartForecast.vue
ChartStackedBar.vue
detail/
EntityHeader.vue
EntityFields.vue
EntityRelations.vue
NotesPanel.vue
FilesPanel.vue
misc/
QuickActions.vue
TeamLeaderboard.vue
CalendarMini.vue
HtmlEmbed.vue
Spacer.vue
Divider.vue
Heading.vue
TextBlock.vue
composables/
usePageDesigner.ts # Designer-State (edit/preview, undo/redo, drag)
useWidgetRegistry.ts # Widget-Registry (type → component + meta)
useTheme.ts # Theme laden, CSS-Vars setzen, Live-Update
useAutoSave.ts # Draft Auto-Save (debounced, 3s)
usePublish.ts # Publish-Workflow
usePageFilters.ts # Globale Seitenfilter (provide/inject)
stores/
pageStore.ts # Pinia: aktuelle Seite, Draft-State
themeStore.ts # Pinia: aktives Theme
designerStore.ts # Pinia: Designer-UI-State (selected cell, panel open)
views/
CrmPageView.vue # Route: /crm/:slug — rendert Page (published)
CrmDesignerView.vue # Route: /crm/:slug/edit — WYSIWYG Designer
CrmPagesListView.vue # Route: /crm/pages — Alle Seiten verwalten
CrmThemesView.vue # Route: /crm/themes — Theme-Verwaltung
10. Implementierungsreihenfolge¶
Phase A: Kern-Infrastruktur¶
- DB-Tabellen (crm_pages, crm_themes, crm_page_versions, crm_page_templates)
- Backend API (Pages CRUD, Themes CRUD, Publish-Workflow)
- ThemeProvider (CSS Custom Properties Runtime-Injection)
- PageRenderer (Row/Cell/Widget dynamisches Rendering)
- Widget Registry (Registrierung, Resolve, Schema)
Phase B: Basis-Widgets¶
- KPI-Widgets (kpi-number, kpi-trend, kpi-comparison)
- Listen-Widgets (deal-table, contact-table, activity-timeline, task-list)
- Pipeline-Widgets (pipeline-kanban)
- Chart-Widgets (chart-bar, chart-line, chart-donut)
- Misc-Widgets (heading, text-block, spacer, divider, quick-actions)
Phase C: WYSIWYG Designer¶
- PageDesigner (Edit-Modus Overlay auf Live-Page)
- DesignerToolbar (Edit/Preview/Publish Toggle)
- CellWrapper (Select, Drag, Resize Overlays)
- WidgetPalette ([+] Panel mit Kategorien)
- ConfigPanel (Slide-Over mit Widget/Theme/Page Tabs)
- Auto-Save (Debounced Draft-Save)
Phase D: Publish & Versioning¶
- Publish-Dialog (Diff-Anzeige, Bestaetigung)
- VersionHistory (Timeline, Rollback)
- Draft/Published Split-Rendering
Phase E: Erweiterte Widgets¶
- pipeline-funnel, chart-forecast, chart-stacked-bar
- Detail-Widgets (entity-header, entity-fields, entity-relations, notes-panel, files-panel)
- team-leaderboard, calendar-mini, kpi-gauge, kpi-group
Phase F: Polish¶
- Responsive Designer (Viewport-Toggle, Breakpoint-spezifische Spans)
- Undo/Redo (Command-Stack)
- Page Templates (Galerie, Import/Export)
- Theme Presets (10 Themes)
- Seitenfilter (Globale Filter die Widgets beeinflussen)
- User-Praeferenzen (persoenliche Default-Pages)
11. Technische Entscheidungen¶
| Entscheidung | Wahl | Begruendung |
|---|---|---|
| Designer-Ansatz | In-Page Overlay (kein iframe) | Echtes WYSIWYG, keine Styling-Isolation-Probleme |
| Charts | Chart.js 4 (vue-chartjs) | Leichtgewichtig, gute Vue-Integration, responsive |
| Drag & Drop | HTML5 DnD + @vueuse/core | Kein Extra-Package, ausreichend fuer Grid-Layout |
| Grid-CSS | CSS Grid (12-Col, repeat + span) | Nativ, performant, responsive Breakpoints |
| Themes | JSON → CSS Custom Properties | Kein Build-Step, sofortige Live-Aenderung |
| Auto-Save | Debounced PUT (3s nach letzter Aenderung) | Kein Datenverlust, keine uebermaessige API-Last |
| Widget-Rendering | Vue <component :is> + Registry |
Standard-Pattern, Tree-Shakeable, Lazy-Load moeglich |
| Undo/Redo | Command-Pattern (Array von Snapshots) | Simpel, zuverlaessig, kein Extra-Package |
| Config UI | Auto-generiert aus JSON Schema | Einheitliches Config-Panel fuer alle Widgets |
12. Abgrenzung¶
- Kein Website-Builder — Der Designer ist fuer CRM-Seiten (Dashboards, Detail-Views, Reports), NICHT fuer oeffentliche Websites. Dafuer gibt es XyNaP.cms.
- Kein Code-Editor — Alles visuell, kein HTML/CSS/JS Zugang fuer Endnutzer.
- Keine Custom-Widgets (vorerst) — Nur mitgelieferte Widgets. Plugin-System fuer spaeter geplant.
- Kein Pixel-Perfect — 12-Spalten-Grid mit Snap, kein freies Positionieren (Squarespace-Ansatz, nicht Figma).
- Kein Echtzeit-Collaboration — Ein User bearbeitet eine Seite gleichzeitig. Multi-User spaeter via Locking.