feat: Komplett-Redesign + Ausbau zur Plattform

- Ruhiges Dark-Designsystem (Tokens, Karten, Badges/Pills, KPIs) in global.css,
  animierter PixelBlast-Hintergrund entfernt (kleineres Bundle)
- Aufgeraeumter App-Rahmen: feste Sidebar mit sichtbaren Labels, gruppierter
  Navigation, aktivem Zustand und Collapse
- Wiederverwendbare UI-Bausteine unter src/components/ui/
- Ticketliste als scanbare Karten (TicketCard) statt rowSpan-Tabelle
- Neue Bereiche: Kunden (CRM-360), Finanzen-Cockpit, Dokumente-Browser
- Dashboard als echtes Cockpit (Ticket-/Finanz-/Dokument-Kennzahlen)
- Alle Seiten auf einheitlichen PageHeader/Container/Tokens umgestellt

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Webklar DeployandCursor committed 2026-06-17 09:41:33 +00:00
1 parent 8f6927fbb0
commit d20a4de117
28 files changed
+1639 -840

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
export default function Badge({ tone = 'muted', dot = false, children, className = '', style }) {
return (
<span className={`badge badge-${tone} ${className}`} style={style}>
{dot && <span className="dot" />}
{children}
</span>
)
}
+23
View File
@@ -0,0 +1,23 @@
const VARIANTS = {
default: 'ui-btn',
primary: 'ui-btn ui-btn-primary',
ghost: 'ui-btn ui-btn-ghost',
danger: 'ui-btn ui-btn-danger',
}
export default function Button({
variant = 'default',
size,
as = 'button',
className = '',
children,
...props
}) {
const cls = `${VARIANTS[variant] || VARIANTS.default}${size === 'sm' ? ' ui-btn-sm' : ''} ${className}`
const Comp = as
return (
<Comp className={cls} {...props}>
{children}
</Comp>
)
}
+18
View File
@@ -0,0 +1,18 @@
export default function Card({ title, actions, children, pad = true, className = '', bodyStyle, style }) {
if (!title && !actions) {
return (
<div className={`ui-card ${pad ? 'pad' : ''} ${className}`} style={style}>
{children}
</div>
)
}
return (
<div className={`ui-card ${className}`} style={style}>
<div className="ui-card-head">
<h3>{title}</h3>
{actions && <div className="flex gap-2 items-center">{actions}</div>}
</div>
<div className="ui-card-body" style={bodyStyle}>{children}</div>
</div>
)
}
+10
View File
@@ -0,0 +1,10 @@
export default function EmptyState({ icon, title, hint, action }) {
return (
<div className="empty">
{icon && <div className="empty-icon">{icon}</div>}
<div style={{ fontWeight: 700, marginBottom: 4 }}>{title}</div>
{hint && <div className="faint" style={{ fontSize: 13 }}>{hint}</div>}
{action && <div style={{ marginTop: 16 }}>{action}</div>}
</div>
)
}
+9
View File
@@ -0,0 +1,9 @@
export default function Field({ label, children, hint }) {
return (
<div className="field">
{label && <label>{label}</label>}
{children}
{hint && <div className="faint" style={{ fontSize: 12 }}>{hint}</div>}
</div>
)
}
+10
View File
@@ -0,0 +1,10 @@
export default function Kpi({ label, value, sub, icon, accent = 'var(--accent)' }) {
return (
<div className="kpi">
<span className="kpi-accent" style={{ background: accent }} />
<div className="kpi-label">{icon}{label}</div>
<div className="kpi-value">{value}</div>
{sub && <div className="kpi-sub">{sub}</div>}
</div>
)
}
+12
View File
@@ -0,0 +1,12 @@
export default function PageHeader({ title, subtitle, actions, children }) {
return (
<div className="page-header">
<div>
<h1 className="page-title">{title}</h1>
{subtitle && <div className="page-subtitle">{subtitle}</div>}
{children}
</div>
{actions && <div className="page-actions">{actions}</div>}
</div>
)
}
+36
View File
@@ -0,0 +1,36 @@
const STATUS_CLASS = {
Open: 'status-open',
Occupied: 'status-occupied',
Assigned: 'status-assigned',
Awaiting: 'status-awaiting',
'Added Info': 'status-awaiting',
Closed: 'status-closed',
}
const PRIORITY = {
0: { cls: 'priority-none', label: 'Keine' },
1: { cls: 'priority-low', label: 'Niedrig' },
2: { cls: 'priority-medium', label: 'Mittel' },
3: { cls: 'priority-high', label: 'Hoch' },
4: { cls: 'priority-critical', label: 'Kritisch' },
}
const pillBase = {
display: 'inline-flex',
alignItems: 'center',
gap: 6,
padding: '3px 10px',
borderRadius: 999,
fontSize: 12,
fontWeight: 700,
}
export function StatusPill({ status }) {
const cls = STATUS_CLASS[status] || 'status-open'
return <span className={cls} style={pillBase}>{status || 'Open'}</span>
}
export function PriorityPill({ priority }) {
const p = PRIORITY[priority] ?? PRIORITY[1]
return <span className={p.cls} style={pillBase}>{p.label}</span>
}
+16
View File
@@ -0,0 +1,16 @@
export default function Tabs({ tabs, active, onChange }) {
return (
<div className="ticket-tabs">
{tabs.map((t) => (
<button
key={t.id}
type="button"
className={`ticket-tab ${active === t.id ? 'ticket-tab-active' : ''}`}
onClick={() => onChange(t.id)}
>
{t.label}
</button>
))}
</div>
)
}
+9
View File
@@ -0,0 +1,9 @@
export { default as PageHeader } from './PageHeader'
export { default as Card } from './Card'
export { default as Button } from './Button'
export { default as Badge } from './Badge'
export { StatusPill, PriorityPill } from './StatusPill'
export { default as Tabs } from './Tabs'
export { default as EmptyState } from './EmptyState'
export { default as Field } from './Field'
export { default as Kpi } from './Kpi'