refactor(theme): migrate all remaining bare shadcn tokens to --color-*

Follow-up to the shared-packages migration. Covers 61 more files
across the mana web app (lib/components, lib/modules, routes) and
the same handful of packages, including surface / error / success /
warning tokens that were missed in the first pass.

Two patterns migrated:
1. hsl(var(--X[, raw-channels])) → hsl(var(--color-X[, raw-channels]))
2. var(--X, #hex)               → hsl(var(--color-X))
   (the hex fallback was only doing work because the CSS var was
   undefined; now that the theme resolves cleanly, the fallback is
   unnecessary and actively wrong — it never tracked the active theme)

Module-literal accents (news-research's #0891b2 / #10b981) left
untouched per the themes.css convention for brand-semantic colors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-15 22:56:59 +02:00
parent 115afea519
commit cd22e42afc
61 changed files with 1308 additions and 1120 deletions

View file

@ -132,8 +132,8 @@
align-items: flex-start;
gap: 0.875rem;
padding: 1rem 1.25rem;
background: var(--surface, #fff);
border: 1px solid var(--border, #e5e7eb);
background: hsl(var(--color-surface));
border: 1px solid hsl(var(--color-border));
border-left: 4px solid rgb(34, 197, 94);
border-radius: 0.75rem;
box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.18);
@ -184,7 +184,7 @@
.learn-more {
margin-top: 0.25rem;
font-weight: 500;
color: var(--primary, #6366f1);
color: hsl(var(--color-primary));
text-decoration: none;
font-size: 0.9rem;
}
@ -212,8 +212,8 @@
@media (prefers-color-scheme: dark) {
.banner {
background: var(--surface, #1f2937);
border-color: var(--border, #374151);
background: hsl(var(--color-surface));
border-color: hsl(var(--color-border));
border-left-color: rgb(34, 197, 94);
}
.banner-body strong {

View file

@ -124,8 +124,8 @@
.modal {
max-width: 32rem;
width: 100%;
background: var(--surface, #fff);
border: 1px solid var(--border, #e5e7eb);
background: hsl(var(--color-surface));
border: 1px solid hsl(var(--color-border));
border-radius: 0.75rem;
padding: 1.5rem;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.4);
@ -152,7 +152,7 @@
width: 100%;
margin: 1rem 0 0.5rem;
padding: 0.75rem 1rem;
border: 1px solid var(--border, #e5e7eb);
border: 1px solid hsl(var(--color-border));
border-radius: 0.5rem;
font-family: ui-monospace, SFMono-Regular, monospace;
font-size: 1rem;
@ -162,7 +162,7 @@
}
.recovery-input:focus {
outline: 2px solid var(--primary, #6366f1);
outline: 2px solid hsl(var(--color-primary));
outline-offset: 1px;
}
@ -189,8 +189,8 @@
.btn {
padding: 0.5rem 1.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border, #e5e7eb);
background: var(--surface, #fff);
border: 1px solid hsl(var(--color-border));
background: hsl(var(--color-surface));
font-size: 0.9rem;
cursor: pointer;
font-weight: 500;
@ -202,7 +202,7 @@
}
.btn-primary {
background: var(--primary, #6366f1);
background: hsl(var(--color-primary));
color: white;
border-color: transparent;
}
@ -214,23 +214,23 @@
.help {
margin-top: 1.25rem;
padding-top: 1rem;
border-top: 1px solid var(--border, #e5e7eb);
border-top: 1px solid hsl(var(--color-border));
font-size: 0.8rem;
color: var(--text-secondary, #6b7280);
}
@media (prefers-color-scheme: dark) {
.modal {
background: var(--surface, #1f2937);
border-color: var(--border, #374151);
background: hsl(var(--color-surface));
border-color: hsl(var(--color-border));
}
.recovery-input {
background: var(--surface-muted, #111827);
border-color: var(--border, #374151);
border-color: hsl(var(--color-border));
color: #f3f4f6;
}
.help {
border-color: var(--border, #374151);
border-color: hsl(var(--color-border));
}
}
</style>

View file

@ -129,7 +129,7 @@
.conflict {
pointer-events: auto;
background: var(--surface, #fff);
background: hsl(var(--color-surface));
border: 1px solid rgba(245, 158, 11, 0.4);
border-left: 4px solid rgb(245, 158, 11);
border-radius: 0.5rem;
@ -198,9 +198,9 @@
align-items: center;
gap: 0.375rem;
padding: 0.375rem 0.75rem;
border: 1px solid var(--border, #e5e7eb);
border: 1px solid hsl(var(--color-border));
border-radius: 0.375rem;
background: var(--surface, #fff);
background: hsl(var(--color-surface));
font-size: 0.8125rem;
font-weight: 500;
cursor: pointer;
@ -212,7 +212,7 @@
}
.btn-primary {
background: var(--primary, #6366f1);
background: hsl(var(--color-primary));
color: white;
border-color: transparent;
}
@ -223,7 +223,7 @@
@media (prefers-color-scheme: dark) {
.conflict {
background: var(--surface, #1f2937);
background: hsl(var(--color-surface));
border-color: rgba(245, 158, 11, 0.5);
border-left-color: rgb(245, 158, 11);
}
@ -234,8 +234,8 @@
color: #9ca3af;
}
.btn {
background: var(--surface, #1f2937);
border-color: var(--border, #374151);
background: hsl(var(--color-surface));
border-color: hsl(var(--color-border));
color: #e5e7eb;
}
.btn:hover {

View file

@ -96,8 +96,8 @@
.modal {
max-width: 30rem;
width: 100%;
background: var(--surface, #fff);
border: 1px solid var(--border, #e5e7eb);
background: hsl(var(--color-surface));
border: 1px solid hsl(var(--color-border));
border-radius: 0.75rem;
padding: 1.5rem;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.4);
@ -111,19 +111,19 @@
margin: 0;
font-size: 1.25rem;
font-weight: 600;
color: var(--foreground, #111);
color: hsl(var(--color-foreground));
}
p {
margin: 0.75rem 0;
font-size: 0.95rem;
line-height: 1.5;
color: var(--foreground, #222);
color: hsl(var(--color-foreground));
}
.hint {
font-size: 0.85rem;
color: var(--muted-foreground, #666);
color: hsl(var(--color-muted-foreground));
}
.actions {
@ -136,23 +136,23 @@
.btn {
padding: 0.55rem 1.25rem;
border-radius: 0.5rem;
border: 1px solid var(--border, #e5e7eb);
border: 1px solid hsl(var(--color-border));
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
background: var(--surface, #fff);
color: var(--foreground, #111);
background: hsl(var(--color-surface));
color: hsl(var(--color-foreground));
transition: background 0.15s ease;
}
.btn:hover {
background: var(--surface-hover, #f3f4f6);
background: hsl(var(--color-surface-hover));
}
.btn-primary {
background: var(--primary, #6366f1);
background: hsl(var(--color-primary));
color: hsl(var(--color-primary-foreground));
border-color: var(--primary, #6366f1);
border-color: hsl(var(--color-primary));
}
.btn-primary:hover {

View file

@ -21,16 +21,16 @@
<div class="flex min-h-[60vh] items-center justify-center p-6">
<div
class="w-full max-w-96 rounded-2xl border px-8 py-10 text-center shadow-sm"
style:border-color="hsl(var(--border, 0 0% 90%))"
style:background-color="hsl(var(--card, 0 0% 100%))"
style:border-color="hsl(var(--color-border, 0 0% 90%))"
style:background-color="hsl(var(--color-card, 0 0% 100%))"
>
<h1 class="mb-4 text-xl font-bold" style:color="hsl(var(--foreground, 0 0% 9%))">
<h1 class="mb-4 text-xl font-bold" style:color="hsl(var(--color-foreground, 0 0% 9%))">
{appName}
</h1>
<div class="mb-4 text-5xl">🔒</div>
<p
class="mb-6 text-[0.9375rem] leading-relaxed"
style:color="hsl(var(--muted-foreground, 0 0% 45%))"
style:color="hsl(var(--color-muted-foreground, 0 0% 45%))"
>
{isDE
? 'Diese App ist aktuell in der geschlossenen '
@ -40,18 +40,18 @@
</p>
<div
class="mb-6 flex flex-col gap-2 rounded-xl p-4"
style:background-color="hsl(var(--muted, 0 0% 96%))"
style:background-color="hsl(var(--color-muted, 0 0% 96%))"
>
<div class="flex items-center justify-between text-sm">
<span style:color="hsl(var(--muted-foreground, 0 0% 45%))"
<span style:color="hsl(var(--color-muted-foreground, 0 0% 45%))"
>{isDE ? 'Dein Zugang:' : 'Your access:'}</span
>
<span class="font-semibold" style:color="hsl(var(--foreground, 0 0% 9%))"
<span class="font-semibold" style:color="hsl(var(--color-foreground, 0 0% 9%))"
>{userTierLabel}</span
>
</div>
<div class="flex items-center justify-between text-sm">
<span style:color="hsl(var(--muted-foreground, 0 0% 45%))"
<span style:color="hsl(var(--color-muted-foreground, 0 0% 45%))"
>{isDE ? 'Benötigt:' : 'Required:'}</span
>
<span class="font-semibold text-violet-500">{requiredTierLabel}</span>
@ -60,8 +60,8 @@
<div class="flex flex-col gap-2">
<button
class="w-full cursor-pointer rounded-lg border-none px-4 py-2.5 text-sm font-medium transition-opacity hover:opacity-90"
style:background-color="hsl(var(--primary, 239 84% 67%))"
style:color="hsl(var(--primary-foreground, 0 0% 100%))"
style:background-color="hsl(var(--color-primary, 239 84% 67%))"
style:color="hsl(var(--color-primary-foreground, 0 0% 100%))"
onclick={() => goto('/')}
>
{isDE ? 'Zur Übersicht' : 'Back to overview'}
@ -69,8 +69,8 @@
{#if !authStore.isAuthenticated}
<button
class="w-full cursor-pointer rounded-lg border px-4 py-2.5 text-sm font-medium transition-opacity hover:opacity-90"
style:border-color="hsl(var(--border, 0 0% 90%))"
style:color="hsl(var(--foreground, 0 0% 9%))"
style:border-color="hsl(var(--color-border, 0 0% 90%))"
style:color="hsl(var(--color-foreground, 0 0% 9%))"
onclick={() => goto('/login')}
>
{isDE ? 'Anmelden' : 'Sign in'}

View file

@ -77,7 +77,7 @@
.minimal-expression {
font-family: system-ui, sans-serif;
font-size: 14px;
color: hsl(var(--muted-foreground));
color: hsl(var(--color-muted-foreground));
opacity: 0.5;
min-height: 20px;
overflow: hidden;
@ -89,7 +89,7 @@
font-family: system-ui, sans-serif;
font-size: 48px;
font-weight: 200;
color: hsl(var(--foreground));
color: hsl(var(--color-foreground));
letter-spacing: -1px;
overflow: hidden;
text-overflow: ellipsis;
@ -98,7 +98,7 @@
}
.minimal-error {
color: hsl(var(--destructive, 0 84% 60%));
color: hsl(var(--color-error, 0 84% 60%));
font-size: 24px;
}
@ -118,31 +118,31 @@
cursor: pointer;
transition: background 0.15s;
background: transparent;
color: hsl(var(--foreground));
color: hsl(var(--color-foreground));
}
.minimal-btn:hover {
background: hsl(var(--muted));
background: hsl(var(--color-muted));
}
.minimal-btn:active {
background: hsl(var(--muted));
background: hsl(var(--color-muted));
opacity: 0.7;
}
.minimal-btn-eq {
background: hsl(var(--foreground));
color: hsl(var(--background));
background: hsl(var(--color-foreground));
color: hsl(var(--color-background));
font-weight: 400;
}
.minimal-btn-eq:hover {
background: hsl(var(--foreground));
background: hsl(var(--color-foreground));
opacity: 0.8;
}
.minimal-btn-clear {
color: hsl(var(--destructive, 0 84% 60%));
color: hsl(var(--color-error, 0 84% 60%));
font-weight: 400;
}
@ -155,13 +155,13 @@
font-size: 16px;
cursor: pointer;
background: transparent;
color: hsl(var(--muted-foreground));
color: hsl(var(--color-muted-foreground));
}
.minimal-copy {
background: none;
border: none;
color: hsl(var(--muted-foreground));
color: hsl(var(--color-muted-foreground));
opacity: 0.3;
font-size: 16px;
cursor: pointer;
@ -173,6 +173,6 @@
}
.minimal-backspace:hover {
background: hsl(var(--muted));
background: hsl(var(--color-muted));
}
</style>

View file

@ -23,14 +23,14 @@
];
</script>
<div class="flex rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))]">
<div class="flex rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))]">
{#each modes as mode}
<button
type="button"
onclick={() => onchange(mode.value)}
class="p-2 transition-colors {current === mode.value
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
: 'text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]'} {mode.value ===
? 'bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))]'
: 'text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]'} {mode.value ===
'list'
? 'rounded-l-lg'
: mode.value === 'table'

View file

@ -37,7 +37,7 @@
}
const inputClass =
'w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] placeholder:text-[hsl(var(--muted-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))] focus:ring-offset-1';
'w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] placeholder:text-[hsl(var(--color-muted-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))] focus:ring-offset-1';
</script>
{#if field.type === 'text'}
@ -66,7 +66,7 @@
class="{inputClass} flex-1"
oninput={handleInput}
/>
<span class="flex items-center text-sm text-[hsl(var(--muted-foreground))]">
<span class="flex items-center text-sm text-[hsl(var(--color-muted-foreground))]">
{field.currencyCode || 'EUR'}
</span>
</div>
@ -77,10 +77,10 @@
<input
type="checkbox"
checked={!!value}
class="h-4 w-4 rounded border-[hsl(var(--border))] text-[hsl(var(--primary))]"
class="h-4 w-4 rounded border-[hsl(var(--color-border))] text-[hsl(var(--color-primary))]"
onchange={handleInput}
/>
<span class="text-sm text-[hsl(var(--foreground))]">{field.name}</span>
<span class="text-sm text-[hsl(var(--color-foreground))]">{field.name}</span>
</label>
{:else if field.type === 'select'}
<select value={String(value || '')} class={inputClass} onchange={handleSelectChange}>
@ -103,12 +103,12 @@
<div class="flex flex-wrap gap-1">
{#each currentTags as tag, i}
<span
class="inline-flex items-center gap-1 rounded-full bg-[hsl(var(--muted))] px-2 py-0.5 text-xs"
class="inline-flex items-center gap-1 rounded-full bg-[hsl(var(--color-muted))] px-2 py-0.5 text-xs"
>
{tag}
<button
type="button"
class="text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
onclick={() => onchange(currentTags.filter((_, idx) => idx !== i))}>x</button
>
</span>

View file

@ -35,12 +35,12 @@
</script>
{#if value === undefined || value === null || value === ''}
<span class="text-[hsl(var(--muted-foreground))] italic">--</span>
<span class="text-[hsl(var(--color-muted-foreground))] italic">--</span>
{:else if field.type === 'checkbox'}
{#if value}
<span class="text-green-500">&#10003;</span>
{:else}
<span class="text-[hsl(var(--muted-foreground))]">&#10007;</span>
<span class="text-[hsl(var(--color-muted-foreground))]">&#10007;</span>
{/if}
{:else if field.type === 'currency'}
<span>{formatCurrency(value, field.currencyCode)}</span>
@ -51,7 +51,7 @@
href={String(value)}
target="_blank"
rel="noopener noreferrer"
class="text-[hsl(var(--primary))] underline hover:no-underline"
class="text-[hsl(var(--color-primary))] underline hover:no-underline"
>
{String(value)
.replace(/^https?:\/\//, '')
@ -59,14 +59,14 @@
</a>
{:else if field.type === 'select'}
<span
class="inline-block rounded-full bg-[hsl(var(--accent)/0.15)] px-2 py-0.5 text-xs font-medium text-[hsl(var(--accent-foreground))]"
class="inline-block rounded-full bg-[hsl(var(--color-accent)/0.15)] px-2 py-0.5 text-xs font-medium text-[hsl(var(--color-accent-foreground))]"
>
{String(value)}
</span>
{:else if field.type === 'tags'}
<div class="flex flex-wrap gap-1">
{#each Array.isArray(value) ? value : [] as tag}
<span class="rounded-full bg-[hsl(var(--muted))] px-2 py-0.5 text-xs">{tag}</span>
<span class="rounded-full bg-[hsl(var(--color-muted))] px-2 py-0.5 text-xs">{tag}</span>
{/each}
</div>
{:else if field.type === 'number'}

View file

@ -78,12 +78,14 @@
}
const inputClass =
'w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]';
'w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]';
</script>
<div class="space-y-3">
{#each fields.sort((a, b) => a.order - b.order) as field, index (field.id)}
<div class="rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-3">
<div
class="rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-3"
>
<div class="flex items-start gap-2">
<!-- Reorder buttons -->
<div class="flex flex-col gap-0.5 pt-1">
@ -91,14 +93,14 @@
type="button"
onclick={() => moveField(field.id, 'up')}
disabled={index === 0}
class="text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))] disabled:opacity-30"
class="text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))] disabled:opacity-30"
>&#9650;</button
>
<button
type="button"
onclick={() => moveField(field.id, 'down')}
disabled={index === fields.length - 1}
class="text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))] disabled:opacity-30"
class="text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))] disabled:opacity-30"
>&#9660;</button
>
</div>
@ -126,7 +128,9 @@
</div>
<div class="flex items-center gap-4">
<label class="flex items-center gap-1 text-xs text-[hsl(var(--muted-foreground))]">
<label
class="flex items-center gap-1 text-xs text-[hsl(var(--color-muted-foreground))]"
>
<input
type="checkbox"
checked={field.required || false}
@ -167,13 +171,13 @@
<div class="flex flex-wrap gap-1">
{#each field.options || [] as option, i}
<span
class="inline-flex items-center gap-1 rounded bg-[hsl(var(--muted))] px-2 py-0.5 text-xs"
class="inline-flex items-center gap-1 rounded bg-[hsl(var(--color-muted))] px-2 py-0.5 text-xs"
>
{option}
<button
type="button"
onclick={() => removeOption(field.id, i)}
class="text-[hsl(var(--muted-foreground))] hover:text-red-500">x</button
class="text-[hsl(var(--color-muted-foreground))] hover:text-red-500">x</button
>
</span>
{/each}
@ -194,7 +198,7 @@
<button
type="button"
onclick={() => addOption(field.id)}
class="rounded bg-[hsl(var(--primary))] px-2 py-1 text-xs text-[hsl(var(--primary-foreground))]"
class="rounded bg-[hsl(var(--color-primary))] px-2 py-1 text-xs text-[hsl(var(--color-primary-foreground))]"
>+</button
>
</div>
@ -206,7 +210,7 @@
<button
type="button"
onclick={() => removeField(field.id)}
class="mt-1 text-[hsl(var(--muted-foreground))] hover:text-red-500"
class="mt-1 text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
title="Feld entfernen"
>
<Trash size={20} />
@ -218,7 +222,7 @@
<button
type="button"
onclick={addField}
class="flex w-full items-center justify-center gap-2 rounded-lg border-2 border-dashed border-[hsl(var(--border))] py-3 text-sm text-[hsl(var(--muted-foreground))] transition-colors hover:border-[hsl(var(--primary))] hover:text-[hsl(var(--primary))]"
class="flex w-full items-center justify-center gap-2 rounded-lg border-2 border-dashed border-[hsl(var(--color-border))] py-3 text-sm text-[hsl(var(--color-muted-foreground))] transition-colors hover:border-[hsl(var(--color-primary))] hover:text-[hsl(var(--color-primary))]"
>
<Plus size={20} />
Feld hinzufugen

View file

@ -240,18 +240,19 @@
padding: 0.65rem;
height: 100%;
overflow-y: auto;
color: hsl(var(--color-foreground));
}
.slot {
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 0.55rem;
border: 1px solid var(--border, #e5e5e5);
border: 1px solid hsl(var(--color-border));
border-radius: 0.5rem;
background: var(--surface, #fff);
background: hsl(var(--color-background));
}
.empty {
color: var(--text-muted, #888);
color: hsl(var(--color-muted-foreground));
font-size: 0.85rem;
padding: 0.75rem;
text-align: center;
@ -260,7 +261,7 @@
display: flex;
gap: 0.2rem;
align-items: center;
background: var(--surface-alt, #f4f4f4);
background: hsl(var(--color-muted));
padding: 0.2rem;
border-radius: 0.4rem;
}
@ -272,16 +273,17 @@
border-radius: 0.3rem;
cursor: pointer;
font-size: 0.8rem;
color: var(--text, #333);
color: hsl(var(--color-muted-foreground));
}
.mode-switch button.active {
background: var(--surface, #fff);
background: hsl(var(--color-background));
color: hsl(var(--color-foreground));
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
}
.open-full {
padding: 0 0.4rem;
text-decoration: none;
color: var(--text-muted, #888);
color: hsl(var(--color-muted-foreground));
font-size: 0.9rem;
}
.row {
@ -292,18 +294,18 @@
flex: 1;
min-width: 0;
padding: 0.35rem 0.5rem;
border: 1px solid var(--border, #e5e5e5);
border: 1px solid hsl(var(--color-border));
border-radius: 0.35rem;
background: var(--surface, #fff);
color: var(--text, #333);
background: hsl(var(--color-background));
color: hsl(var(--color-foreground));
font-size: 0.85rem;
}
.row button {
padding: 0.35rem 0.7rem;
border: none;
border-radius: 0.35rem;
background: var(--accent, #0891b2);
color: white;
background: hsl(var(--color-primary));
color: hsl(var(--color-primary-foreground, 0 0% 100%));
cursor: pointer;
font-size: 0.8rem;
white-space: nowrap;
@ -320,7 +322,7 @@
padding: 0.2rem 0;
cursor: pointer;
font-size: 0.85rem;
color: var(--text, #333);
color: hsl(var(--color-foreground));
font-weight: 500;
}
.feeds,
@ -347,6 +349,7 @@
min-width: 0;
cursor: pointer;
font-size: 0.8rem;
color: hsl(var(--color-foreground));
}
.ft {
overflow: hidden;
@ -358,11 +361,11 @@
border: none;
cursor: pointer;
font-size: 0.95rem;
color: var(--text-muted, #888);
color: hsl(var(--color-muted-foreground));
padding: 0 0.25rem;
}
.pin.pinned {
color: var(--accent, #0891b2);
color: hsl(var(--color-primary));
}
.results-head {
display: flex;
@ -370,29 +373,29 @@
align-items: center;
font-size: 0.85rem;
font-weight: 500;
color: var(--text, #333);
color: hsl(var(--color-foreground));
}
.ctx {
background: var(--surface-alt, #f4f4f4);
border: 1px solid var(--border, #e5e5e5);
background: hsl(var(--color-muted));
border: 1px solid hsl(var(--color-border));
border-radius: 0.3rem;
padding: 0.2rem 0.5rem;
font-size: 0.75rem;
cursor: pointer;
color: var(--text, #333);
color: hsl(var(--color-foreground));
}
.results li {
display: flex;
flex-direction: column;
gap: 0.2rem;
padding: 0.35rem;
border: 1px solid var(--border, #eee);
border: 1px solid hsl(var(--color-border));
border-radius: 0.35rem;
}
.rt {
font-size: 0.85rem;
font-weight: 600;
color: var(--text, #333);
color: hsl(var(--color-foreground));
text-decoration: none;
line-height: 1.25;
}
@ -404,25 +407,25 @@
gap: 0.3rem;
align-items: center;
font-size: 0.7rem;
color: var(--text-muted, #888);
color: hsl(var(--color-muted-foreground));
}
.save {
margin-left: auto;
background: transparent;
border: 1px solid var(--border, #e5e5e5);
border: 1px solid hsl(var(--color-border));
border-radius: 0.25rem;
padding: 0.1rem 0.4rem;
font-size: 0.7rem;
cursor: pointer;
color: var(--text, #333);
color: hsl(var(--color-foreground));
}
.save:disabled {
opacity: 0.5;
}
.error {
background: #fee;
border: 1px solid #fcc;
color: #900;
background: hsl(var(--color-destructive) / 0.1);
border: 1px solid hsl(var(--color-destructive) / 0.4);
color: hsl(var(--color-destructive));
padding: 0.35rem 0.5rem;
border-radius: 0.35rem;
font-size: 0.8rem;

View file

@ -85,7 +85,7 @@
}
</script>
<div class="answer-citations whitespace-pre-wrap text-[hsl(var(--foreground))]">
<div class="answer-citations whitespace-pre-wrap text-[hsl(var(--color-foreground))]">
{#each segments as segment, i (i)}
{#if segment.kind === 'text'}{segment.text}{:else}
{@const src = sourceForRank(segment.rank)}
@ -109,18 +109,20 @@
onmouseleave={hidePopover}
>
{#if loading && !src}
<span class="text-xs text-[hsl(var(--muted-foreground))]">Lade Quelle…</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">Lade Quelle…</span>
{:else if loadError}
<span class="text-xs text-red-500">Fehler: {loadError}</span>
{:else if src}
<span class="block text-xs font-semibold text-[hsl(var(--foreground))]">
<span class="block text-xs font-semibold text-[hsl(var(--color-foreground))]">
{src.title ?? hostFromUrl(src.url)}
</span>
<span class="mt-1 block text-[10px] uppercase text-[hsl(var(--muted-foreground))]">
<span
class="mt-1 block text-[10px] uppercase text-[hsl(var(--color-muted-foreground))]"
>
{hostFromUrl(src.url)}
</span>
{#if src.snippet}
<span class="mt-2 block text-xs text-[hsl(var(--muted-foreground))]">
<span class="mt-2 block text-xs text-[hsl(var(--color-muted-foreground))]">
{src.snippet}
</span>
{/if}
@ -128,12 +130,12 @@
href={src.url}
target="_blank"
rel="noopener noreferrer"
class="mt-2 inline-flex items-center gap-1 text-xs text-[hsl(var(--primary))] hover:underline"
class="mt-2 inline-flex items-center gap-1 text-xs text-[hsl(var(--color-primary))] hover:underline"
>
Öffnen <ArrowSquareOut class="h-3 w-3" />
</a>
{:else if loaded}
<span class="text-xs text-[hsl(var(--muted-foreground))]">
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">
Quelle {segment.rank} nicht gefunden
</span>
{/if}
@ -158,8 +160,8 @@
padding: 0 0.35em;
margin: 0 0.1em;
border-radius: 9999px;
background: hsl(var(--primary) / 0.15);
color: hsl(var(--primary));
background: hsl(var(--color-primary) / 0.15);
color: hsl(var(--color-primary));
font-size: 0.7em;
font-weight: 600;
line-height: 1.5;
@ -171,7 +173,7 @@
.citation-pill:hover,
.citation-pill:focus-visible {
background: hsl(var(--primary) / 0.3);
background: hsl(var(--color-primary) / 0.3);
outline: none;
}
@ -186,8 +188,8 @@
max-width: calc(100vw - 2rem);
padding: 0.75rem 0.85rem;
border-radius: 0.5rem;
border: 1px solid hsl(var(--border));
background: hsl(var(--card));
border: 1px solid hsl(var(--color-border));
background: hsl(var(--color-card));
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
text-align: left;
white-space: normal;

View file

@ -198,15 +198,15 @@
aria-modal="true"
>
<div
class="w-full max-w-lg rounded-t-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-6 shadow-xl sm:rounded-2xl"
class="w-full max-w-lg rounded-t-2xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-6 shadow-xl sm:rounded-2xl"
>
<div class="mb-4 flex items-center justify-between">
<h2 class="text-lg font-semibold text-[hsl(var(--foreground))]">
<h2 class="text-lg font-semibold text-[hsl(var(--color-foreground))]">
{$_('entry.manual')}
</h2>
<button
onclick={onClose}
class="rounded-lg p-1 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded-lg p-1 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<X size={20} />
</button>
@ -220,19 +220,19 @@
oninput={handleQuickInput}
onkeydown={handleQuickKeydown}
placeholder="Schnelleingabe: Meeting 2h @Projekt $; Review 1h"
class="w-full rounded-lg border border-dashed border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.3)] px-4 py-2.5 text-sm text-[hsl(var(--foreground))] placeholder:text-xs placeholder:text-[hsl(var(--muted-foreground))] focus:border-solid focus:border-[hsl(var(--primary)/0.5)] focus:bg-[hsl(var(--input))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary)/0.1)]"
class="w-full rounded-lg border border-dashed border-[hsl(var(--color-border))] bg-[hsl(var(--color-muted)/0.3)] px-4 py-2.5 text-sm text-[hsl(var(--color-foreground))] placeholder:text-xs placeholder:text-[hsl(var(--color-muted-foreground))] focus:border-solid focus:border-[hsl(var(--color-primary)/0.5)] focus:bg-[hsl(var(--color-input))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary)/0.1)]"
/>
{#if quickPreview}
<div class="mt-1 px-1 text-[0.7rem] text-[hsl(var(--muted-foreground))]">
<div class="mt-1 px-1 text-[0.7rem] text-[hsl(var(--color-muted-foreground))]">
{quickPreview}
</div>
{/if}
</div>
<div class="mb-3 flex items-center gap-2">
<div class="h-px flex-1 bg-[hsl(var(--border))]"></div>
<span class="text-[0.65rem] text-[hsl(var(--muted-foreground))]">oder manuell</span>
<div class="h-px flex-1 bg-[hsl(var(--border))]"></div>
<div class="h-px flex-1 bg-[hsl(var(--color-border))]"></div>
<span class="text-[0.65rem] text-[hsl(var(--color-muted-foreground))]">oder manuell</span>
<div class="h-px flex-1 bg-[hsl(var(--color-border))]"></div>
</div>
<form
@ -247,14 +247,14 @@
type="text"
bind:value={description}
placeholder={$_('entry.description')}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2.5 text-sm text-[hsl(var(--foreground))] focus:border-[hsl(var(--primary))] focus:outline-none"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2.5 text-sm text-[hsl(var(--color-foreground))] focus:border-[hsl(var(--color-primary))] focus:outline-none"
/>
<!-- Project -->
<select
value={projectId}
onchange={(e) => handleProjectChange((e.target as HTMLSelectElement).value)}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2.5 text-sm text-[hsl(var(--foreground))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2.5 text-sm text-[hsl(var(--color-foreground))]"
>
<option value="">{$_('project.internal')}</option>
{#each activeProjects as proj}
@ -273,12 +273,12 @@
<input
type="date"
bind:value={date}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2.5 text-sm text-[hsl(var(--foreground))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2.5 text-sm text-[hsl(var(--color-foreground))]"
/>
<!-- Quick Duration Buttons -->
<div>
<span class="mb-1.5 block text-xs font-medium text-[hsl(var(--muted-foreground))]">
<span class="mb-1.5 block text-xs font-medium text-[hsl(var(--color-muted-foreground))]">
{$_('entry.duration')}
</span>
<div class="mb-2 flex flex-wrap gap-2">
@ -291,8 +291,8 @@
}}
class="rounded-lg border px-3 py-1.5 text-xs transition-colors {durationHours ===
qd.h && durationMinutes === qd.m
? 'border-[hsl(var(--primary))] bg-[hsl(var(--primary)/0.1)] text-[hsl(var(--primary))]'
: 'border-[hsl(var(--border))] text-[hsl(var(--muted-foreground))] hover:border-[hsl(var(--primary)/0.5)]'}"
? 'border-[hsl(var(--color-primary))] bg-[hsl(var(--color-primary)/0.1)] text-[hsl(var(--color-primary))]'
: 'border-[hsl(var(--color-border))] text-[hsl(var(--color-muted-foreground))] hover:border-[hsl(var(--color-primary)/0.5)]'}"
>
{qd.label}
</button>
@ -306,18 +306,18 @@
bind:value={durationHours}
min="0"
max="24"
class="w-16 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-center text-sm text-[hsl(var(--foreground))]"
class="w-16 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-center text-sm text-[hsl(var(--color-foreground))]"
/>
<span class="text-xs text-[hsl(var(--muted-foreground))]">h</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">h</span>
<input
type="number"
bind:value={durationMinutes}
min="0"
max="59"
step="5"
class="w-16 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-center text-sm text-[hsl(var(--foreground))]"
class="w-16 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-center text-sm text-[hsl(var(--color-foreground))]"
/>
<span class="text-xs text-[hsl(var(--muted-foreground))]">min</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">min</span>
</div>
</div>
@ -327,8 +327,8 @@
type="button"
onclick={() => (isBillable = !isBillable)}
class="flex items-center gap-2 rounded-lg px-3 py-1.5 text-sm transition-colors {isBillable
? 'bg-[hsl(var(--primary)/0.1)] text-[hsl(var(--primary))]'
: 'text-[hsl(var(--muted-foreground))]'}"
? 'bg-[hsl(var(--color-primary)/0.1)] text-[hsl(var(--color-primary))]'
: 'text-[hsl(var(--color-muted-foreground))]'}"
>
<CurrencyDollar size={16} />
{isBillable ? $_('entry.billable') : $_('entry.notBillable')}
@ -337,7 +337,7 @@
<!-- Tags -->
<div>
<span class="mb-1.5 block text-xs font-medium text-[hsl(var(--muted-foreground))]"
<span class="mb-1.5 block text-xs font-medium text-[hsl(var(--color-muted-foreground))]"
>Tags</span
>
<TagField
@ -352,13 +352,13 @@
<button
type="button"
onclick={onClose}
class="flex-1 rounded-lg border border-[hsl(var(--border))] py-2.5 text-sm text-[hsl(var(--muted-foreground))]"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] py-2.5 text-sm text-[hsl(var(--color-muted-foreground))]"
>
{$_('common.cancel')}
</button>
<button
type="submit"
class="flex-1 rounded-lg bg-[hsl(var(--primary))] py-2.5 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="flex-1 rounded-lg bg-[hsl(var(--color-primary))] py-2.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
{$_('common.create')}
</button>

View file

@ -36,7 +36,7 @@
{#if entries.length === 0}
<div
class="rounded-xl border border-dashed border-[hsl(var(--border))] p-8 text-center text-[hsl(var(--muted-foreground))]"
class="rounded-xl border border-dashed border-[hsl(var(--color-border))] p-8 text-center text-[hsl(var(--color-muted-foreground))]"
>
<Clock size={20} class="mx-auto mb-3 opacity-50" />
<p>{$_('entry.noEntries')}</p>
@ -47,10 +47,10 @@
<div>
<!-- Day Header -->
<div class="mb-2 flex items-center justify-between">
<h3 class="text-sm font-medium text-[hsl(var(--muted-foreground))]">
<h3 class="text-sm font-medium text-[hsl(var(--color-muted-foreground))]">
{formatDateHeader(date)}
</h3>
<span class="duration-display text-sm font-medium text-[hsl(var(--foreground))]">
<span class="duration-display text-sm font-medium text-[hsl(var(--color-foreground))]">
{formatDurationCompact(getTotalDuration(dayEntries))}
</span>
</div>

View file

@ -35,7 +35,7 @@
{#if recentEntries().length > 0}
<div>
<h3 class="mb-2 text-xs font-medium text-[hsl(var(--muted-foreground))]">Quick Start</h3>
<h3 class="mb-2 text-xs font-medium text-[hsl(var(--color-muted-foreground))]">Quick Start</h3>
<div class="flex flex-wrap gap-2">
{#each recentEntries() as entry}
{@const project = entry.projectId
@ -44,12 +44,12 @@
<button
onclick={() => startFromEntry(entry)}
disabled={timerStore.isRunning}
class="flex items-center gap-1.5 rounded-full border border-[hsl(var(--border))] px-3 py-1.5 text-xs transition-colors hover:border-[hsl(var(--primary)/0.5)] hover:bg-[hsl(var(--primary)/0.05)] disabled:opacity-50"
class="flex items-center gap-1.5 rounded-full border border-[hsl(var(--color-border))] px-3 py-1.5 text-xs transition-colors hover:border-[hsl(var(--color-primary)/0.5)] hover:bg-[hsl(var(--color-primary)/0.05)] disabled:opacity-50"
>
{#if project}
<div class="h-2 w-2 rounded-full" style="background-color: {project.color}"></div>
{/if}
<span class="max-w-[150px] truncate text-[hsl(var(--foreground))]"
<span class="max-w-[150px] truncate text-[hsl(var(--color-foreground))]"
>{entry.description}</span
>
</button>

View file

@ -89,7 +89,7 @@
</script>
<div
class="rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-6 {timerStore.isRunning
class="rounded-2xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-6 {timerStore.isRunning
? 'timer-active'
: ''}"
>
@ -97,8 +97,8 @@
<div class="mb-4 text-center">
<div
class="duration-display text-5xl font-bold {timerStore.isRunning
? 'text-[hsl(var(--primary))]'
: 'text-[hsl(var(--foreground))]'}"
? 'text-[hsl(var(--color-primary))]'
: 'text-[hsl(var(--color-foreground))]'}"
>
{formattedTime}
</div>
@ -111,7 +111,7 @@
value={description}
oninput={(e) => handleDescriptionChange((e.target as HTMLInputElement).value)}
placeholder={$_('timer.whatAreYouWorkingOn')}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2.5 text-sm text-[hsl(var(--foreground))] placeholder:text-[hsl(var(--muted-foreground))] focus:border-[hsl(var(--primary))] focus:outline-none focus:ring-1 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2.5 text-sm text-[hsl(var(--color-foreground))] placeholder:text-[hsl(var(--color-muted-foreground))] focus:border-[hsl(var(--color-primary))] focus:outline-none focus:ring-1 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
@ -125,7 +125,7 @@
const val = (e.target as HTMLSelectElement).value;
handleProjectChange(val || null);
}}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))]"
>
<option value="">{$_('project.noProjects')}</option>
{#each activeProjects as project}
@ -145,8 +145,8 @@
<button
onclick={handleBillableToggle}
class="flex h-9 w-9 items-center justify-center rounded-lg border transition-colors {isBillable
? 'border-[hsl(var(--primary))] bg-[hsl(var(--primary)/0.1)] text-[hsl(var(--primary))]'
: 'border-[hsl(var(--border))] text-[hsl(var(--muted-foreground))]'}"
? 'border-[hsl(var(--color-primary))] bg-[hsl(var(--color-primary)/0.1)] text-[hsl(var(--color-primary))]'
: 'border-[hsl(var(--color-border))] text-[hsl(var(--color-muted-foreground))]'}"
title={isBillable ? $_('entry.billable') : $_('entry.notBillable')}
>
<CurrencyDollar size={16} />
@ -158,7 +158,7 @@
onclick={handleStartStop}
class="w-full rounded-xl py-3 text-lg font-medium transition-all {timerStore.isRunning
? 'bg-red-500 text-white hover:bg-red-600'
: 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] hover:opacity-90'}"
: 'bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))] hover:opacity-90'}"
>
{#if timerStore.isRunning}
<span class="flex items-center justify-center gap-2">
@ -176,7 +176,7 @@
<!-- Running info -->
{#if timerStore.isRunning && selectedProject}
<div
class="mt-3 flex items-center justify-center gap-2 text-xs text-[hsl(var(--muted-foreground))]"
class="mt-3 flex items-center justify-center gap-2 text-xs text-[hsl(var(--color-muted-foreground))]"
>
<div class="project-dot" style="background-color: {selectedProject.color}"></div>
<span>{selectedProject.name}</span>

View file

@ -23,14 +23,14 @@
{#if timerStore.isRunning}
<div
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary)/0.1)] px-3 py-1.5 border border-[hsl(var(--primary)/0.2)]"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary)/0.1)] px-3 py-1.5 border border-[hsl(var(--color-primary)/0.2)]"
>
<!-- Pulsing dot -->
<div class="relative flex h-2 w-2">
<span
class="absolute inline-flex h-full w-full animate-ping rounded-full bg-[hsl(var(--primary))] opacity-75"
class="absolute inline-flex h-full w-full animate-ping rounded-full bg-[hsl(var(--color-primary))] opacity-75"
></span>
<span class="relative inline-flex h-2 w-2 rounded-full bg-[hsl(var(--primary))]"></span>
<span class="relative inline-flex h-2 w-2 rounded-full bg-[hsl(var(--color-primary))]"></span>
</div>
<!-- Project dot + Description -->
@ -40,12 +40,14 @@
style="background-color: {project.color}"
></div>
{/if}
<span class="hidden sm:inline max-w-[120px] truncate text-xs text-[hsl(var(--foreground))]">
<span
class="hidden sm:inline max-w-[120px] truncate text-xs text-[hsl(var(--color-foreground))]"
>
{timerStore.runningEntry?.description || $_('timer.running')}
</span>
<!-- Elapsed time -->
<span class="duration-display text-xs font-medium text-[hsl(var(--primary))]">
<span class="duration-display text-xs font-medium text-[hsl(var(--color-primary))]">
{formattedTime}
</span>

View file

@ -40,7 +40,7 @@
<div class="map-placeholder">
<svg viewBox="0 0 800 400" class="map-svg">
<!-- Simple world outline -->
<rect x="0" y="0" width="800" height="400" fill="hsl(var(--muted))" opacity="0.3" />
<rect x="0" y="0" width="800" height="400" fill="hsl(var(--color-muted))" opacity="0.3" />
<!-- City markers -->
{#each cities as city}
@ -60,7 +60,7 @@
cx={x}
cy={y}
r={isSelected ? 8 : 5}
fill={isSelected ? 'hsl(var(--primary))' : 'hsl(var(--muted-foreground))'}
fill={isSelected ? 'hsl(var(--color-primary))' : 'hsl(var(--color-muted-foreground))'}
class="cursor-pointer hover:opacity-80 transition-all"
/>
{#if isSelected}
@ -69,7 +69,7 @@
y={y - 12}
text-anchor="middle"
font-size="10"
fill="hsl(var(--foreground))"
fill="hsl(var(--color-foreground))"
class="pointer-events-none"
>
{city.city}
@ -90,10 +90,10 @@
.world-map {
width: 100%;
height: 300px;
background: hsl(var(--card));
background: hsl(var(--color-card));
border-radius: 12px;
overflow: hidden;
border: 1px solid hsl(var(--border));
border: 1px solid hsl(var(--color-border));
}
.map-placeholder {

View file

@ -8,7 +8,7 @@
<style>
.apps-page-wrapper {
background-color: hsl(var(--background));
background-color: hsl(var(--color-background));
min-height: 100%;
}
</style>

View file

@ -57,21 +57,21 @@
<!-- Header -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Chat</h1>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Chat</h1>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{conversationsCtx.value.length} Konversationen
</p>
</div>
<div class="flex items-center gap-2">
<a
href="/chat/templates"
class="flex items-center gap-2 rounded-lg border border-[hsl(var(--border))] px-4 py-2 text-sm font-medium text-[hsl(var(--foreground))] transition-colors hover:bg-[hsl(var(--muted))]"
class="flex items-center gap-2 rounded-lg border border-[hsl(var(--color-border))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-foreground))] transition-colors hover:bg-[hsl(var(--color-muted))]"
>
Vorlagen
</a>
<button
onclick={handleNewChat}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] transition-colors hover:opacity-90"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] transition-colors hover:opacity-90"
>
<Plus size={20} />
Neuer Chat
@ -83,35 +83,35 @@
<div class="relative">
<MagnifyingGlass
size={18}
class="absolute left-3 top-1/2 -translate-y-1/2 text-[hsl(var(--muted-foreground))]"
class="absolute left-3 top-1/2 -translate-y-1/2 text-[hsl(var(--color-muted-foreground))]"
/>
<input
type="text"
placeholder="Konversationen durchsuchen..."
bind:value={searchQuery}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] py-2.5 pl-10 pr-4 text-sm text-[hsl(var(--foreground))] placeholder:text-[hsl(var(--muted-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] py-2.5 pl-10 pr-4 text-sm text-[hsl(var(--color-foreground))] placeholder:text-[hsl(var(--color-muted-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<!-- Conversations -->
{#if conversationsCtx.value.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-16"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<div
class="mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-[hsl(var(--primary)/0.1)]"
class="mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-[hsl(var(--color-primary)/0.1)]"
>
<Sparkle size={32} weight="duotone" class="text-[hsl(var(--primary))]" />
<Sparkle size={32} weight="duotone" class="text-[hsl(var(--color-primary))]" />
</div>
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--foreground))]">
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--color-foreground))]">
Starte deine erste Unterhaltung
</h2>
<p class="mb-6 text-sm text-[hsl(var(--muted-foreground))]">
<p class="mb-6 text-sm text-[hsl(var(--color-muted-foreground))]">
Stelle eine Frage oder bitte um Hilfe bei einem Projekt.
</p>
<button
onclick={handleNewChat}
class="rounded-lg bg-[hsl(var(--primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="rounded-lg bg-[hsl(var(--color-primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
Neuer Chat
</button>
@ -122,7 +122,7 @@
{#if pinned.length > 0}
<div>
<h3
class="mb-2 text-xs font-medium uppercase tracking-wide text-[hsl(var(--muted-foreground))]"
class="mb-2 text-xs font-medium uppercase tracking-wide text-[hsl(var(--color-muted-foreground))]"
>
Angepinnt
</h3>
@ -133,14 +133,14 @@
tabindex="0"
onclick={() => handleConversationClick(conv.id)}
onkeydown={(e) => e.key === 'Enter' && handleConversationClick(conv.id)}
class="group flex items-center gap-3 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-3 transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="group flex items-center gap-3 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-3 transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<ChatCircle size={20} class="shrink-0 text-[hsl(var(--primary))]" />
<ChatCircle size={20} class="shrink-0 text-[hsl(var(--color-primary))]" />
<div class="min-w-0 flex-1">
<p class="truncate text-sm font-medium text-[hsl(var(--foreground))]">
<p class="truncate text-sm font-medium text-[hsl(var(--color-foreground))]">
{conv.title || 'Neue Konversation'}
</p>
<p class="text-xs text-[hsl(var(--muted-foreground))]">
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
{formatDate(conv.updatedAt)}
</p>
</div>
@ -149,14 +149,14 @@
>
<button
onclick={(e) => handlePin(e, conv.id, true)}
class="rounded p-1 text-[hsl(var(--primary))] hover:bg-[hsl(var(--muted))]"
class="rounded p-1 text-[hsl(var(--color-primary))] hover:bg-[hsl(var(--color-muted))]"
title="Loslösen"
>
<PushPin size={16} weight="fill" />
</button>
<button
onclick={(e) => handleArchive(e, conv.id)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
title="Archivieren"
>
<Archive size={16} />
@ -173,7 +173,7 @@
<div>
{#if pinned.length > 0}
<h3
class="mb-2 text-xs font-medium uppercase tracking-wide text-[hsl(var(--muted-foreground))]"
class="mb-2 text-xs font-medium uppercase tracking-wide text-[hsl(var(--color-muted-foreground))]"
>
Zuletzt
</h3>
@ -185,14 +185,14 @@
tabindex="0"
onclick={() => handleConversationClick(conv.id)}
onkeydown={(e) => e.key === 'Enter' && handleConversationClick(conv.id)}
class="group flex items-center gap-3 rounded-lg border border-transparent p-3 transition-all hover:border-[hsl(var(--border))] hover:bg-[hsl(var(--card))]"
class="group flex items-center gap-3 rounded-lg border border-transparent p-3 transition-all hover:border-[hsl(var(--color-border))] hover:bg-[hsl(var(--color-card))]"
>
<ChatCircle size={20} class="shrink-0 text-[hsl(var(--muted-foreground))]" />
<ChatCircle size={20} class="shrink-0 text-[hsl(var(--color-muted-foreground))]" />
<div class="min-w-0 flex-1">
<p class="truncate text-sm font-medium text-[hsl(var(--foreground))]">
<p class="truncate text-sm font-medium text-[hsl(var(--color-foreground))]">
{conv.title || 'Neue Konversation'}
</p>
<p class="text-xs text-[hsl(var(--muted-foreground))]">
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
{formatDate(conv.updatedAt)}
</p>
</div>
@ -201,14 +201,14 @@
>
<button
onclick={(e) => handlePin(e, conv.id, false)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
title="Anpinnen"
>
<PushPin size={16} />
</button>
<button
onclick={(e) => handleArchive(e, conv.id)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
title="Archivieren"
>
<Archive size={16} />
@ -226,7 +226,7 @@
<div class="pt-2">
<a
href="/chat/archive"
class="inline-flex items-center gap-2 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="inline-flex items-center gap-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<Archive size={16} />
Archiv anzeigen

View file

@ -114,11 +114,11 @@
<div class="flex h-full flex-col">
<!-- Header -->
<div
class="flex items-center gap-3 border-b border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3"
class="flex items-center gap-3 border-b border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] px-4 py-3"
>
<a
href="/chat"
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowLeft size={20} />
</a>
@ -130,23 +130,23 @@
type="text"
bind:value={editTitle}
onkeydown={(e) => e.key === 'Enter' && saveTitle()}
class="flex-1 rounded border border-[hsl(var(--border))] bg-transparent px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-[hsl(var(--primary))]"
class="flex-1 rounded border border-[hsl(var(--color-border))] bg-transparent px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-[hsl(var(--color-primary))]"
/>
<button onclick={saveTitle} class="text-[hsl(var(--primary))]">
<button onclick={saveTitle} class="text-[hsl(var(--color-primary))]">
<Check size={18} />
</button>
<button onclick={cancelEditTitle} class="text-[hsl(var(--muted-foreground))]">
<button onclick={cancelEditTitle} class="text-[hsl(var(--color-muted-foreground))]">
<X size={18} />
</button>
</div>
{:else}
<button onclick={startEditTitle} class="group flex items-center gap-1.5 text-left">
<h1 class="truncate text-sm font-semibold text-[hsl(var(--foreground))]">
<h1 class="truncate text-sm font-semibold text-[hsl(var(--color-foreground))]">
{conversation?.title || 'Neue Konversation'}
</h1>
<PencilSimple
size={14}
class="shrink-0 text-[hsl(var(--muted-foreground))] opacity-0 group-hover:opacity-100"
class="shrink-0 text-[hsl(var(--color-muted-foreground))] opacity-0 group-hover:opacity-100"
/>
</button>
{/if}
@ -156,22 +156,22 @@
<button
onclick={togglePin}
class="rounded-lg p-1.5 transition-colors {conversation?.isPinned
? 'text-[hsl(var(--primary))]'
: 'text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]'}"
? 'text-[hsl(var(--color-primary))]'
: 'text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]'}"
title={conversation?.isPinned ? 'Loslösen' : 'Anpinnen'}
>
<PushPin size={18} weight={conversation?.isPinned ? 'fill' : 'regular'} />
</button>
<button
onclick={() => (showShare = true)}
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
title="Kurzlink teilen"
>
<ShareNetwork size={18} />
</button>
<button
onclick={handleDelete}
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:text-red-500"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
title="Loschen"
>
<Trash size={18} />
@ -183,7 +183,7 @@
<div class="flex-1 overflow-y-auto px-4 py-6">
{#if messages.length === 0}
<div class="flex h-full items-center justify-center">
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
Schreibe eine Nachricht, um die Unterhaltung zu starten.
</p>
</div>
@ -193,8 +193,8 @@
<div class="flex {msg.sender === 'user' ? 'justify-end' : 'justify-start'}">
<div
class="max-w-[80%] rounded-2xl px-4 py-2.5 text-sm {msg.sender === 'user'
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
: 'bg-[hsl(var(--muted))] text-[hsl(var(--foreground))]'}"
? 'bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))]'
: 'bg-[hsl(var(--color-muted))] text-[hsl(var(--color-foreground))]'}"
>
<p class="whitespace-pre-wrap">{msg.messageText}</p>
<p class="mt-1 text-[10px] opacity-60">
@ -210,7 +210,7 @@
{#if isSending && streamingText}
<div class="flex justify-start">
<div
class="max-w-[80%] rounded-2xl px-4 py-2.5 text-sm bg-[hsl(var(--muted))] text-[hsl(var(--foreground))]"
class="max-w-[80%] rounded-2xl px-4 py-2.5 text-sm bg-[hsl(var(--color-muted))] text-[hsl(var(--color-foreground))]"
>
<p class="whitespace-pre-wrap">{streamingText}</p>
<p class="mt-1 text-[10px] opacity-60">...</p>
@ -219,7 +219,7 @@
{:else if isSending}
<div class="flex justify-start">
<div
class="rounded-2xl px-4 py-2.5 text-sm bg-[hsl(var(--muted))] text-[hsl(var(--muted-foreground))]"
class="rounded-2xl px-4 py-2.5 text-sm bg-[hsl(var(--color-muted))] text-[hsl(var(--color-muted-foreground))]"
>
<span class="thinking-dots">●●●</span>
</div>
@ -230,19 +230,19 @@
</div>
<!-- Input -->
<div class="border-t border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<div class="border-t border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4">
<div class="mx-auto flex max-w-3xl items-end gap-3">
<textarea
bind:value={inputText}
onkeydown={handleKeydown}
placeholder="Nachricht schreiben..."
rows="1"
class="flex-1 resize-none rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-4 py-3 text-sm text-[hsl(var(--foreground))] placeholder:text-[hsl(var(--muted-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="flex-1 resize-none rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-background))] px-4 py-3 text-sm text-[hsl(var(--color-foreground))] placeholder:text-[hsl(var(--color-muted-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
></textarea>
<button
onclick={handleSend}
disabled={!inputText.trim() || isSending}
class="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] transition-colors hover:opacity-90 disabled:opacity-50"
class="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))] transition-colors hover:opacity-90 disabled:opacity-50"
>
<PaperPlaneRight size={20} />
</button>

View file

@ -41,13 +41,13 @@
<div class="flex items-center gap-3">
<a
href="/chat"
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowLeft size={20} />
</a>
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Archiv</h1>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Archiv</h1>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{archivedCtx.value.length} archivierte Konversationen
</p>
</div>
@ -55,8 +55,8 @@
{#if archivedCtx.value.length === 0}
<div class="flex flex-col items-center justify-center py-16">
<ChatCircle size={48} class="mb-4 text-[hsl(var(--muted-foreground))]" />
<p class="text-[hsl(var(--muted-foreground))]">Keine archivierten Konversationen</p>
<ChatCircle size={48} class="mb-4 text-[hsl(var(--color-muted-foreground))]" />
<p class="text-[hsl(var(--color-muted-foreground))]">Keine archivierten Konversationen</p>
</div>
{:else}
<div class="space-y-1">
@ -66,28 +66,28 @@
tabindex="0"
onclick={() => handleClick(conv.id)}
onkeydown={(e) => e.key === 'Enter' && handleClick(conv.id)}
class="group flex items-center gap-3 rounded-lg border border-transparent p-3 transition-all hover:border-[hsl(var(--border))] hover:bg-[hsl(var(--card))]"
class="group flex items-center gap-3 rounded-lg border border-transparent p-3 transition-all hover:border-[hsl(var(--color-border))] hover:bg-[hsl(var(--color-card))]"
>
<ChatCircle size={20} class="shrink-0 text-[hsl(var(--muted-foreground))]" />
<ChatCircle size={20} class="shrink-0 text-[hsl(var(--color-muted-foreground))]" />
<div class="min-w-0 flex-1">
<p class="truncate text-sm font-medium text-[hsl(var(--foreground))]">
<p class="truncate text-sm font-medium text-[hsl(var(--color-foreground))]">
{conv.title || 'Konversation ohne Titel'}
</p>
<p class="text-xs text-[hsl(var(--muted-foreground))]">
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
{formatDate(conv.updatedAt)}
</p>
</div>
<div class="flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100">
<button
onclick={(e) => handleUnarchive(e, conv.id)}
class="rounded p-1.5 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--primary))]"
class="rounded p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-primary))]"
title="Wiederherstellen"
>
<ArrowCounterClockwise size={16} />
</button>
<button
onclick={(e) => handleDelete(e, conv.id)}
class="rounded p-1.5 text-[hsl(var(--muted-foreground))] hover:text-red-500"
class="rounded p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
title="Endgultig loschen"
>
<Trash size={16} />

View file

@ -112,20 +112,20 @@
<div class="flex items-center gap-3">
<a
href="/chat"
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowLeft size={20} />
</a>
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Vorlagen</h1>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Vorlagen</h1>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
Erstelle Vorlagen mit benutzerdefinierten System-Prompts.
</p>
</div>
</div>
<button
onclick={openCreateForm}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90"
>
<Plus size={20} />
Neue Vorlage
@ -135,16 +135,16 @@
<!-- Templates Grid -->
{#if templates.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-16"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<FileText size={48} class="mb-4 text-[hsl(var(--muted-foreground))]" />
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--foreground))]">Keine Vorlagen</h2>
<p class="mb-6 text-sm text-[hsl(var(--muted-foreground))]">
<FileText size={48} class="mb-4 text-[hsl(var(--color-muted-foreground))]" />
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--color-foreground))]">Keine Vorlagen</h2>
<p class="mb-6 text-sm text-[hsl(var(--color-muted-foreground))]">
Erstelle deine erste Vorlage, um loszulegen.
</p>
<button
onclick={openCreateForm}
class="rounded-lg bg-[hsl(var(--primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="rounded-lg bg-[hsl(var(--color-primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
Neue Vorlage
</button>
@ -153,20 +153,20 @@
<div class="grid gap-4 sm:grid-cols-2">
{#each templates as template (template.id)}
<div
class="group rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-5 transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="group rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-5 transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<div class="flex items-start justify-between">
<div class="flex items-center gap-3">
<div class="h-3 w-3 rounded-full" style="background-color: {template.color}"></div>
<div>
<h3 class="font-semibold text-[hsl(var(--foreground))]">
<h3 class="font-semibold text-[hsl(var(--color-foreground))]">
{template.name}
{#if template.isDefault}
<Star size={14} weight="fill" class="ml-1 inline text-yellow-500" />
{/if}
</h3>
{#if template.description}
<p class="mt-0.5 text-xs text-[hsl(var(--muted-foreground))] line-clamp-2">
<p class="mt-0.5 text-xs text-[hsl(var(--color-muted-foreground))] line-clamp-2">
{template.description}
</p>
{/if}
@ -176,7 +176,7 @@
{#if template.documentMode}
<span
class="mt-2 inline-block rounded bg-[hsl(var(--muted))] px-2 py-0.5 text-[10px] text-[hsl(var(--muted-foreground))]"
class="mt-2 inline-block rounded bg-[hsl(var(--color-muted))] px-2 py-0.5 text-[10px] text-[hsl(var(--color-muted-foreground))]"
>
Dokumentmodus
</span>
@ -185,27 +185,27 @@
<div class="mt-4 flex items-center gap-2">
<button
onclick={() => handleUse(template)}
class="flex items-center gap-1.5 rounded-lg bg-[hsl(var(--primary))] px-3 py-1.5 text-xs font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90"
class="flex items-center gap-1.5 rounded-lg bg-[hsl(var(--color-primary))] px-3 py-1.5 text-xs font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90"
>
<Play size={14} />
Verwenden
</button>
<button
onclick={() => openEditForm(template)}
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<PencilSimple size={16} />
</button>
<button
onclick={() => handleSetDefault(template.id)}
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:text-yellow-500"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-yellow-500"
title="Als Standard setzen"
>
<Star size={16} weight={template.isDefault ? 'fill' : 'regular'} />
</button>
<button
onclick={() => handleDelete(template.id)}
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:text-red-500"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
>
<Trash size={16} />
</button>
@ -220,15 +220,15 @@
{#if showForm}
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
<div
class="w-full max-w-lg rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-6"
class="w-full max-w-lg rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-6"
>
<div class="mb-4 flex items-center justify-between">
<h2 class="text-lg font-semibold text-[hsl(var(--foreground))]">
<h2 class="text-lg font-semibold text-[hsl(var(--color-foreground))]">
{editingId ? 'Vorlage bearbeiten' : 'Neue Vorlage'}
</h2>
<button
onclick={() => (showForm = false)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<X size={20} />
</button>
@ -248,7 +248,7 @@
type="text"
bind:value={formName}
required
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
@ -257,7 +257,7 @@
id="tpl-desc"
type="text"
bind:value={formDescription}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
@ -267,7 +267,7 @@
bind:value={formSystemPrompt}
required
rows="4"
class="w-full resize-none rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-3 py-2 font-mono text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full resize-none rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-background))] px-3 py-2 font-mono text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
></textarea>
</div>
<div>
@ -279,7 +279,7 @@
aria-label="Farbe wählen"
onclick={() => (formColor = color)}
class="h-7 w-7 rounded-full border-2 transition-transform {formColor === color
? 'scale-110 border-[hsl(var(--foreground))]'
? 'scale-110 border-[hsl(var(--color-foreground))]'
: 'border-transparent hover:scale-105'}"
style="background-color: {color}"
></button>
@ -294,14 +294,14 @@
<button
type="button"
onclick={() => (showForm = false)}
class="rounded-lg px-4 py-2 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded-lg px-4 py-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
Abbrechen
</button>
<button
type="submit"
disabled={!formName.trim() || !formSystemPrompt.trim()}
class="rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90 disabled:opacity-50"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90 disabled:opacity-50"
>
{editingId ? $_('common.save') : $_('common.create')}
</button>

View file

@ -49,21 +49,21 @@
<!-- Header -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Heute</h1>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Heute</h1>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{new Date().toLocaleDateString('de-DE', { weekday: 'long', day: 'numeric', month: 'long' })}
</p>
</div>
<div class="flex gap-2">
<a
href="/food/history"
class="rounded-lg border border-[hsl(var(--border))] px-4 py-2 text-sm font-medium text-[hsl(var(--foreground))] transition-colors hover:bg-[hsl(var(--muted))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-foreground))] transition-colors hover:bg-[hsl(var(--color-muted))]"
>
Verlauf
</a>
<a
href="/food/add"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] transition-colors hover:opacity-90"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] transition-colors hover:opacity-90"
>
<Plus size={16} />
Mahlzeit
@ -74,21 +74,23 @@
<!-- Progress Cards -->
<div class="grid grid-cols-2 gap-3 sm:grid-cols-4">
<!-- Calories -->
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<div class="flex items-center gap-2">
<div
class="h-2 w-2 rounded-full"
style="background-color: {NUTRIENT_INFO.calories.color}"
></div>
<span class="text-xs font-medium text-[hsl(var(--muted-foreground))]">Kalorien</span>
<span class="text-xs font-medium text-[hsl(var(--color-muted-foreground))]">Kalorien</span>
</div>
<p class="mt-2 text-2xl font-bold text-[hsl(var(--foreground))]">
<p class="mt-2 text-2xl font-bold text-[hsl(var(--color-foreground))]">
{progress.calories.current}
</p>
<p class="text-xs text-[hsl(var(--muted-foreground))]">
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
/ {progress.calories.target} kcal
</p>
<div class="mt-2 h-1.5 overflow-hidden rounded-full bg-[hsl(var(--muted))]">
<div class="mt-2 h-1.5 overflow-hidden rounded-full bg-[hsl(var(--color-muted))]">
<div
class="h-full rounded-full transition-all {getProgressColor(
progress.calories.percentage
@ -99,21 +101,23 @@
</div>
<!-- Protein -->
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<div class="flex items-center gap-2">
<div
class="h-2 w-2 rounded-full"
style="background-color: {NUTRIENT_INFO.protein.color}"
></div>
<span class="text-xs font-medium text-[hsl(var(--muted-foreground))]">Protein</span>
<span class="text-xs font-medium text-[hsl(var(--color-muted-foreground))]">Protein</span>
</div>
<p class="mt-2 text-2xl font-bold text-[hsl(var(--foreground))]">
<p class="mt-2 text-2xl font-bold text-[hsl(var(--color-foreground))]">
{progress.protein.current}g
</p>
<p class="text-xs text-[hsl(var(--muted-foreground))]">
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
/ {progress.protein.target}g
</p>
<div class="mt-2 h-1.5 overflow-hidden rounded-full bg-[hsl(var(--muted))]">
<div class="mt-2 h-1.5 overflow-hidden rounded-full bg-[hsl(var(--color-muted))]">
<div
class="h-full rounded-full transition-all {getProgressColor(progress.protein.percentage)}"
style="width: {Math.min(progress.protein.percentage, 100)}%"
@ -122,21 +126,25 @@
</div>
<!-- Carbs -->
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<div class="flex items-center gap-2">
<div
class="h-2 w-2 rounded-full"
style="background-color: {NUTRIENT_INFO.carbohydrates.color}"
></div>
<span class="text-xs font-medium text-[hsl(var(--muted-foreground))]">Kohlenhydrate</span>
<span class="text-xs font-medium text-[hsl(var(--color-muted-foreground))]"
>Kohlenhydrate</span
>
</div>
<p class="mt-2 text-2xl font-bold text-[hsl(var(--foreground))]">
<p class="mt-2 text-2xl font-bold text-[hsl(var(--color-foreground))]">
{progress.carbs.current}g
</p>
<p class="text-xs text-[hsl(var(--muted-foreground))]">
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
/ {progress.carbs.target}g
</p>
<div class="mt-2 h-1.5 overflow-hidden rounded-full bg-[hsl(var(--muted))]">
<div class="mt-2 h-1.5 overflow-hidden rounded-full bg-[hsl(var(--color-muted))]">
<div
class="h-full rounded-full transition-all {getProgressColor(progress.carbs.percentage)}"
style="width: {Math.min(progress.carbs.percentage, 100)}%"
@ -145,18 +153,20 @@
</div>
<!-- Fat -->
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<div class="flex items-center gap-2">
<div class="h-2 w-2 rounded-full" style="background-color: {NUTRIENT_INFO.fat.color}"></div>
<span class="text-xs font-medium text-[hsl(var(--muted-foreground))]">Fett</span>
<span class="text-xs font-medium text-[hsl(var(--color-muted-foreground))]">Fett</span>
</div>
<p class="mt-2 text-2xl font-bold text-[hsl(var(--foreground))]">
<p class="mt-2 text-2xl font-bold text-[hsl(var(--color-foreground))]">
{progress.fat.current}g
</p>
<p class="text-xs text-[hsl(var(--muted-foreground))]">
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
/ {progress.fat.target}g
</p>
<div class="mt-2 h-1.5 overflow-hidden rounded-full bg-[hsl(var(--muted))]">
<div class="mt-2 h-1.5 overflow-hidden rounded-full bg-[hsl(var(--color-muted))]">
<div
class="h-full rounded-full transition-all {getProgressColor(progress.fat.percentage)}"
style="width: {Math.min(progress.fat.percentage, 100)}%"
@ -168,26 +178,26 @@
<!-- Today's Meals -->
<div>
<div class="mb-3 flex items-center justify-between">
<h2 class="text-lg font-semibold text-[hsl(var(--foreground))]">Heutige Mahlzeiten</h2>
<span class="text-sm text-[hsl(var(--muted-foreground))]">
<h2 class="text-lg font-semibold text-[hsl(var(--color-foreground))]">Heutige Mahlzeiten</h2>
<span class="text-sm text-[hsl(var(--color-muted-foreground))]">
{todaysMeals.length} Eintraege
</span>
</div>
{#if todaysMeals.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-12"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-12"
>
<span class="mb-4 text-5xl">🍽️</span>
<h3 class="mb-2 text-lg font-semibold text-[hsl(var(--foreground))]">
<h3 class="mb-2 text-lg font-semibold text-[hsl(var(--color-foreground))]">
Noch keine Mahlzeiten
</h3>
<p class="mb-4 text-sm text-[hsl(var(--muted-foreground))]">
<p class="mb-4 text-sm text-[hsl(var(--color-muted-foreground))]">
Trage deine erste Mahlzeit ein.
</p>
<a
href="/food/add"
class="rounded-lg bg-[hsl(var(--primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="rounded-lg bg-[hsl(var(--color-primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
Mahlzeit hinzufuegen
</a>
@ -197,7 +207,7 @@
{#each todaysMeals as meal (meal.id)}
<a
href="/food/{meal.id}"
class="block rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="block rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<div class="flex items-start gap-3">
{#if meal.photoThumbnailUrl || meal.photoUrl}
@ -211,24 +221,24 @@
<div class="min-w-0 flex-1">
<div class="flex items-center gap-2">
<span
class="rounded-full bg-[hsl(var(--muted))] px-2 py-0.5 text-xs font-medium text-[hsl(var(--muted-foreground))]"
class="rounded-full bg-[hsl(var(--color-muted))] px-2 py-0.5 text-xs font-medium text-[hsl(var(--color-muted-foreground))]"
>
{getMealTypeLabel(meal.mealType)}
</span>
<span class="text-xs text-[hsl(var(--muted-foreground))]">
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">
{formatTime(meal.createdAt)}
</span>
{#if meal.inputType === 'photo'}
<span class="text-xs text-[hsl(var(--muted-foreground))]">📷</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">📷</span>
{/if}
</div>
<p class="mt-1 font-medium text-[hsl(var(--foreground))]">
<p class="mt-1 font-medium text-[hsl(var(--color-foreground))]">
{meal.description}
</p>
{#if meal.nutrition}
<div
class="mt-2 flex flex-wrap gap-3 text-xs text-[hsl(var(--muted-foreground))]"
class="mt-2 flex flex-wrap gap-3 text-xs text-[hsl(var(--color-muted-foreground))]"
>
<span>{meal.nutrition.calories} kcal</span>
<span>{meal.nutrition.protein}g Protein</span>
@ -239,9 +249,11 @@
</div>
{#if meal.nutrition}
<span class="text-lg font-bold text-[hsl(var(--foreground))]">
<span class="text-lg font-bold text-[hsl(var(--color-foreground))]">
{meal.nutrition.calories}
<span class="text-xs font-normal text-[hsl(var(--muted-foreground))]">kcal</span>
<span class="text-xs font-normal text-[hsl(var(--color-muted-foreground))]"
>kcal</span
>
</span>
{/if}
</div>
@ -255,17 +267,17 @@
<div class="flex gap-3">
<a
href="/food/goals"
class="flex-1 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-center transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="flex-1 rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 text-center transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<span class="text-2xl">🎯</span>
<p class="mt-1 text-sm font-medium text-[hsl(var(--foreground))]">Ziele</p>
<p class="mt-1 text-sm font-medium text-[hsl(var(--color-foreground))]">Ziele</p>
</a>
<a
href="/food/history"
class="flex-1 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-center transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="flex-1 rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 text-center transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<span class="text-2xl">📊</span>
<p class="mt-1 text-sm font-medium text-[hsl(var(--foreground))]">Verlauf</p>
<p class="mt-1 text-sm font-medium text-[hsl(var(--color-foreground))]">Verlauf</p>
</a>
</div>
</div>

View file

@ -150,7 +150,7 @@
<div class="mx-auto max-w-2xl space-y-6">
<a
href="/food"
class="inline-flex items-center gap-2 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="inline-flex items-center gap-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<ArrowLeft class="h-4 w-4" />
Zurueck
@ -158,9 +158,9 @@
{#if !meal}
<div
class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-12 text-center"
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-12 text-center"
>
<p class="text-sm text-[hsl(var(--muted-foreground))]">Mahlzeit nicht gefunden.</p>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">Mahlzeit nicht gefunden.</p>
</div>
{:else}
{#if error}
@ -176,7 +176,7 @@
<button
type="button"
onclick={() => (lightboxOpen = true)}
class="block w-full overflow-hidden rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--muted))] transition-opacity hover:opacity-95"
class="block w-full overflow-hidden rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-muted))] transition-opacity hover:opacity-95"
aria-label="Bild vergrößern"
>
<img src={meal.photoUrl} alt={meal.description} class="max-h-96 w-full object-contain" />
@ -184,87 +184,95 @@
{/if}
<!-- Header / Metadata -->
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-6">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-6"
>
<div class="mb-3 flex items-center gap-2">
<span
class="rounded-full bg-[hsl(var(--muted))] px-2 py-0.5 text-xs font-medium text-[hsl(var(--muted-foreground))]"
class="rounded-full bg-[hsl(var(--color-muted))] px-2 py-0.5 text-xs font-medium text-[hsl(var(--color-muted-foreground))]"
>
{getMealTypeLabel(meal.mealType)}
</span>
<span class="text-xs text-[hsl(var(--muted-foreground))]">
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">
{formatDateTime(meal.createdAt)}
</span>
{#if meal.inputType === 'photo'}
<span class="text-xs text-[hsl(var(--muted-foreground))]">📷</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">📷</span>
{/if}
{#if meal.confidence > 0 && meal.confidence < 1}
<span class="ml-auto text-xs text-[hsl(var(--muted-foreground))]">
<span class="ml-auto text-xs text-[hsl(var(--color-muted-foreground))]">
KI {Math.round(meal.confidence * 100)}%
</span>
{/if}
</div>
{#if !editing}
<h1 class="text-xl font-semibold text-[hsl(var(--foreground))]">{meal.description}</h1>
<h1 class="text-xl font-semibold text-[hsl(var(--color-foreground))]">
{meal.description}
</h1>
{#if meal.nutrition}
<div class="mt-4 grid grid-cols-2 gap-3 sm:grid-cols-4">
<div class="rounded-lg bg-[hsl(var(--muted)/0.4)] p-3">
<div class="rounded-lg bg-[hsl(var(--color-muted)/0.4)] p-3">
<div class="flex items-center gap-1.5">
<div
class="h-2 w-2 rounded-full"
style="background-color: {NUTRIENT_INFO.calories.color}"
></div>
<span class="text-xs text-[hsl(var(--muted-foreground))]">Kalorien</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">Kalorien</span>
</div>
<p class="mt-1 text-lg font-bold text-[hsl(var(--foreground))]">
<p class="mt-1 text-lg font-bold text-[hsl(var(--color-foreground))]">
{meal.nutrition.calories}
<span class="text-xs font-normal text-[hsl(var(--muted-foreground))]">kcal</span>
<span class="text-xs font-normal text-[hsl(var(--color-muted-foreground))]"
>kcal</span
>
</p>
</div>
<div class="rounded-lg bg-[hsl(var(--muted)/0.4)] p-3">
<div class="rounded-lg bg-[hsl(var(--color-muted)/0.4)] p-3">
<div class="flex items-center gap-1.5">
<div
class="h-2 w-2 rounded-full"
style="background-color: {NUTRIENT_INFO.protein.color}"
></div>
<span class="text-xs text-[hsl(var(--muted-foreground))]">Protein</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">Protein</span>
</div>
<p class="mt-1 text-lg font-bold text-[hsl(var(--foreground))]">
<p class="mt-1 text-lg font-bold text-[hsl(var(--color-foreground))]">
{meal.nutrition.protein}<span
class="text-xs font-normal text-[hsl(var(--muted-foreground))]">g</span
class="text-xs font-normal text-[hsl(var(--color-muted-foreground))]">g</span
>
</p>
</div>
<div class="rounded-lg bg-[hsl(var(--muted)/0.4)] p-3">
<div class="rounded-lg bg-[hsl(var(--color-muted)/0.4)] p-3">
<div class="flex items-center gap-1.5">
<div
class="h-2 w-2 rounded-full"
style="background-color: {NUTRIENT_INFO.carbohydrates.color}"
></div>
<span class="text-xs text-[hsl(var(--muted-foreground))]">Kohlenhydrate</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">Kohlenhydrate</span>
</div>
<p class="mt-1 text-lg font-bold text-[hsl(var(--foreground))]">
<p class="mt-1 text-lg font-bold text-[hsl(var(--color-foreground))]">
{meal.nutrition.carbohydrates}<span
class="text-xs font-normal text-[hsl(var(--muted-foreground))]">g</span
class="text-xs font-normal text-[hsl(var(--color-muted-foreground))]">g</span
>
</p>
</div>
<div class="rounded-lg bg-[hsl(var(--muted)/0.4)] p-3">
<div class="rounded-lg bg-[hsl(var(--color-muted)/0.4)] p-3">
<div class="flex items-center gap-1.5">
<div
class="h-2 w-2 rounded-full"
style="background-color: {NUTRIENT_INFO.fat.color}"
></div>
<span class="text-xs text-[hsl(var(--muted-foreground))]">Fett</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">Fett</span>
</div>
<p class="mt-1 text-lg font-bold text-[hsl(var(--foreground))]">
<p class="mt-1 text-lg font-bold text-[hsl(var(--color-foreground))]">
{meal.nutrition.fat}<span
class="text-xs font-normal text-[hsl(var(--muted-foreground))]">g</span
class="text-xs font-normal text-[hsl(var(--color-muted-foreground))]">g</span
>
</p>
</div>
</div>
<div class="mt-2 grid grid-cols-2 gap-3 text-xs text-[hsl(var(--muted-foreground))]">
<div
class="mt-2 grid grid-cols-2 gap-3 text-xs text-[hsl(var(--color-muted-foreground))]"
>
<div>Ballaststoffe: {meal.nutrition.fiber}g</div>
<div>Zucker: {meal.nutrition.sugar}g</div>
</div>
@ -274,7 +282,7 @@
<button
type="button"
onclick={startEdit}
class="rounded-lg border border-[hsl(var(--border))] px-4 py-2 text-sm text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
Bearbeiten
</button>
@ -283,7 +291,7 @@
type="button"
onclick={handleReanalyze}
disabled={reanalyzing}
class="rounded-lg border border-[hsl(var(--border))] px-4 py-2 text-sm text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))] disabled:opacity-50"
class="rounded-lg border border-[hsl(var(--color-border))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))] hover:bg-[hsl(var(--color-muted))] disabled:opacity-50"
>
{reanalyzing ? 'Analysiere…' : '🔄 Erneut analysieren'}
</button>
@ -292,18 +300,18 @@
<button
type="button"
onclick={() => (confirmDelete = true)}
class="ml-auto inline-flex items-center gap-1 rounded-lg border border-[hsl(var(--border))] px-4 py-2 text-sm text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20"
class="ml-auto inline-flex items-center gap-1 rounded-lg border border-[hsl(var(--color-border))] px-4 py-2 text-sm text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-900/20"
>
<Trash size={14} />
Löschen
</button>
{:else}
<div class="ml-auto flex items-center gap-2">
<span class="text-xs text-[hsl(var(--muted-foreground))]">Sicher?</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">Sicher?</span>
<button
type="button"
onclick={() => (confirmDelete = false)}
class="rounded-lg border border-[hsl(var(--border))] px-3 py-1.5 text-xs text-[hsl(var(--foreground))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 py-1.5 text-xs text-[hsl(var(--color-foreground))]"
>
Abbrechen
</button>
@ -321,7 +329,7 @@
<!-- Edit form -->
<div class="space-y-5">
<div>
<span class="mb-2 block text-sm font-medium text-[hsl(var(--foreground))]">
<span class="mb-2 block text-sm font-medium text-[hsl(var(--color-foreground))]">
Mahlzeittyp
</span>
<div class="grid grid-cols-4 gap-2">
@ -331,8 +339,8 @@
onclick={() => (editMealType = type)}
class="rounded-lg border-2 px-3 py-2 text-sm transition-all
{editMealType === type
? 'border-[hsl(var(--primary))] bg-[hsl(var(--primary)/0.05)] font-medium'
: 'border-[hsl(var(--border))] hover:border-[hsl(var(--primary)/0.3)]'}"
? 'border-[hsl(var(--color-primary))] bg-[hsl(var(--color-primary)/0.05)] font-medium'
: 'border-[hsl(var(--color-border))] hover:border-[hsl(var(--color-primary)/0.3)]'}"
>
{MEAL_TYPE_LABELS[type].de}
</button>
@ -343,7 +351,7 @@
<div>
<label
for="edit-desc"
class="mb-2 block text-sm font-medium text-[hsl(var(--foreground))]"
class="mb-2 block text-sm font-medium text-[hsl(var(--color-foreground))]"
>
Beschreibung
</label>
@ -351,17 +359,17 @@
id="edit-desc"
bind:value={editDescription}
rows="3"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-3 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-3 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
></textarea>
</div>
<div>
<h3 class="mb-3 text-sm font-medium text-[hsl(var(--foreground))]">Naehrwerte</h3>
<h3 class="mb-3 text-sm font-medium text-[hsl(var(--color-foreground))]">Naehrwerte</h3>
<div class="grid grid-cols-2 gap-3 sm:grid-cols-3">
<div>
<label
for="edit-cal"
class="mb-1 block text-xs text-[hsl(var(--muted-foreground))]"
class="mb-1 block text-xs text-[hsl(var(--color-muted-foreground))]"
>
Kalorien (kcal)
</label>
@ -370,13 +378,13 @@
type="number"
bind:value={editCalories}
min="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
<label
for="edit-prot"
class="mb-1 block text-xs text-[hsl(var(--muted-foreground))]"
class="mb-1 block text-xs text-[hsl(var(--color-muted-foreground))]"
>
Protein (g)
</label>
@ -385,13 +393,13 @@
type="number"
bind:value={editProtein}
min="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
<label
for="edit-carbs"
class="mb-1 block text-xs text-[hsl(var(--muted-foreground))]"
class="mb-1 block text-xs text-[hsl(var(--color-muted-foreground))]"
>
Kohlenhydrate (g)
</label>
@ -400,13 +408,13 @@
type="number"
bind:value={editCarbs}
min="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
<label
for="edit-fat"
class="mb-1 block text-xs text-[hsl(var(--muted-foreground))]"
class="mb-1 block text-xs text-[hsl(var(--color-muted-foreground))]"
>
Fett (g)
</label>
@ -415,13 +423,13 @@
type="number"
bind:value={editFat}
min="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
<label
for="edit-fiber"
class="mb-1 block text-xs text-[hsl(var(--muted-foreground))]"
class="mb-1 block text-xs text-[hsl(var(--color-muted-foreground))]"
>
Ballaststoffe (g)
</label>
@ -430,13 +438,13 @@
type="number"
bind:value={editFiber}
min="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
<label
for="edit-sugar"
class="mb-1 block text-xs text-[hsl(var(--muted-foreground))]"
class="mb-1 block text-xs text-[hsl(var(--color-muted-foreground))]"
>
Zucker (g)
</label>
@ -445,7 +453,7 @@
type="number"
bind:value={editSugar}
min="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
</div>
@ -455,7 +463,7 @@
<button
type="button"
onclick={cancelEdit}
class="flex-1 rounded-lg border border-[hsl(var(--border))] px-4 py-3 text-sm font-medium text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))]"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] px-4 py-3 text-sm font-medium text-[hsl(var(--color-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
Abbrechen
</button>
@ -463,7 +471,7 @@
type="button"
onclick={saveEdit}
disabled={saving || !editDescription.trim()}
class="flex-1 rounded-lg bg-[hsl(var(--primary))] px-4 py-3 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90 disabled:opacity-50"
class="flex-1 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-3 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90 disabled:opacity-50"
>
{saving ? 'Speichere…' : 'Speichern'}
</button>
@ -474,25 +482,27 @@
<!-- Foods Breakdown -->
{#if !editing && meal.foods && meal.foods.length > 0}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-6">
<h2 class="mb-3 text-sm font-semibold text-[hsl(var(--foreground))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-6"
>
<h2 class="mb-3 text-sm font-semibold text-[hsl(var(--color-foreground))]">
Erkannte Bestandteile
</h2>
<ul class="space-y-2">
{#each meal.foods as food}
<li
class="flex items-baseline justify-between gap-2 border-b border-[hsl(var(--border))] pb-2 last:border-b-0 last:pb-0"
class="flex items-baseline justify-between gap-2 border-b border-[hsl(var(--color-border))] pb-2 last:border-b-0 last:pb-0"
>
<div class="text-sm text-[hsl(var(--foreground))]">
<div class="text-sm text-[hsl(var(--color-foreground))]">
{food.name}
{#if food.quantity}
<span class="text-xs text-[hsl(var(--muted-foreground))]">
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">
· {food.quantity}</span
>
{/if}
</div>
{#if food.calories != null}
<span class="whitespace-nowrap text-sm text-[hsl(var(--muted-foreground))]">
<span class="whitespace-nowrap text-sm text-[hsl(var(--color-muted-foreground))]">
{food.calories} kcal
</span>
{/if}

View file

@ -243,25 +243,25 @@
<div>
<a
href="/food"
class="mb-4 inline-flex items-center gap-2 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="mb-4 inline-flex items-center gap-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<ArrowLeft class="h-4 w-4" />
Zurueck
</a>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Mahlzeit hinzufuegen</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Mahlzeit hinzufuegen</h1>
</div>
<!-- Mode Toggle -->
<div
class="grid grid-cols-2 gap-2 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-1"
class="grid grid-cols-2 gap-2 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-1"
>
<button
type="button"
onclick={() => switchMode('text')}
class="rounded-md px-4 py-2 text-sm font-medium transition-colors
{mode === 'text'
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
: 'text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]'}"
? 'bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))]'
: 'text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]'}"
>
Text
</button>
@ -270,8 +270,8 @@
onclick={() => switchMode('photo')}
class="rounded-md px-4 py-2 text-sm font-medium transition-colors
{mode === 'photo'
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
: 'text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]'}"
? 'bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))]'
: 'text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]'}"
>
📷 Foto
</button>
@ -285,7 +285,9 @@
<!-- Photo Capture (only in photo mode) -->
{#if mode === 'photo'}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-6 space-y-4">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-6 space-y-4"
>
<input
bind:this={fileInput}
type="file"
@ -299,26 +301,26 @@
<button
type="button"
onclick={() => fileInput?.click()}
class="flex w-full flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-12 transition-colors hover:border-[hsl(var(--primary)/0.5)]"
class="flex w-full flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-12 transition-colors hover:border-[hsl(var(--color-primary)/0.5)]"
>
<span class="text-4xl">📷</span>
<span class="text-sm font-medium text-[hsl(var(--foreground))]">
<span class="text-sm font-medium text-[hsl(var(--color-foreground))]">
Foto aufnehmen oder hochladen
</span>
<span class="text-xs text-[hsl(var(--muted-foreground))]">
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">
Die KI erkennt das Gericht und schätzt die Nährwerte
</span>
</button>
{:else}
<div class="space-y-3">
<div class="relative overflow-hidden rounded-lg bg-[hsl(var(--muted))]">
<div class="relative overflow-hidden rounded-lg bg-[hsl(var(--color-muted))]">
<img src={photoPreviewUrl} alt="Mahlzeit" class="max-h-80 w-full object-contain" />
</div>
<div class="flex gap-2">
<button
type="button"
onclick={() => fileInput?.click()}
class="flex-1 rounded-lg border border-[hsl(var(--border))] px-3 py-2 text-sm text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))]"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
Anderes Foto
</button>
@ -327,7 +329,7 @@
type="button"
onclick={handleAnalyzePhoto}
disabled={analyzing}
class="flex-[2] rounded-lg bg-[hsl(var(--primary))] px-3 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90 disabled:opacity-50"
class="flex-[2] rounded-lg bg-[hsl(var(--color-primary))] px-3 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90 disabled:opacity-50"
>
{analyzing ? 'Analysiere…' : '✨ Mit KI analysieren'}
</button>
@ -336,7 +338,7 @@
type="button"
onclick={handleAnalyzePhoto}
disabled={analyzing}
class="flex-[2] rounded-lg border border-[hsl(var(--border))] px-3 py-2 text-sm text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))] disabled:opacity-50"
class="flex-[2] rounded-lg border border-[hsl(var(--color-border))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] hover:bg-[hsl(var(--color-muted))] disabled:opacity-50"
>
{analyzing ? 'Analysiere…' : '🔄 Erneut analysieren'}
</button>
@ -361,22 +363,24 @@
{#if analyzed && aiFoods && aiFoods.length > 0}
<div
class="rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.3)] p-3"
class="rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-muted)/0.3)] p-3"
>
<p class="mb-2 text-xs font-medium text-[hsl(var(--muted-foreground))]">
<p class="mb-2 text-xs font-medium text-[hsl(var(--color-muted-foreground))]">
Erkannte Bestandteile
</p>
<ul class="space-y-1">
{#each aiFoods as food}
<li class="flex items-baseline justify-between gap-2 text-xs">
<span class="text-[hsl(var(--foreground))]">
<span class="text-[hsl(var(--color-foreground))]">
{food.name}
{#if food.quantity}
<span class="text-[hsl(var(--muted-foreground))]"> · {food.quantity}</span>
<span class="text-[hsl(var(--color-muted-foreground))]">
· {food.quantity}</span
>
{/if}
</span>
{#if food.calories != null}
<span class="whitespace-nowrap text-[hsl(var(--muted-foreground))]">
<span class="whitespace-nowrap text-[hsl(var(--color-muted-foreground))]">
{food.calories} kcal
</span>
{/if}
@ -393,12 +397,12 @@
<!-- Favorites (only in text mode) -->
{#if mode === 'text' && favorites.length > 0}
<div>
<h3 class="mb-2 text-sm font-medium text-[hsl(var(--foreground))]">Favoriten</h3>
<h3 class="mb-2 text-sm font-medium text-[hsl(var(--color-foreground))]">Favoriten</h3>
<div class="flex flex-wrap gap-2">
{#each favorites as fav (fav.id)}
<button
onclick={() => applyFavorite(fav)}
class="rounded-full border border-[hsl(var(--border))] px-3 py-1.5 text-sm text-[hsl(var(--foreground))] transition-colors hover:bg-[hsl(var(--muted))]"
class="rounded-full border border-[hsl(var(--color-border))] px-3 py-1.5 text-sm text-[hsl(var(--color-foreground))] transition-colors hover:bg-[hsl(var(--color-muted))]"
>
{fav.name}
</button>
@ -407,10 +411,12 @@
</div>
{/if}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-6 space-y-5">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-6 space-y-5"
>
<!-- Meal Type -->
<div>
<span class="mb-2 block text-sm font-medium text-[hsl(var(--foreground))]">
<span class="mb-2 block text-sm font-medium text-[hsl(var(--color-foreground))]">
Mahlzeittyp
</span>
<div class="grid grid-cols-4 gap-2">
@ -420,8 +426,8 @@
onclick={() => (mealType = type)}
class="rounded-lg border-2 px-3 py-2 text-sm transition-all
{mealType === type
? 'border-[hsl(var(--primary))] bg-[hsl(var(--primary)/0.05)] font-medium'
: 'border-[hsl(var(--border))] hover:border-[hsl(var(--primary)/0.3)]'}"
? 'border-[hsl(var(--color-primary))] bg-[hsl(var(--color-primary)/0.05)] font-medium'
: 'border-[hsl(var(--color-border))] hover:border-[hsl(var(--color-primary)/0.3)]'}"
>
{MEAL_TYPE_LABELS[type].de}
</button>
@ -432,10 +438,13 @@
<!-- Description -->
<div>
<div class="mb-2 flex items-center justify-between">
<label for="meal-desc" class="block text-sm font-medium text-[hsl(var(--foreground))]">
<label
for="meal-desc"
class="block text-sm font-medium text-[hsl(var(--color-foreground))]"
>
Beschreibung
{#if mode === 'photo' && analyzed}
<span class="text-xs font-normal text-[hsl(var(--muted-foreground))]"
<span class="text-xs font-normal text-[hsl(var(--color-muted-foreground))]"
>(KI-Vorschlag, editierbar)</span
>
{/if}
@ -445,7 +454,7 @@
type="button"
onclick={handleSuggestFromText}
disabled={textAnalyzing || !description.trim()}
class="rounded-md border border-[hsl(var(--border))] px-2.5 py-1 text-xs font-medium text-[hsl(var(--foreground))] transition-colors hover:bg-[hsl(var(--muted))] disabled:opacity-50"
class="rounded-md border border-[hsl(var(--color-border))] px-2.5 py-1 text-xs font-medium text-[hsl(var(--color-foreground))] transition-colors hover:bg-[hsl(var(--color-muted))] disabled:opacity-50"
>
{textAnalyzing ? 'Analysiere…' : '✨ KI-Vorschlag'}
</button>
@ -456,7 +465,7 @@
bind:value={description}
placeholder="Was hast du gegessen?"
rows="3"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-3 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-3 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
></textarea>
{#if mode === 'text' && textAnalyzed && textConfidencePct !== null}
<div
@ -477,23 +486,23 @@
<!-- Nutrition -->
<div>
<h3 class="mb-3 text-sm font-medium text-[hsl(var(--foreground))]">
<h3 class="mb-3 text-sm font-medium text-[hsl(var(--color-foreground))]">
Naehrwerte
{#if mode === 'photo' && analyzed}
<span class="text-xs font-normal text-[hsl(var(--muted-foreground))]"
<span class="text-xs font-normal text-[hsl(var(--color-muted-foreground))]"
>(KI-Schätzung, editierbar)</span
>
{:else if mode === 'text' && textAnalyzed}
<span class="text-xs font-normal text-[hsl(var(--muted-foreground))]"
<span class="text-xs font-normal text-[hsl(var(--color-muted-foreground))]"
>(KI-Schätzung, editierbar)</span
>
{:else}
<span class="text-[hsl(var(--muted-foreground))]">(optional)</span>
<span class="text-[hsl(var(--color-muted-foreground))]">(optional)</span>
{/if}
</h3>
<div class="grid grid-cols-2 gap-3 sm:grid-cols-3">
<div>
<label for="n-cal" class="mb-1 block text-xs text-[hsl(var(--muted-foreground))]">
<label for="n-cal" class="mb-1 block text-xs text-[hsl(var(--color-muted-foreground))]">
Kalorien (kcal)
</label>
<input
@ -502,11 +511,11 @@
bind:value={calories}
min="0"
placeholder="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
<label for="n-prot" class="mb-1 block text-xs text-[hsl(var(--muted-foreground))]">
<label for="n-prot" class="mb-1 block text-xs text-[hsl(var(--color-muted-foreground))]">
Protein (g)
</label>
<input
@ -515,11 +524,11 @@
bind:value={protein}
min="0"
placeholder="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
<label for="n-carbs" class="mb-1 block text-xs text-[hsl(var(--muted-foreground))]">
<label for="n-carbs" class="mb-1 block text-xs text-[hsl(var(--color-muted-foreground))]">
Kohlenhydrate (g)
</label>
<input
@ -528,11 +537,11 @@
bind:value={carbohydrates}
min="0"
placeholder="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
<label for="n-fat" class="mb-1 block text-xs text-[hsl(var(--muted-foreground))]">
<label for="n-fat" class="mb-1 block text-xs text-[hsl(var(--color-muted-foreground))]">
Fett (g)
</label>
<input
@ -541,11 +550,11 @@
bind:value={fat}
min="0"
placeholder="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
<label for="n-fiber" class="mb-1 block text-xs text-[hsl(var(--muted-foreground))]">
<label for="n-fiber" class="mb-1 block text-xs text-[hsl(var(--color-muted-foreground))]">
Ballaststoffe (g)
</label>
<input
@ -554,11 +563,11 @@
bind:value={fiber}
min="0"
placeholder="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
<label for="n-sugar" class="mb-1 block text-xs text-[hsl(var(--muted-foreground))]">
<label for="n-sugar" class="mb-1 block text-xs text-[hsl(var(--color-muted-foreground))]">
Zucker (g)
</label>
<input
@ -567,7 +576,7 @@
bind:value={sugar}
min="0"
placeholder="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
</div>
@ -577,7 +586,7 @@
<div class="flex gap-3 pt-2">
<a
href="/food"
class="flex-1 rounded-lg border border-[hsl(var(--border))] px-4 py-3 text-center text-sm font-medium text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))]"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] px-4 py-3 text-center text-sm font-medium text-[hsl(var(--color-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
Abbrechen
</a>
@ -585,7 +594,7 @@
type="button"
onclick={handleSubmit}
disabled={saving || !description.trim() || (mode === 'photo' && !photoMediaId)}
class="flex-1 rounded-lg bg-[hsl(var(--primary))] px-4 py-3 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90 disabled:opacity-50"
class="flex-1 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-3 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90 disabled:opacity-50"
>
{saving ? $_('common.saving') : $_('common.save')}
</button>

View file

@ -80,13 +80,13 @@
<div>
<a
href="/food"
class="mb-4 inline-flex items-center gap-2 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="mb-4 inline-flex items-center gap-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<ArrowLeft class="h-4 w-4" />
Zurueck
</a>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Tagesziele</h1>
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Tagesziele</h1>
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))]">
Passe deine taeglichen Naehrwertziele an
</p>
</div>
@ -99,12 +99,14 @@
</div>
{/if}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-6 space-y-5">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-6 space-y-5"
>
<!-- Calories -->
<div>
<label
for="g-cal"
class="mb-1 flex items-center gap-2 text-sm font-medium text-[hsl(var(--foreground))]"
class="mb-1 flex items-center gap-2 text-sm font-medium text-[hsl(var(--color-foreground))]"
>
<div
class="h-3 w-3 rounded-full"
@ -118,7 +120,7 @@
bind:value={dailyCalories}
min="0"
step="50"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
@ -126,7 +128,7 @@
<div>
<label
for="g-prot"
class="mb-1 flex items-center gap-2 text-sm font-medium text-[hsl(var(--foreground))]"
class="mb-1 flex items-center gap-2 text-sm font-medium text-[hsl(var(--color-foreground))]"
>
<div
class="h-3 w-3 rounded-full"
@ -139,7 +141,7 @@
type="number"
bind:value={dailyProtein}
min="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
@ -147,7 +149,7 @@
<div>
<label
for="g-carbs"
class="mb-1 flex items-center gap-2 text-sm font-medium text-[hsl(var(--foreground))]"
class="mb-1 flex items-center gap-2 text-sm font-medium text-[hsl(var(--color-foreground))]"
>
<div
class="h-3 w-3 rounded-full"
@ -160,7 +162,7 @@
type="number"
bind:value={dailyCarbs}
min="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
@ -168,7 +170,7 @@
<div>
<label
for="g-fat"
class="mb-1 flex items-center gap-2 text-sm font-medium text-[hsl(var(--foreground))]"
class="mb-1 flex items-center gap-2 text-sm font-medium text-[hsl(var(--color-foreground))]"
>
<div class="h-3 w-3 rounded-full" style="background-color: {NUTRIENT_INFO.fat.color}"></div>
Fett (g)
@ -178,7 +180,7 @@
type="number"
bind:value={dailyFat}
min="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
@ -186,7 +188,7 @@
<div>
<label
for="g-fiber"
class="mb-1 flex items-center gap-2 text-sm font-medium text-[hsl(var(--foreground))]"
class="mb-1 flex items-center gap-2 text-sm font-medium text-[hsl(var(--color-foreground))]"
>
<div
class="h-3 w-3 rounded-full"
@ -199,7 +201,7 @@
type="number"
bind:value={dailyFiber}
min="0"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
@ -207,14 +209,14 @@
<div class="flex gap-3 pt-2">
<button
onclick={resetToDefaults}
class="rounded-lg border border-[hsl(var(--border))] px-4 py-2.5 text-sm text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-4 py-2.5 text-sm text-[hsl(var(--color-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
Standardwerte
</button>
<button
onclick={handleSave}
disabled={saving}
class="flex-1 rounded-lg bg-[hsl(var(--primary))] px-4 py-2.5 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90 disabled:opacity-50"
class="flex-1 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90 disabled:opacity-50"
>
{saving ? 'Speichert...' : 'Ziele speichern'}
</button>
@ -222,8 +224,10 @@
</div>
<!-- Info -->
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.3)] p-4">
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-muted)/0.3)] p-4"
>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
Die Standardwerte basieren auf einer 2000 kcal Diaet. Passe sie an deine individuellen
Beduerfnisse an. Konsultiere bei Bedarf einen Ernaehrungsberater.
</p>

View file

@ -94,13 +94,13 @@
<div>
<a
href="/food"
class="mb-4 inline-flex items-center gap-2 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="mb-4 inline-flex items-center gap-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<ArrowLeft class="h-4 w-4" />
Zurueck
</a>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Mahlzeiten-Verlauf</h1>
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Mahlzeiten-Verlauf</h1>
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))]">
{meals.length} Eintraege insgesamt
</p>
</div>
@ -109,24 +109,24 @@
<div class="flex gap-3">
<div class="relative flex-1">
<MagnifyingGlass
class="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[hsl(var(--muted-foreground))]"
class="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[hsl(var(--color-muted-foreground))]"
/>
<input
type="text"
bind:value={searchQuery}
placeholder="Mahlzeiten durchsuchen..."
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] py-2 pl-10 pr-4 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] py-2 pl-10 pr-4 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<input
type="date"
bind:value={selectedDate}
class="rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
{#if selectedDate}
<button
onclick={() => (selectedDate = '')}
class="rounded-lg border border-[hsl(var(--border))] px-3 py-2 text-sm text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 py-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
Reset
</button>
@ -136,11 +136,13 @@
<!-- Grouped Meals -->
{#if groupedByDate.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-16"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<span class="mb-4 text-5xl">📋</span>
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--foreground))]">Keine Eintraege</h2>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--color-foreground))]">
Keine Eintraege
</h2>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{searchQuery || selectedDate
? 'Keine Ergebnisse fuer diese Filter.'
: 'Noch keine Mahlzeiten erfasst.'}
@ -151,10 +153,10 @@
{#each groupedByDate as group (group.date)}
<div>
<div class="mb-2 flex items-center justify-between">
<h3 class="font-semibold text-[hsl(var(--foreground))]">
<h3 class="font-semibold text-[hsl(var(--color-foreground))]">
{formatDateHeader(group.date)}
</h3>
<span class="text-sm text-[hsl(var(--muted-foreground))]">
<span class="text-sm text-[hsl(var(--color-muted-foreground))]">
{Math.round(group.totalCalories)} kcal
</span>
</div>
@ -162,7 +164,7 @@
<div class="space-y-2">
{#each group.meals as meal (meal.id)}
<div
class="group flex items-center gap-4 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-3"
class="group flex items-center gap-4 rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-3"
>
<a href="/food/{meal.id}" class="flex min-w-0 flex-1 items-center gap-4">
{#if meal.photoThumbnailUrl || meal.photoUrl}
@ -176,25 +178,25 @@
<div class="min-w-0 flex-1">
<div class="flex items-center gap-2">
<span
class="rounded bg-[hsl(var(--muted))] px-1.5 py-0.5 text-[10px] font-medium text-[hsl(var(--muted-foreground))]"
class="rounded bg-[hsl(var(--color-muted))] px-1.5 py-0.5 text-[10px] font-medium text-[hsl(var(--color-muted-foreground))]"
>
{getMealTypeLabel(meal.mealType)}
</span>
<span class="text-xs text-[hsl(var(--muted-foreground))]">
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">
{formatTime(meal.createdAt)}
</span>
{#if meal.inputType === 'photo'}
<span class="text-xs text-[hsl(var(--muted-foreground))]">📷</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">📷</span>
{/if}
</div>
<p class="mt-1 text-sm text-[hsl(var(--foreground))] truncate">
<p class="mt-1 text-sm text-[hsl(var(--color-foreground))] truncate">
{meal.description}
</p>
</div>
{#if meal.nutrition}
<span
class="whitespace-nowrap text-sm font-medium text-[hsl(var(--foreground))]"
class="whitespace-nowrap text-sm font-medium text-[hsl(var(--color-foreground))]"
>
{meal.nutrition.calories} kcal
</span>
@ -203,7 +205,7 @@
<button
onclick={() => deleteMeal(meal.id)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
title="Loeschen"
>
<Trash size={16} />

View file

@ -44,14 +44,14 @@
<!-- Header -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Sammlungen</h1>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Sammlungen</h1>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{totalCollections} Sammlungen &middot; {totalItems} Items
</p>
</div>
<a
href="/inventory/collections/new"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] transition-colors hover:opacity-90"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] transition-colors hover:opacity-90"
>
<Plus size={20} />
Neue Sammlung
@ -61,16 +61,18 @@
<!-- Collections grid -->
{#if sortedCollections.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-16"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<span class="mb-4 text-5xl">📦</span>
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--foreground))]">Keine Sammlungen</h2>
<p class="mb-6 text-sm text-[hsl(var(--muted-foreground))]">
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--color-foreground))]">
Keine Sammlungen
</h2>
<p class="mb-6 text-sm text-[hsl(var(--color-muted-foreground))]">
Erstelle deine erste Sammlung, um loszulegen.
</p>
<a
href="/inventory/collections/new"
class="rounded-lg bg-[hsl(var(--primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="rounded-lg bg-[hsl(var(--color-primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
Neue Sammlung
</a>
@ -83,15 +85,15 @@
tabindex="0"
onclick={() => handleCollectionClick(collection)}
onkeydown={(e) => e.key === 'Enter' && handleCollectionClick(collection)}
class="group rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-5 text-left transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="group rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-5 text-left transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<div class="flex items-start justify-between">
<div class="flex items-center gap-3">
<span class="text-2xl">{collection.icon || '📁'}</span>
<div>
<h3 class="font-semibold text-[hsl(var(--foreground))]">{collection.name}</h3>
<h3 class="font-semibold text-[hsl(var(--color-foreground))]">{collection.name}</h3>
{#if collection.description}
<p class="mt-0.5 text-xs text-[hsl(var(--muted-foreground))] line-clamp-1">
<p class="mt-0.5 text-xs text-[hsl(var(--color-muted-foreground))] line-clamp-1">
{collection.description}
</p>
{/if}
@ -99,27 +101,27 @@
</div>
<button
onclick={(e) => handleDelete(e, collection.id)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
>
<Trash size={20} />
</button>
</div>
<div class="mt-4 flex items-center justify-between">
<span class="text-sm text-[hsl(var(--muted-foreground))]">
<span class="text-sm text-[hsl(var(--color-muted-foreground))]">
{getItemCount(collection.id)} Items
</span>
<div class="flex gap-1">
{#each collection.schema.fields.slice(0, 3) as field}
<span
class="rounded bg-[hsl(var(--muted))] px-1.5 py-0.5 text-[10px] text-[hsl(var(--muted-foreground))]"
class="rounded bg-[hsl(var(--color-muted))] px-1.5 py-0.5 text-[10px] text-[hsl(var(--color-muted-foreground))]"
>
{field.name}
</span>
{/each}
{#if collection.schema.fields.length > 3}
<span
class="rounded bg-[hsl(var(--muted))] px-1.5 py-0.5 text-[10px] text-[hsl(var(--muted-foreground))]"
class="rounded bg-[hsl(var(--color-muted))] px-1.5 py-0.5 text-[10px] text-[hsl(var(--color-muted-foreground))]"
>
+{collection.schema.fields.length - 3}
</span>

View file

@ -56,7 +56,7 @@
let sortedCategories = $derived([...categoriesCtx.value].sort((a, b) => a.order - b.order));
const inputClass =
'rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]';
'rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]';
</script>
<svelte:head>
@ -65,10 +65,10 @@
<div class="mx-auto max-w-2xl space-y-6">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Kategorien</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Kategorien</h1>
<button
onclick={startCreate}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
<Plus size={16} />
Neue Kategorie
@ -76,7 +76,9 @@
</div>
{#if showForm}
<div class="rounded-xl border border-[hsl(var(--primary)/0.3)] bg-[hsl(var(--card))] p-4">
<div
class="rounded-xl border border-[hsl(var(--color-primary)/0.3)] bg-[hsl(var(--color-card))] p-4"
>
<div class="flex gap-2">
<input
type="text"
@ -95,17 +97,17 @@
<input
type="color"
bind:value={color}
class="h-9 w-9 cursor-pointer rounded-lg border border-[hsl(var(--border))]"
class="h-9 w-9 cursor-pointer rounded-lg border border-[hsl(var(--color-border))]"
/>
<button
onclick={save}
disabled={!name.trim()}
class="rounded-lg bg-[hsl(var(--primary))] px-4 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 text-sm text-[hsl(var(--color-primary-foreground))] disabled:opacity-50"
>{$_('common.save')}</button
>
<button
onclick={() => (showForm = false)}
class="rounded-lg border border-[hsl(var(--border))] px-3 text-sm"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 text-sm"
>{$_('common.cancel')}</button
>
</div>
@ -114,30 +116,31 @@
{#if sortedCategories.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-16"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<span class="mb-4 text-4xl">🏷️</span>
<p class="text-[hsl(var(--muted-foreground))]">Keine Kategorien vorhanden</p>
<p class="text-[hsl(var(--color-muted-foreground))]">Keine Kategorien vorhanden</p>
</div>
{:else}
<div class="grid gap-3 sm:grid-cols-2">
{#each sortedCategories as category (category.id)}
<div
class="group flex items-center gap-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3"
class="group flex items-center gap-3 rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] px-4 py-3"
>
<span class="text-xl">{category.icon || '🏷️'}</span>
{#if category.color}
<span class="h-3 w-3 rounded-full" style="background-color: {category.color}"></span>
{/if}
<span class="flex-1 font-medium text-[hsl(var(--foreground))]">{category.name}</span>
<span class="flex-1 font-medium text-[hsl(var(--color-foreground))]">{category.name}</span
>
<button
onclick={() => startEdit(category)}
class="text-xs text-[hsl(var(--muted-foreground))] opacity-0 hover:text-[hsl(var(--foreground))] group-hover:opacity-100"
class="text-xs text-[hsl(var(--color-muted-foreground))] opacity-0 hover:text-[hsl(var(--color-foreground))] group-hover:opacity-100"
>&#9998;</button
>
<button
onclick={() => deleteCategory(category.id)}
class="text-xs text-[hsl(var(--muted-foreground))] opacity-0 hover:text-red-500 group-hover:opacity-100"
class="text-xs text-[hsl(var(--color-muted-foreground))] opacity-0 hover:text-red-500 group-hover:opacity-100"
>&times;</button
>
</div>

View file

@ -58,8 +58,8 @@
{#if !collection}
<div class="text-center py-16">
<p class="text-[hsl(var(--muted-foreground))]">Sammlung nicht gefunden</p>
<a href="/inventory" class="mt-4 text-[hsl(var(--primary))]">Zuruck</a>
<p class="text-[hsl(var(--color-muted-foreground))]">Sammlung nicht gefunden</p>
<a href="/inventory" class="mt-4 text-[hsl(var(--color-primary))]">Zuruck</a>
</div>
{:else}
<div class="space-y-6">
@ -68,15 +68,17 @@
<div class="flex items-center gap-3">
<a
href="/inventory"
class="text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<CaretLeft size={20} />
</a>
<span class="text-2xl">{collection.icon || '📁'}</span>
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">{collection.name}</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{collection.name}</h1>
{#if collection.description}
<p class="text-sm text-[hsl(var(--muted-foreground))]">{collection.description}</p>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{collection.description}
</p>
{/if}
</div>
</div>
@ -84,13 +86,13 @@
<ViewModeToggle current={viewStore.viewMode} onchange={(m) => viewStore.setViewMode(m)} />
<a
href="/inventory/collections/{collection.id}/edit"
class="rounded-lg border border-[hsl(var(--border))] p-2 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded-lg border border-[hsl(var(--color-border))] p-2 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<PencilSimple size={16} />
</a>
<button
onclick={() => (showNewItem = !showNewItem)}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
<Plus size={16} />
Neues Item
@ -101,13 +103,13 @@
<!-- New Item Form -->
{#if showNewItem}
<div
class="rounded-xl border border-[hsl(var(--primary)/0.3)] bg-[hsl(var(--card))] p-4 space-y-3"
class="rounded-xl border border-[hsl(var(--color-primary)/0.3)] bg-[hsl(var(--color-card))] p-4 space-y-3"
>
<input
type="text"
bind:value={newItemName}
placeholder="Item-Name"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2.5 text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2.5 text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
onkeydown={(e) => e.key === 'Enter' && createItem()}
/>
@ -116,7 +118,9 @@
<div class="grid gap-3 sm:grid-cols-2">
{#each collection.schema.fields.sort((a, b) => a.order - b.order) as field}
<div>
<span class="mb-1 block text-xs font-medium text-[hsl(var(--muted-foreground))]">
<span
class="mb-1 block text-xs font-medium text-[hsl(var(--color-muted-foreground))]"
>
{field.name}{field.required ? ' *' : ''}
</span>
<FieldEditor
@ -132,14 +136,14 @@
<div class="flex justify-end gap-2">
<button
onclick={() => (showNewItem = false)}
class="rounded-lg border border-[hsl(var(--border))] px-3 py-1.5 text-sm"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 py-1.5 text-sm"
>
Abbrechen
</button>
<button
onclick={createItem}
disabled={!newItemName.trim()}
class="rounded-lg bg-[hsl(var(--primary))] px-4 py-1.5 text-sm font-medium text-[hsl(var(--primary-foreground))] disabled:opacity-50"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-1.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))] disabled:opacity-50"
>
Erstellen
</button>
@ -150,13 +154,13 @@
<!-- Items -->
{#if sortedItems.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-16"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<span class="mb-4 text-4xl">📭</span>
<p class="text-[hsl(var(--muted-foreground))]">Keine Items vorhanden</p>
<p class="text-[hsl(var(--color-muted-foreground))]">Keine Items vorhanden</p>
<button
onclick={() => (showNewItem = true)}
class="mt-4 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm text-[hsl(var(--primary-foreground))]"
class="mt-4 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm text-[hsl(var(--color-primary-foreground))]"
>
Neues Item
</button>
@ -170,10 +174,10 @@
onkeydown={(e) => e.key === 'Enter' && goto(`/inventory/items/${item.id}`)}
role="button"
tabindex="0"
class="group cursor-pointer rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-left"
class="group cursor-pointer rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 text-left"
>
<div class="flex items-start justify-between">
<h3 class="font-semibold text-[hsl(var(--foreground))]">{item.name}</h3>
<h3 class="font-semibold text-[hsl(var(--color-foreground))]">{item.name}</h3>
<StatusBadge status={item.status} />
</div>
{#if collection.schema.fields.length > 0}
@ -181,7 +185,7 @@
{#each collection.schema.fields.slice(0, 3) as field}
{#if item.fieldValues[field.id] !== undefined}
<div class="flex items-baseline gap-2 text-xs">
<span class="text-[hsl(var(--muted-foreground))]">{field.name}:</span>
<span class="text-[hsl(var(--color-muted-foreground))]">{field.name}:</span>
<FieldRenderer {field} value={item.fieldValues[field.id]} />
</div>
{/if}
@ -190,7 +194,7 @@
{/if}
<button
onclick={(e) => deleteItem(e, item.id)}
class="mt-2 text-xs text-[hsl(var(--muted-foreground))] opacity-0 hover:text-red-500 group-hover:opacity-100"
class="mt-2 text-xs text-[hsl(var(--color-muted-foreground))] opacity-0 hover:text-red-500 group-hover:opacity-100"
>
Loschen
</button>
@ -199,18 +203,21 @@
</div>
{:else if viewStore.viewMode === 'table'}
<!-- Table View -->
<div class="overflow-x-auto rounded-xl border border-[hsl(var(--border))]">
<div class="overflow-x-auto rounded-xl border border-[hsl(var(--color-border))]">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-[hsl(var(--border))] bg-[hsl(var(--muted))]">
<th class="px-4 py-2.5 text-left font-medium text-[hsl(var(--muted-foreground))]"
<tr class="border-b border-[hsl(var(--color-border))] bg-[hsl(var(--color-muted))]">
<th
class="px-4 py-2.5 text-left font-medium text-[hsl(var(--color-muted-foreground))]"
>Name</th
>
<th class="px-4 py-2.5 text-left font-medium text-[hsl(var(--muted-foreground))]"
<th
class="px-4 py-2.5 text-left font-medium text-[hsl(var(--color-muted-foreground))]"
>Status</th
>
{#each collection.schema.fields as field}
<th class="px-4 py-2.5 text-left font-medium text-[hsl(var(--muted-foreground))]"
<th
class="px-4 py-2.5 text-left font-medium text-[hsl(var(--color-muted-foreground))]"
>{field.name}</th
>
{/each}
@ -220,10 +227,12 @@
<tbody>
{#each sortedItems as item (item.id)}
<tr
class="cursor-pointer border-b border-[hsl(var(--border))] transition-colors hover:bg-[hsl(var(--accent)/0.05)]"
class="cursor-pointer border-b border-[hsl(var(--color-border))] transition-colors hover:bg-[hsl(var(--color-accent)/0.05)]"
onclick={() => goto(`/inventory/items/${item.id}`)}
>
<td class="px-4 py-2.5 font-medium text-[hsl(var(--foreground))]">{item.name}</td>
<td class="px-4 py-2.5 font-medium text-[hsl(var(--color-foreground))]"
>{item.name}</td
>
<td class="px-4 py-2.5"><StatusBadge status={item.status} /></td>
{#each collection.schema.fields as field}
<td class="px-4 py-2.5"
@ -233,7 +242,7 @@
<td class="px-4 py-2.5">
<button
onclick={(e) => deleteItem(e, item.id)}
class="text-[hsl(var(--muted-foreground))] hover:text-red-500"
class="text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
>
<Trash size={16} />
</button>
@ -252,15 +261,19 @@
onkeydown={(e) => e.key === 'Enter' && goto(`/inventory/items/${item.id}`)}
role="button"
tabindex="0"
class="group flex w-full cursor-pointer items-center gap-4 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 text-left transition-colors hover:border-[hsl(var(--primary)/0.3)]"
class="group flex w-full cursor-pointer items-center gap-4 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] px-4 py-3 text-left transition-colors hover:border-[hsl(var(--color-primary)/0.3)]"
>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2">
<h3 class="font-medium text-[hsl(var(--foreground))] truncate">{item.name}</h3>
<h3 class="font-medium text-[hsl(var(--color-foreground))] truncate">
{item.name}
</h3>
<StatusBadge status={item.status} />
</div>
{#if collection.schema.fields.length > 0}
<div class="mt-1 flex flex-wrap gap-3 text-xs text-[hsl(var(--muted-foreground))]">
<div
class="mt-1 flex flex-wrap gap-3 text-xs text-[hsl(var(--color-muted-foreground))]"
>
{#each collection.schema.fields.slice(0, 4) as field}
{#if item.fieldValues[field.id] !== undefined}
<span
@ -275,12 +288,13 @@
{/if}
</div>
{#if item.quantity > 1}
<span class="text-sm text-[hsl(var(--muted-foreground))]">&times;{item.quantity}</span
<span class="text-sm text-[hsl(var(--color-muted-foreground))]"
>&times;{item.quantity}</span
>
{/if}
<button
onclick={(e) => deleteItem(e, item.id)}
class="text-[hsl(var(--muted-foreground))] opacity-0 hover:text-red-500 group-hover:opacity-100"
class="text-[hsl(var(--color-muted-foreground))] opacity-0 hover:text-red-500 group-hover:opacity-100"
>
<Trash size={16} />
</button>

View file

@ -46,7 +46,7 @@
}
const inputClass =
'w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-3 text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]';
'w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-3 text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]';
</script>
<svelte:head>
@ -54,17 +54,17 @@
</svelte:head>
{#if !collection}
<p class="text-[hsl(var(--muted-foreground))]">Sammlung nicht gefunden</p>
<p class="text-[hsl(var(--color-muted-foreground))]">Sammlung nicht gefunden</p>
{:else}
<div class="mx-auto max-w-2xl space-y-6">
<div class="flex items-center gap-3">
<button
onclick={() => goto(`/inventory/collections/${collection.id}`)}
class="text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<CaretLeft size={20} />
</button>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Sammlung bearbeiten</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Sammlung bearbeiten</h1>
</div>
<div class="space-y-4">
@ -73,7 +73,7 @@
type="text"
bind:value={icon}
placeholder="📁"
class="h-12 w-12 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] text-center text-2xl"
class="h-12 w-12 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] text-center text-2xl"
maxlength="2"
/>
<input
@ -92,21 +92,23 @@
></textarea>
<div>
<h3 class="mb-3 text-sm font-semibold text-[hsl(var(--foreground))]">Eigene Felder</h3>
<h3 class="mb-3 text-sm font-semibold text-[hsl(var(--color-foreground))]">
Eigene Felder
</h3>
<SchemaEditor fields={schema.fields} onchange={(fields) => (schema = { fields })} />
</div>
<div class="flex justify-end gap-3 pt-4">
<button
onclick={() => goto(`/inventory/collections/${collection.id}`)}
class="rounded-lg border border-[hsl(var(--border))] px-4 py-2 text-sm text-[hsl(var(--foreground))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))]"
>
Abbrechen
</button>
<button
onclick={handleSave}
disabled={!name.trim()}
class="rounded-lg bg-[hsl(var(--primary))] px-6 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] disabled:opacity-50"
class="rounded-lg bg-[hsl(var(--color-primary))] px-6 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] disabled:opacity-50"
>
Speichern
</button>

View file

@ -39,7 +39,7 @@
}
const inputClass =
'w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-3 text-[hsl(var(--foreground))] placeholder:text-[hsl(var(--muted-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]';
'w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-3 text-[hsl(var(--color-foreground))] placeholder:text-[hsl(var(--color-muted-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]';
</script>
<svelte:head>
@ -51,35 +51,37 @@
<button
onclick={() =>
step === 'details' && selectedTemplate ? (step = 'template') : goto('/inventory')}
class="text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<CaretLeft size={20} />
</button>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Neue Sammlung</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Neue Sammlung</h1>
</div>
{#if step === 'template'}
<!-- Template Selection -->
<div>
<h2 class="mb-4 text-lg font-semibold text-[hsl(var(--foreground))]">Vorlage wahlen</h2>
<h2 class="mb-4 text-lg font-semibold text-[hsl(var(--color-foreground))]">Vorlage wahlen</h2>
<div class="grid gap-3 sm:grid-cols-2">
{#each DEFAULT_TEMPLATES as template}
<button
onclick={() => selectTemplate(template)}
class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 text-left transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 text-left transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<div class="flex items-center gap-3">
<span class="text-2xl">{template.icon}</span>
<div>
<h3 class="font-semibold text-[hsl(var(--foreground))]">{template.name}</h3>
<p class="text-xs text-[hsl(var(--muted-foreground))]">{template.description}</p>
<h3 class="font-semibold text-[hsl(var(--color-foreground))]">{template.name}</h3>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
{template.description}
</p>
</div>
</div>
{#if template.schema.fields.length > 0}
<div class="mt-3 flex flex-wrap gap-1">
{#each template.schema.fields as field}
<span
class="rounded bg-[hsl(var(--muted))] px-1.5 py-0.5 text-[10px] text-[hsl(var(--muted-foreground))]"
class="rounded bg-[hsl(var(--color-muted))] px-1.5 py-0.5 text-[10px] text-[hsl(var(--color-muted-foreground))]"
>
{field.name}
</span>
@ -99,7 +101,7 @@
type="text"
bind:value={icon}
placeholder="📁"
class="h-12 w-12 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] text-center text-2xl"
class="h-12 w-12 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] text-center text-2xl"
maxlength="2"
/>
</div>
@ -120,7 +122,9 @@
<!-- Schema Editor -->
<div>
<h3 class="mb-3 text-sm font-semibold text-[hsl(var(--foreground))]">Eigene Felder</h3>
<h3 class="mb-3 text-sm font-semibold text-[hsl(var(--color-foreground))]">
Eigene Felder
</h3>
<SchemaEditor fields={schema.fields} onchange={(fields) => (schema = { fields })} />
</div>
@ -128,14 +132,14 @@
<div class="flex justify-end gap-3 pt-4">
<button
onclick={() => goto('/inventory')}
class="rounded-lg border border-[hsl(var(--border))] px-4 py-2 text-sm text-[hsl(var(--foreground))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))]"
>
Abbrechen
</button>
<button
onclick={handleCreate}
disabled={!name.trim()}
class="rounded-lg bg-[hsl(var(--primary))] px-6 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] disabled:opacity-50"
class="rounded-lg bg-[hsl(var(--color-primary))] px-6 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] disabled:opacity-50"
>
Erstellen
</button>

View file

@ -94,7 +94,7 @@
}
const inputClass =
'w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]';
'w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]';
</script>
<svelte:head>
@ -103,8 +103,8 @@
{#if !item}
<div class="text-center py-16">
<p class="text-[hsl(var(--muted-foreground))]">Item nicht gefunden</p>
<a href="/inventory" class="mt-4 text-[hsl(var(--primary))]">Zuruck</a>
<p class="text-[hsl(var(--color-muted-foreground))]">Item nicht gefunden</p>
<a href="/inventory" class="mt-4 text-[hsl(var(--color-primary))]">Zuruck</a>
</div>
{:else}
<div class="mx-auto max-w-2xl space-y-6">
@ -114,15 +114,15 @@
<button
onclick={() =>
goto(collection ? `/inventory/collections/${collection.id}` : '/inventory')}
class="text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<CaretLeft size={20} />
</button>
{#if !editing}
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">{item.name}</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{item.name}</h1>
{#if collection}
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{collection.icon}
{collection.name}
</p>
@ -134,18 +134,18 @@
{#if editing}
<button
onclick={() => (editing = false)}
class="rounded-lg border border-[hsl(var(--border))] px-3 py-1.5 text-sm"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 py-1.5 text-sm"
>{$_('common.cancel')}</button
>
<button
onclick={saveEdit}
class="rounded-lg bg-[hsl(var(--primary))] px-4 py-1.5 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-1.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>{$_('common.save')}</button
>
{:else}
<button
onclick={startEditing}
class="rounded-lg border border-[hsl(var(--border))] px-3 py-1.5 text-sm text-[hsl(var(--foreground))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 py-1.5 text-sm text-[hsl(var(--color-foreground))]"
>{$_('common.edit')}</button
>
<button
@ -160,7 +160,7 @@
{#if editing}
<!-- Edit Mode -->
<div
class="space-y-4 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-5"
class="space-y-4 rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-5"
>
<input type="text" bind:value={editName} placeholder="Name" class={inputClass} />
<textarea
@ -174,7 +174,7 @@
<div>
<label
for="inventory-status"
class="mb-1 block text-xs font-medium text-[hsl(var(--muted-foreground))]"
class="mb-1 block text-xs font-medium text-[hsl(var(--color-muted-foreground))]"
>Status</label
>
<select id="inventory-status" bind:value={editStatus} class={inputClass}>
@ -184,7 +184,7 @@
<div>
<label
for="inventory-quantity"
class="mb-1 block text-xs font-medium text-[hsl(var(--muted-foreground))]"
class="mb-1 block text-xs font-medium text-[hsl(var(--color-muted-foreground))]"
>Menge</label
>
<input
@ -199,7 +199,7 @@
<div>
<label
for="inventory-location"
class="mb-1 block text-xs font-medium text-[hsl(var(--muted-foreground))]"
class="mb-1 block text-xs font-medium text-[hsl(var(--color-muted-foreground))]"
>Standort</label
>
<select id="inventory-location" bind:value={editLocationId} class={inputClass}>
@ -214,7 +214,7 @@
<div>
<label
for="inventory-category"
class="mb-1 block text-xs font-medium text-[hsl(var(--muted-foreground))]"
class="mb-1 block text-xs font-medium text-[hsl(var(--color-muted-foreground))]"
>Kategorie</label
>
<select id="inventory-category" bind:value={editCategoryId} class={inputClass}>
@ -229,11 +229,14 @@
{#if collection}
<div>
<h3 class="mb-2 text-sm font-semibold text-[hsl(var(--foreground))]">Eigene Felder</h3>
<h3 class="mb-2 text-sm font-semibold text-[hsl(var(--color-foreground))]">
Eigene Felder
</h3>
<div class="grid gap-3 sm:grid-cols-2">
{#each collection.schema.fields.sort((a, b) => a.order - b.order) as field}
<div>
<span class="mb-1 block text-xs font-medium text-[hsl(var(--muted-foreground))]"
<span
class="mb-1 block text-xs font-medium text-[hsl(var(--color-muted-foreground))]"
>{field.name}</span
>
<FieldEditor
@ -254,14 +257,16 @@
<div class="flex flex-wrap items-center gap-3">
<StatusBadge status={item.status} size="md" />
{#if item.quantity > 1}
<span class="rounded-full bg-[hsl(var(--muted))] px-3 py-1 text-sm"
<span class="rounded-full bg-[hsl(var(--color-muted))] px-3 py-1 text-sm"
>&times;{item.quantity}</span
>
{/if}
{#if item.locationId}
{@const loc = getLocationById(locationsCtx.value, item.locationId)}
{#if loc}
<span class="flex items-center gap-1 text-sm text-[hsl(var(--muted-foreground))]">
<span
class="flex items-center gap-1 text-sm text-[hsl(var(--color-muted-foreground))]"
>
📍 {getLocationFullPath(locationsCtx.value, loc.id)}
</span>
{/if}
@ -269,7 +274,7 @@
{#if item.categoryId}
{@const cat = getCategoryById(categoriesCtx.value, item.categoryId)}
{#if cat}
<span class="rounded-full bg-[hsl(var(--muted))] px-2 py-0.5 text-xs"
<span class="rounded-full bg-[hsl(var(--color-muted))] px-2 py-0.5 text-xs"
>{cat.icon || '🏷️'} {cat.name}</span
>
{/if}
@ -277,17 +282,19 @@
</div>
{#if item.description}
<p class="text-[hsl(var(--foreground))]">{item.description}</p>
<p class="text-[hsl(var(--color-foreground))]">{item.description}</p>
{/if}
<!-- Custom Fields -->
{#if collection && collection.schema.fields.length > 0}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<h3 class="mb-3 text-sm font-semibold text-[hsl(var(--foreground))]">Details</h3>
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<h3 class="mb-3 text-sm font-semibold text-[hsl(var(--color-foreground))]">Details</h3>
<div class="grid gap-2 sm:grid-cols-2">
{#each collection.schema.fields.sort((a, b) => a.order - b.order) as field}
<div class="flex items-baseline gap-2">
<span class="text-xs font-medium text-[hsl(var(--muted-foreground))]"
<span class="text-xs font-medium text-[hsl(var(--color-muted-foreground))]"
>{field.name}:</span
>
<FieldRenderer {field} value={item.fieldValues[field.id]} />
@ -298,24 +305,26 @@
{/if}
<!-- Notes -->
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<h3 class="mb-3 text-sm font-semibold text-[hsl(var(--foreground))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<h3 class="mb-3 text-sm font-semibold text-[hsl(var(--color-foreground))]">
Notizen ({item.notes.length})
</h3>
<div class="space-y-2">
{#each item.notes as note (note.id)}
<div
class="group flex items-start justify-between rounded-lg bg-[hsl(var(--muted))] p-3"
class="group flex items-start justify-between rounded-lg bg-[hsl(var(--color-muted))] p-3"
>
<div>
<p class="text-sm text-[hsl(var(--foreground))]">{note.content}</p>
<p class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">
<p class="text-sm text-[hsl(var(--color-foreground))]">{note.content}</p>
<p class="mt-1 text-xs text-[hsl(var(--color-muted-foreground))]">
{new Date(note.createdAt).toLocaleDateString('de-DE')}
</p>
</div>
<button
onclick={() => itemsStore.deleteNote(item.id, note.id)}
class="text-[hsl(var(--muted-foreground))] opacity-0 hover:text-red-500 group-hover:opacity-100"
class="text-[hsl(var(--color-muted-foreground))] opacity-0 hover:text-red-500 group-hover:opacity-100"
>&times;</button
>
</div>
@ -326,13 +335,13 @@
type="text"
bind:value={newNote}
placeholder="Notiz hinzufugen..."
class="flex-1 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))]"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))]"
onkeydown={(e) => e.key === 'Enter' && addNote()}
/>
<button
onclick={addNote}
disabled={!newNote.trim()}
class="rounded-lg bg-[hsl(var(--primary))] px-3 py-2 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50"
class="rounded-lg bg-[hsl(var(--color-primary))] px-3 py-2 text-sm text-[hsl(var(--color-primary-foreground))] disabled:opacity-50"
>+</button
>
</div>

View file

@ -51,7 +51,7 @@
let tree = $derived(getLocationTree(locationsCtx.value));
const inputClass =
'rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]';
'rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]';
</script>
<svelte:head>
@ -60,10 +60,10 @@
<div class="mx-auto max-w-2xl space-y-6">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Standorte</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Standorte</h1>
<button
onclick={() => startCreate()}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
<Plus size={20} />
Neuer Standort
@ -71,7 +71,9 @@
</div>
{#if showForm}
<div class="rounded-xl border border-[hsl(var(--primary)/0.3)] bg-[hsl(var(--card))] p-4">
<div
class="rounded-xl border border-[hsl(var(--color-primary)/0.3)] bg-[hsl(var(--color-card))] p-4"
>
<h3 class="mb-3 text-sm font-semibold">
{editingId ? 'Standort bearbeiten' : 'Neuer Standort'}
</h3>
@ -93,13 +95,13 @@
<button
onclick={save}
disabled={!name.trim()}
class="rounded-lg bg-[hsl(var(--primary))] px-4 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 text-sm text-[hsl(var(--color-primary-foreground))] disabled:opacity-50"
>
Speichern
</button>
<button
onclick={() => (showForm = false)}
class="rounded-lg border border-[hsl(var(--border))] px-3 text-sm"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 text-sm"
>
Abbrechen
</button>
@ -109,37 +111,37 @@
{#if tree.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-16"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<span class="mb-4 text-4xl">📍</span>
<p class="text-[hsl(var(--muted-foreground))]">Keine Standorte vorhanden</p>
<p class="text-[hsl(var(--color-muted-foreground))]">Keine Standorte vorhanden</p>
</div>
{:else}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))]">
<div class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))]">
{#snippet renderTree(locations: Location[], depth: number)}
{#each locations as location (location.id)}
<div class="border-b border-[hsl(var(--border))] last:border-b-0">
<div class="border-b border-[hsl(var(--color-border))] last:border-b-0">
<div
class="flex items-center gap-2 px-4 py-2.5"
style="padding-left: {16 + depth * 24}px"
>
<span class="text-lg">{location.icon || '📍'}</span>
<span class="flex-1 text-sm font-medium text-[hsl(var(--foreground))]"
<span class="flex-1 text-sm font-medium text-[hsl(var(--color-foreground))]"
>{location.name}</span
>
<button
onclick={() => startCreate(location.id)}
class="rounded p-1 text-xs text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--primary))]"
class="rounded p-1 text-xs text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-primary))]"
title="Unterstandort hinzufugen">+</button
>
<button
onclick={() => startEdit(location)}
class="rounded p-1 text-xs text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded p-1 text-xs text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>&#9998;</button
>
<button
onclick={() => deleteLocation(location.id)}
class="rounded p-1 text-xs text-[hsl(var(--muted-foreground))] hover:text-red-500"
class="rounded p-1 text-xs text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
>&times;</button
>
</div>

View file

@ -20,37 +20,37 @@
</svelte:head>
<div class="mx-auto max-w-2xl space-y-6">
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Suche</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Suche</h1>
<div class="relative">
<MagnifyingGlass
size={20}
class="absolute left-3 top-1/2 -translate-y-1/2 text-[hsl(var(--muted-foreground))]"
class="absolute left-3 top-1/2 -translate-y-1/2 text-[hsl(var(--color-muted-foreground))]"
/>
<!-- svelte-ignore a11y_autofocus -->
<input
type="text"
bind:value={query}
placeholder="In allen Items suchen..."
class="w-full rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--input))] py-3 pl-11 pr-4 text-lg text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] py-3 pl-11 pr-4 text-lg text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
autofocus
/>
</div>
{#if query.length >= 2}
<p class="text-sm text-[hsl(var(--muted-foreground))]">{results.length} Ergebnisse</p>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">{results.length} Ergebnisse</p>
<div class="space-y-2">
{#each results as item (item.id)}
<button
onclick={() => goto(`/inventory/items/${item.id}`)}
class="flex w-full items-center gap-4 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 text-left transition-colors hover:border-[hsl(var(--primary)/0.3)]"
class="flex w-full items-center gap-4 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] px-4 py-3 text-left transition-colors hover:border-[hsl(var(--color-primary)/0.3)]"
>
<div class="flex-1">
<div class="flex items-center gap-2">
<h3 class="font-medium text-[hsl(var(--foreground))]">{item.name}</h3>
<h3 class="font-medium text-[hsl(var(--color-foreground))]">{item.name}</h3>
<StatusBadge status={item.status} />
</div>
<p class="text-xs text-[hsl(var(--muted-foreground))]">
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
{getCollectionById(collectionsCtx.value, item.collectionId)?.name || ''}
</p>
</div>
@ -58,6 +58,8 @@
{/each}
</div>
{:else if query.length > 0}
<p class="text-sm text-[hsl(var(--muted-foreground))]">Mindestens 2 Zeichen eingeben...</p>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
Mindestens 2 Zeichen eingeben...
</p>
{/if}
</div>

View file

@ -45,6 +45,6 @@
min-height: 100%;
width: 100%;
overflow-x: hidden;
background-color: hsl(var(--background));
background-color: hsl(var(--color-background));
}
</style>

View file

@ -89,15 +89,15 @@
<!-- Header -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Memoro</h1>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Memoro</h1>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{memosCtx.value.length} Memos
</p>
</div>
<div class="flex items-center gap-2">
<a
href="/memoro/tags"
class="flex items-center gap-2 rounded-lg border border-[hsl(var(--border))] px-4 py-2 text-sm font-medium text-[hsl(var(--foreground))] transition-colors hover:bg-[hsl(var(--muted))]"
class="flex items-center gap-2 rounded-lg border border-[hsl(var(--color-border))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-foreground))] transition-colors hover:bg-[hsl(var(--color-muted))]"
>
<TagIcon size={16} />
Tags
@ -112,7 +112,7 @@
</div>
<button
onclick={handleNewMemo}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] transition-colors hover:opacity-90"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] transition-colors hover:opacity-90"
>
<Plus size={20} />
Neues Memo
@ -124,13 +124,13 @@
<div class="relative">
<MagnifyingGlass
size={18}
class="absolute left-3 top-1/2 -translate-y-1/2 text-[hsl(var(--muted-foreground))]"
class="absolute left-3 top-1/2 -translate-y-1/2 text-[hsl(var(--color-muted-foreground))]"
/>
<input
type="text"
placeholder="Memos durchsuchen..."
bind:value={searchQuery}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] py-2.5 pl-10 pr-4 text-sm text-[hsl(var(--foreground))] placeholder:text-[hsl(var(--muted-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] py-2.5 pl-10 pr-4 text-sm text-[hsl(var(--color-foreground))] placeholder:text-[hsl(var(--color-muted-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
@ -140,8 +140,8 @@
<button
onclick={() => (selectedTagId = null)}
class="rounded-full px-3 py-1 text-xs font-medium transition-colors {selectedTagId === null
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
: 'bg-[hsl(var(--muted))] text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted)/0.8)]'}"
? 'bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))]'
: 'bg-[hsl(var(--color-muted))] text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted)/0.8)]'}"
>
Alle
</button>
@ -151,7 +151,7 @@
class="rounded-full px-3 py-1 text-xs font-medium transition-colors {selectedTagId ===
tag.id
? 'text-white'
: 'bg-[hsl(var(--muted))] text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted)/0.8)]'}"
: 'bg-[hsl(var(--color-muted))] text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted)/0.8)]'}"
style={selectedTagId === tag.id && tag.color ? `background-color: ${tag.color}` : ''}
>
{tag.name}
@ -163,22 +163,22 @@
<!-- Memos List -->
{#if memosCtx.value.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-16"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<div
class="mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-[hsl(var(--primary)/0.1)]"
class="mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-[hsl(var(--color-primary)/0.1)]"
>
<Microphone size={32} weight="duotone" class="text-[hsl(var(--primary))]" />
<Microphone size={32} weight="duotone" class="text-[hsl(var(--color-primary))]" />
</div>
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--foreground))]">
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--color-foreground))]">
Erstelle dein erstes Memo
</h2>
<p class="mb-6 text-sm text-[hsl(var(--muted-foreground))]">
<p class="mb-6 text-sm text-[hsl(var(--color-muted-foreground))]">
Nimm Gedanken auf oder schreibe sie direkt auf.
</p>
<button
onclick={handleNewMemo}
class="rounded-lg bg-[hsl(var(--primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="rounded-lg bg-[hsl(var(--color-primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
Neues Memo
</button>
@ -191,24 +191,28 @@
tabindex="0"
onclick={() => handleMemoClick(memo.id)}
onkeydown={(e) => e.key === 'Enter' && handleMemoClick(memo.id)}
class="group rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="group rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<div class="flex items-start justify-between">
<div class="min-w-0 flex-1">
<div class="flex items-center gap-2">
{#if memo.isPinned}
<PushPin size={14} weight="fill" class="shrink-0 text-[hsl(var(--primary))]" />
<PushPin
size={14}
weight="fill"
class="shrink-0 text-[hsl(var(--color-primary))]"
/>
{/if}
<h3 class="truncate font-semibold text-[hsl(var(--foreground))]">
<h3 class="truncate font-semibold text-[hsl(var(--color-foreground))]">
{memo.title || 'Unbenanntes Memo'}
</h3>
</div>
{#if memo.intro}
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))] line-clamp-2">
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))] line-clamp-2">
{memo.intro}
</p>
{:else if memo.transcript}
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))] line-clamp-2">
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))] line-clamp-2">
{memo.transcript}
</p>
{/if}
@ -218,14 +222,14 @@
>
<button
onclick={(e) => handlePin(e, memo.id, memo.isPinned)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
title={memo.isPinned ? 'Loslosen' : 'Anpinnen'}
>
<PushPin size={16} weight={memo.isPinned ? 'fill' : 'regular'} />
</button>
<button
onclick={(e) => handleArchive(e, memo.id)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
title="Archivieren"
>
<Archive size={16} />
@ -235,17 +239,17 @@
<!-- Footer -->
<div class="mt-3 flex items-center gap-3">
<span class="text-xs text-[hsl(var(--muted-foreground))]">
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">
{formatDate(memo.createdAt)}
</span>
{#if memo.audioDurationMs}
<span class="text-xs text-[hsl(var(--muted-foreground))]">
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">
{formatDuration(memo.audioDurationMs)}
</span>
{/if}
{#if memo.processingStatus !== 'completed'}
<span
class="rounded bg-[hsl(var(--muted))] px-1.5 py-0.5 text-[10px] text-[hsl(var(--muted-foreground))]"
class="rounded bg-[hsl(var(--color-muted))] px-1.5 py-0.5 text-[10px] text-[hsl(var(--color-muted-foreground))]"
>
{getStatusLabel(memo.processingStatus)}
</span>
@ -254,7 +258,7 @@
{#each getMemoTags(memo.id) as tag (tag.id)}
<span
class="rounded-full px-2 py-0.5 text-[10px] font-medium text-white"
style="background-color: {tag.color || 'hsl(var(--muted))'}"
style="background-color: {tag.color || 'hsl(var(--color-muted))'}"
>
{tag.name}
</span>
@ -269,7 +273,7 @@
<div class="pt-2">
<a
href="/memoro/archive"
class="inline-flex items-center gap-2 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="inline-flex items-center gap-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<Archive size={16} />
Archiv anzeigen

View file

@ -94,8 +94,8 @@
{#if !memo}
<div class="flex flex-col items-center justify-center py-16">
<p class="mb-4 text-[hsl(var(--muted-foreground))]">Memo nicht gefunden</p>
<a href="/memoro" class="text-sm text-[hsl(var(--primary))] hover:underline">
<p class="mb-4 text-[hsl(var(--color-muted-foreground))]">Memo nicht gefunden</p>
<a href="/memoro" class="text-sm text-[hsl(var(--color-primary))] hover:underline">
Zuruck zu Memoro
</a>
</div>
@ -106,7 +106,7 @@
<div class="flex items-center gap-3">
<a
href="/memoro"
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowLeft size={20} />
</a>
@ -117,30 +117,30 @@
type="text"
bind:value={editTitle}
onkeydown={(e) => e.key === 'Enter' && saveTitle()}
class="flex-1 rounded border border-[hsl(var(--border))] bg-transparent px-2 py-1 text-xl font-bold focus:outline-none focus:ring-1 focus:ring-[hsl(var(--primary))]"
class="flex-1 rounded border border-[hsl(var(--color-border))] bg-transparent px-2 py-1 text-xl font-bold focus:outline-none focus:ring-1 focus:ring-[hsl(var(--color-primary))]"
/>
<button onclick={saveTitle} class="text-[hsl(var(--primary))]">
<button onclick={saveTitle} class="text-[hsl(var(--color-primary))]">
<Check size={18} />
</button>
<button
onclick={() => (isEditingTitle = false)}
class="text-[hsl(var(--muted-foreground))]"
class="text-[hsl(var(--color-muted-foreground))]"
>
<X size={18} />
</button>
</div>
{:else}
<button onclick={startEditTitle} class="group flex items-center gap-2 text-left">
<h1 class="text-xl font-bold text-[hsl(var(--foreground))]">
<h1 class="text-xl font-bold text-[hsl(var(--color-foreground))]">
{memo.title || 'Unbenanntes Memo'}
</h1>
<PencilSimple
size={16}
class="shrink-0 text-[hsl(var(--muted-foreground))] opacity-0 group-hover:opacity-100"
class="shrink-0 text-[hsl(var(--color-muted-foreground))] opacity-0 group-hover:opacity-100"
/>
</button>
{/if}
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{new Date(memo.createdAt).toLocaleDateString('de-DE', {
day: '2-digit',
month: 'long',
@ -159,22 +159,22 @@
<button
onclick={togglePin}
class="rounded-lg p-1.5 transition-colors {memo.isPinned
? 'text-[hsl(var(--primary))]'
: 'text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]'}"
? 'text-[hsl(var(--color-primary))]'
: 'text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]'}"
title={memo.isPinned ? 'Loslosen' : 'Anpinnen'}
>
<PushPin size={18} weight={memo.isPinned ? 'fill' : 'regular'} />
</button>
<button
onclick={handleArchive}
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
title="Archivieren"
>
<Archive size={18} />
</button>
<button
onclick={handleDelete}
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:text-red-500"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
title="Loschen"
>
<Trash size={18} />
@ -185,7 +185,7 @@
<!-- Status -->
{#if memo.processingStatus !== 'completed'}
<div
class="rounded-lg bg-[hsl(var(--muted))] px-4 py-2 text-sm text-[hsl(var(--muted-foreground))]"
class="rounded-lg bg-[hsl(var(--color-muted))] px-4 py-2 text-sm text-[hsl(var(--color-muted-foreground))]"
>
Status: {getStatusLabel(memo.processingStatus)}
</div>
@ -196,7 +196,7 @@
{#each memoTags as tag (tag.id)}
<span
class="inline-flex items-center gap-1 rounded-full px-3 py-1 text-xs font-medium text-white"
style="background-color: {tag.color || 'hsl(var(--muted))'}"
style="background-color: {tag.color || 'hsl(var(--color-muted))'}"
>
{tag.name}
<button
@ -210,19 +210,19 @@
<div class="relative">
<button
onclick={() => (showTagPicker = !showTagPicker)}
class="flex items-center gap-1 rounded-full border border-dashed border-[hsl(var(--border))] px-2.5 py-1 text-xs text-[hsl(var(--muted-foreground))] hover:border-[hsl(var(--primary))] hover:text-[hsl(var(--primary))]"
class="flex items-center gap-1 rounded-full border border-dashed border-[hsl(var(--color-border))] px-2.5 py-1 text-xs text-[hsl(var(--color-muted-foreground))] hover:border-[hsl(var(--color-primary))] hover:text-[hsl(var(--color-primary))]"
>
<TagIcon size={12} />
Tag hinzufugen
</button>
{#if showTagPicker && availableTags.length > 0}
<div
class="absolute left-0 top-full z-10 mt-1 w-48 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-2 shadow-lg"
class="absolute left-0 top-full z-10 mt-1 w-48 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-2 shadow-lg"
>
{#each availableTags as tag (tag.id)}
<button
onclick={() => handleAddTag(tag.id)}
class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-sm hover:bg-[hsl(var(--muted))]"
class="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-sm hover:bg-[hsl(var(--color-muted))]"
>
<span class="h-3 w-3 rounded-full" style="background-color: {tag.color || '#888'}"
></span>
@ -236,25 +236,29 @@
<!-- Intro -->
{#if memo.intro}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-5">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-5"
>
<h2
class="mb-2 text-sm font-medium uppercase tracking-wide text-[hsl(var(--muted-foreground))]"
class="mb-2 text-sm font-medium uppercase tracking-wide text-[hsl(var(--color-muted-foreground))]"
>
Zusammenfassung
</h2>
<p class="text-[hsl(var(--foreground))]">{memo.intro}</p>
<p class="text-[hsl(var(--color-foreground))]">{memo.intro}</p>
</div>
{/if}
<!-- Transcript -->
{#if memo.transcript}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-5">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-5"
>
<h2
class="mb-2 text-sm font-medium uppercase tracking-wide text-[hsl(var(--muted-foreground))]"
class="mb-2 text-sm font-medium uppercase tracking-wide text-[hsl(var(--color-muted-foreground))]"
>
Transkript
</h2>
<p class="whitespace-pre-wrap text-sm leading-relaxed text-[hsl(var(--foreground))]">
<p class="whitespace-pre-wrap text-sm leading-relaxed text-[hsl(var(--color-foreground))]">
{memo.transcript}
</p>
</div>
@ -263,21 +267,23 @@
<!-- Memories -->
<div>
<h2
class="mb-3 text-sm font-medium uppercase tracking-wide text-[hsl(var(--muted-foreground))]"
class="mb-3 text-sm font-medium uppercase tracking-wide text-[hsl(var(--color-muted-foreground))]"
>
Erinnerungen ({memories.length})
</h2>
{#if memories.length === 0}
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
Noch keine Erinnerungen fur dieses Memo.
</p>
{:else}
<div class="space-y-3">
{#each memories as memory (memory.id)}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<h3 class="font-medium text-[hsl(var(--foreground))]">{memory.title}</h3>
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<h3 class="font-medium text-[hsl(var(--color-foreground))]">{memory.title}</h3>
{#if memory.content}
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))]">
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))]">
{memory.content}
</p>
{/if}

View file

@ -41,13 +41,13 @@
<div class="flex items-center gap-3">
<a
href="/memoro"
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowLeft size={20} />
</a>
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Archiv</h1>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Archiv</h1>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{archivedCtx.value.length} archivierte Memos
</p>
</div>
@ -55,8 +55,8 @@
{#if archivedCtx.value.length === 0}
<div class="flex flex-col items-center justify-center py-16">
<Microphone size={48} class="mb-4 text-[hsl(var(--muted-foreground))]" />
<p class="text-[hsl(var(--muted-foreground))]">Keine archivierten Memos</p>
<Microphone size={48} class="mb-4 text-[hsl(var(--color-muted-foreground))]" />
<p class="text-[hsl(var(--color-muted-foreground))]">Keine archivierten Memos</p>
</div>
{:else}
<div class="space-y-2">
@ -66,19 +66,19 @@
tabindex="0"
onclick={() => handleClick(memo.id)}
onkeydown={(e) => e.key === 'Enter' && handleClick(memo.id)}
class="group rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="group rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<div class="flex items-start justify-between">
<div class="min-w-0 flex-1">
<h3 class="truncate font-medium text-[hsl(var(--foreground))]">
<h3 class="truncate font-medium text-[hsl(var(--color-foreground))]">
{memo.title || 'Unbenanntes Memo'}
</h3>
{#if memo.intro}
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))] line-clamp-1">
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))] line-clamp-1">
{memo.intro}
</p>
{/if}
<p class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">
<p class="mt-1 text-xs text-[hsl(var(--color-muted-foreground))]">
{formatDate(memo.updatedAt)}
</p>
</div>
@ -87,14 +87,14 @@
>
<button
onclick={(e) => handleUnarchive(e, memo.id)}
class="rounded p-1.5 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--primary))]"
class="rounded p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-primary))]"
title="Wiederherstellen"
>
<ArrowCounterClockwise size={16} />
</button>
<button
onclick={(e) => handleDelete(e, memo.id)}
class="rounded p-1.5 text-[hsl(var(--muted-foreground))] hover:text-red-500"
class="rounded p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
title="Endgultig loschen"
>
<Trash size={16} />

View file

@ -73,20 +73,20 @@
<div class="flex items-center gap-3">
<a
href="/memoro"
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowLeft size={20} />
</a>
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Tags</h1>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Tags</h1>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{tagsCtx.value.length} Tags
</p>
</div>
</div>
<button
onclick={openCreateForm}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90"
>
<Plus size={16} />
Neuer Tag
@ -95,16 +95,16 @@
{#if tagsCtx.value.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-16"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<TagIcon size={48} class="mb-4 text-[hsl(var(--muted-foreground))]" />
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--foreground))]">Keine Tags</h2>
<p class="mb-6 text-sm text-[hsl(var(--muted-foreground))]">
<TagIcon size={48} class="mb-4 text-[hsl(var(--color-muted-foreground))]" />
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--color-foreground))]">Keine Tags</h2>
<p class="mb-6 text-sm text-[hsl(var(--color-muted-foreground))]">
Erstelle Tags, um deine Memos zu organisieren.
</p>
<button
onclick={openCreateForm}
class="rounded-lg bg-[hsl(var(--primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="rounded-lg bg-[hsl(var(--color-primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
Neuer Tag
</button>
@ -113,23 +113,23 @@
<div class="space-y-2">
{#each tagsCtx.value as tag (tag.id)}
<div
class="group flex items-center gap-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4"
class="group flex items-center gap-3 rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<span
class="h-4 w-4 shrink-0 rounded-full"
style="background-color: {tag.color || '#888'}"
></span>
<span class="flex-1 font-medium text-[hsl(var(--foreground))]">{tag.name}</span>
<span class="flex-1 font-medium text-[hsl(var(--color-foreground))]">{tag.name}</span>
<div class="flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100">
<button
onclick={() => openEditForm(tag)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<PencilSimple size={16} />
</button>
<button
onclick={() => handleDelete(tag.id)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] hover:text-red-500"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
>
<Trash size={16} />
</button>
@ -144,15 +144,15 @@
{#if showCreateForm}
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
<div
class="w-full max-w-sm rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-6"
class="w-full max-w-sm rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-6"
>
<div class="mb-4 flex items-center justify-between">
<h2 class="text-lg font-semibold text-[hsl(var(--foreground))]">
<h2 class="text-lg font-semibold text-[hsl(var(--color-foreground))]">
{editingId ? 'Tag bearbeiten' : 'Neuer Tag'}
</h2>
<button
onclick={() => (showCreateForm = false)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<X size={20} />
</button>
@ -171,7 +171,7 @@
type="text"
bind:value={formName}
required
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
@ -183,7 +183,7 @@
aria-label="Farbe wählen"
onclick={() => (formColor = color)}
class="h-7 w-7 rounded-full border-2 transition-transform {formColor === color
? 'scale-110 border-[hsl(var(--foreground))]'
? 'scale-110 border-[hsl(var(--color-foreground))]'
: 'border-transparent hover:scale-105'}"
style="background-color: {color}"
></button>
@ -194,14 +194,14 @@
<button
type="button"
onclick={() => (showCreateForm = false)}
class="px-4 py-2 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="px-4 py-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
Abbrechen
</button>
<button
type="submit"
disabled={!formName.trim()}
class="rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90 disabled:opacity-50"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90 disabled:opacity-50"
>
{editingId ? $_('common.save') : $_('common.create')}
</button>

View file

@ -24,31 +24,39 @@
</svelte:head>
<div class="space-y-8">
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Music</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Music</h1>
<!-- Quick Stats -->
<section>
<h2
class="mb-4 text-sm font-medium uppercase tracking-wide text-[hsl(var(--muted-foreground))]"
class="mb-4 text-sm font-medium uppercase tracking-wide text-[hsl(var(--color-muted-foreground))]"
>
Bibliothek
</h2>
<div class="grid grid-cols-2 gap-4 md:grid-cols-4">
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-sm text-[hsl(var(--muted-foreground))]">Songs</p>
<p class="text-2xl font-bold text-[hsl(var(--foreground))]">{stats.totalSongs}</p>
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">Songs</p>
<p class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{stats.totalSongs}</p>
</div>
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-sm text-[hsl(var(--muted-foreground))]">Alben</p>
<p class="text-2xl font-bold text-[hsl(var(--foreground))]">{stats.totalAlbums}</p>
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">Alben</p>
<p class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{stats.totalAlbums}</p>
</div>
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-sm text-[hsl(var(--muted-foreground))]">Kunstler</p>
<p class="text-2xl font-bold text-[hsl(var(--foreground))]">{stats.totalArtists}</p>
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">Kunstler</p>
<p class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{stats.totalArtists}</p>
</div>
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-sm text-[hsl(var(--muted-foreground))]">Genres</p>
<p class="text-2xl font-bold text-[hsl(var(--foreground))]">{stats.totalGenres}</p>
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">Genres</p>
<p class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{stats.totalGenres}</p>
</div>
</div>
</section>
@ -56,28 +64,28 @@
<!-- Quick Actions -->
<section>
<h2
class="mb-4 text-sm font-medium uppercase tracking-wide text-[hsl(var(--muted-foreground))]"
class="mb-4 text-sm font-medium uppercase tracking-wide text-[hsl(var(--color-muted-foreground))]"
>
Schnellzugriff
</h2>
<div class="flex flex-wrap gap-3">
<a
href="/music/library"
class="inline-flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2.5 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90"
class="inline-flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90"
>
<MusicNote size={20} />
Bibliothek
</a>
<a
href="/music/playlists"
class="inline-flex items-center gap-2 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-2.5 text-sm font-medium text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))]"
class="inline-flex items-center gap-2 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] px-4 py-2.5 text-sm font-medium text-[hsl(var(--color-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<PlaylistIcon size={20} />
Playlists
</a>
<a
href="/music/projects"
class="inline-flex items-center gap-2 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-2.5 text-sm font-medium text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))]"
class="inline-flex items-center gap-2 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] px-4 py-2.5 text-sm font-medium text-[hsl(var(--color-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<Plus size={20} />
Projekte
@ -88,33 +96,35 @@
<!-- Recent Projects -->
<section>
<div class="mb-4 flex items-center justify-between">
<h2 class="text-sm font-medium uppercase tracking-wide text-[hsl(var(--muted-foreground))]">
<h2
class="text-sm font-medium uppercase tracking-wide text-[hsl(var(--color-muted-foreground))]"
>
Letzte Projekte
</h2>
<a href="/music/projects" class="text-sm text-[hsl(var(--primary))] hover:underline">
<a href="/music/projects" class="text-sm text-[hsl(var(--color-primary))] hover:underline">
Alle anzeigen
</a>
</div>
{#if projectsCtx.value.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-12"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-12"
>
<Note size={40} class="mb-3 text-[hsl(var(--muted-foreground))]" />
<p class="text-sm text-[hsl(var(--muted-foreground))]">Noch keine Projekte</p>
<Note size={40} class="mb-3 text-[hsl(var(--color-muted-foreground))]" />
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">Noch keine Projekte</p>
</div>
{:else}
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{#each projectsCtx.value.slice(0, 6) as project (project.id)}
<div
class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<h3 class="font-medium text-[hsl(var(--foreground))]">{project.title}</h3>
<h3 class="font-medium text-[hsl(var(--color-foreground))]">{project.title}</h3>
{#if project.description}
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))] line-clamp-2">
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))] line-clamp-2">
{project.description}
</p>
{/if}
<p class="mt-2 text-xs text-[hsl(var(--muted-foreground))]">
<p class="mt-2 text-xs text-[hsl(var(--color-muted-foreground))]">
Aktualisiert {formatDate(project.updatedAt)}
</p>
</div>

View file

@ -61,22 +61,22 @@
<div class="flex items-center gap-3">
<a
href="/music"
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowLeft size={20} />
</a>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Bibliothek</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Bibliothek</h1>
</div>
<!-- Tab Bar -->
<div class="flex max-w-md rounded-lg bg-[hsl(var(--muted))] p-1">
<div class="flex max-w-md rounded-lg bg-[hsl(var(--color-muted))] p-1">
{#each tabs as tab}
<button
onclick={() => (activeTab = tab)}
class="flex-1 rounded-md px-4 py-2 text-sm font-medium capitalize transition-colors {activeTab ===
tab
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
: 'text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]'}"
? 'bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))]'
: 'text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]'}"
>
{tab === 'songs' ? 'Songs' : tab === 'albums' ? 'Alben' : 'Genres'}
</button>
@ -88,13 +88,13 @@
<div class="relative">
<MagnifyingGlass
size={18}
class="absolute left-3 top-1/2 -translate-y-1/2 text-[hsl(var(--muted-foreground))]"
class="absolute left-3 top-1/2 -translate-y-1/2 text-[hsl(var(--color-muted-foreground))]"
/>
<input
type="text"
placeholder="Songs durchsuchen..."
bind:value={searchQuery}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] py-2.5 pl-10 pr-4 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] py-2.5 pl-10 pr-4 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
{/if}
@ -103,18 +103,18 @@
{#if activeTab === 'songs'}
{#if filteredSongs.length === 0}
<div class="flex flex-col items-center justify-center py-16">
<MusicNote size={48} class="mb-3 text-[hsl(var(--muted-foreground))]" />
<p class="text-[hsl(var(--muted-foreground))]">
<MusicNote size={48} class="mb-3 text-[hsl(var(--color-muted-foreground))]" />
<p class="text-[hsl(var(--color-muted-foreground))]">
{searchQuery ? 'Keine Songs gefunden' : 'Noch keine Songs in deiner Bibliothek'}
</p>
</div>
{:else}
<div
class="overflow-hidden rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))]"
class="overflow-hidden rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))]"
>
<!-- Header -->
<div
class="grid grid-cols-[40px_1fr_1fr_80px_40px_40px] gap-4 border-b border-[hsl(var(--border))] px-4 py-3 text-xs font-medium uppercase tracking-wide text-[hsl(var(--muted-foreground))]"
class="grid grid-cols-[40px_1fr_1fr_80px_40px_40px] gap-4 border-b border-[hsl(var(--color-border))] px-4 py-3 text-xs font-medium uppercase tracking-wide text-[hsl(var(--color-muted-foreground))]"
>
<span></span>
<span>Titel</span>
@ -135,15 +135,15 @@
handlePlaySong(song, index);
}
}}
class="group grid grid-cols-[40px_1fr_1fr_80px_40px_40px] items-center gap-4 px-4 py-3 transition-colors hover:bg-[hsl(var(--muted))] {playerStore
class="group grid grid-cols-[40px_1fr_1fr_80px_40px_40px] items-center gap-4 px-4 py-3 transition-colors hover:bg-[hsl(var(--color-muted))] {playerStore
.currentSong?.id === song.id
? 'bg-[hsl(var(--primary)/0.05)]'
? 'bg-[hsl(var(--color-primary)/0.05)]'
: ''}"
>
<div
class="relative flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded bg-[hsl(var(--muted))]"
class="relative flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded bg-[hsl(var(--color-muted))]"
>
<MusicNote size={20} class="text-[hsl(var(--muted-foreground))]" />
<MusicNote size={20} class="text-[hsl(var(--color-muted-foreground))]" />
{#if playerStore.currentSong?.id === song.id && playerStore.isPlaying}
<div class="absolute inset-0 flex items-center justify-center rounded bg-black/40">
<Pause size={20} weight="fill" class="text-white" />
@ -158,28 +158,28 @@
</div>
<span
class="truncate font-medium {playerStore.currentSong?.id === song.id
? 'text-[hsl(var(--primary))]'
: 'text-[hsl(var(--foreground))]'}"
? 'text-[hsl(var(--color-primary))]'
: 'text-[hsl(var(--color-foreground))]'}"
>
{song.title}
</span>
<span class="truncate text-[hsl(var(--muted-foreground))]">
<span class="truncate text-[hsl(var(--color-muted-foreground))]">
{song.artist ?? 'Unbekannt'}
</span>
<span class="text-right text-sm text-[hsl(var(--muted-foreground))]">
<span class="text-right text-sm text-[hsl(var(--color-muted-foreground))]">
{formatDuration(song.duration)}
</span>
<button
onclick={(e) => handleToggleFavorite(song.id, e)}
class="transition-colors {song.favorite
? 'text-red-500'
: 'text-[hsl(var(--muted-foreground))] hover:text-red-500'}"
: 'text-[hsl(var(--color-muted-foreground))] hover:text-red-500'}"
>
<Heart size={16} weight={song.favorite ? 'fill' : 'regular'} />
</button>
<button
onclick={(e) => handleDelete(song.id, e)}
class="text-[hsl(var(--muted-foreground))] opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
class="text-[hsl(var(--color-muted-foreground))] opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
>
<Trash size={16} />
</button>
@ -193,19 +193,21 @@
{#if activeTab === 'albums'}
{#if albums.length === 0}
<div class="flex flex-col items-center justify-center py-16">
<p class="text-[hsl(var(--muted-foreground))]">Keine Alben gefunden</p>
<p class="text-[hsl(var(--color-muted-foreground))]">Keine Alben gefunden</p>
</div>
{:else}
<div class="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4">
{#each albums as album}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<div
class="mb-3 flex aspect-square items-center justify-center rounded-lg bg-[hsl(var(--muted))]"
class="mb-3 flex aspect-square items-center justify-center rounded-lg bg-[hsl(var(--color-muted))]"
>
<MusicNote size={48} class="text-[hsl(var(--muted-foreground))]" />
<MusicNote size={48} class="text-[hsl(var(--color-muted-foreground))]" />
</div>
<h3 class="truncate font-medium text-[hsl(var(--foreground))]">{album.album}</h3>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<h3 class="truncate font-medium text-[hsl(var(--color-foreground))]">{album.album}</h3>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{album.songCount}
{album.songCount === 1 ? 'Song' : 'Songs'}
</p>
@ -219,18 +221,18 @@
{#if activeTab === 'genres'}
{#if genres.length === 0}
<div class="flex flex-col items-center justify-center py-16">
<p class="text-[hsl(var(--muted-foreground))]">Keine Genres gefunden</p>
<p class="text-[hsl(var(--color-muted-foreground))]">Keine Genres gefunden</p>
</div>
{:else}
<div
class="overflow-hidden rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))]"
class="overflow-hidden rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))]"
>
{#each genres as genre}
<div
class="flex items-center justify-between border-b border-[hsl(var(--border))] px-4 py-3 last:border-b-0"
class="flex items-center justify-between border-b border-[hsl(var(--color-border))] px-4 py-3 last:border-b-0"
>
<span class="font-medium text-[hsl(var(--foreground))]">{genre.genre}</span>
<span class="text-sm text-[hsl(var(--muted-foreground))]">
<span class="font-medium text-[hsl(var(--color-foreground))]">{genre.genre}</span>
<span class="text-sm text-[hsl(var(--color-muted-foreground))]">
{genre.songCount}
{genre.songCount === 1 ? 'Song' : 'Songs'}
</span>

View file

@ -51,15 +51,15 @@
<div class="flex items-center gap-3">
<a
href="/music"
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowLeft size={20} />
</a>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Playlists</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Playlists</h1>
</div>
<button
onclick={() => (showCreateModal = true)}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90"
>
<Plus size={16} />
Neue Playlist
@ -68,13 +68,13 @@
{#if playlistsCtx.value.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-16"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<PlaylistIcon size={48} class="mb-3 text-[hsl(var(--muted-foreground))]" />
<p class="mb-3 text-[hsl(var(--muted-foreground))]">Noch keine Playlists</p>
<PlaylistIcon size={48} class="mb-3 text-[hsl(var(--color-muted-foreground))]" />
<p class="mb-3 text-[hsl(var(--color-muted-foreground))]">Noch keine Playlists</p>
<button
onclick={() => (showCreateModal = true)}
class="text-sm text-[hsl(var(--primary))] hover:underline"
class="text-sm text-[hsl(var(--color-primary))] hover:underline"
>
Erste Playlist erstellen
</button>
@ -84,26 +84,26 @@
{#each playlistsCtx.value as playlist (playlist.id)}
<a
href="/music/playlists/{playlist.id}"
class="group relative rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="group relative rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<div
class="mb-3 flex aspect-square items-center justify-center overflow-hidden rounded-lg bg-[hsl(var(--muted))]"
class="mb-3 flex aspect-square items-center justify-center overflow-hidden rounded-lg bg-[hsl(var(--color-muted))]"
>
<MusicNote size={48} class="text-[hsl(var(--muted-foreground))]" />
<MusicNote size={48} class="text-[hsl(var(--color-muted-foreground))]" />
</div>
<h3
class="truncate font-medium text-[hsl(var(--foreground))] group-hover:text-[hsl(var(--primary))]"
class="truncate font-medium text-[hsl(var(--color-foreground))] group-hover:text-[hsl(var(--color-primary))]"
>
{playlist.name}
</h3>
{#if playlist.description}
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))] line-clamp-1">
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))] line-clamp-1">
{playlist.description}
</p>
{/if}
<button
onclick={(e) => handleDelete(playlist.id, e)}
class="absolute right-3 top-3 rounded-lg bg-[hsl(var(--card)/0.8)] p-1.5 text-[hsl(var(--muted-foreground))] opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
class="absolute right-3 top-3 rounded-lg bg-[hsl(var(--color-card)/0.8)] p-1.5 text-[hsl(var(--color-muted-foreground))] opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
>
<Trash size={16} />
</button>
@ -117,13 +117,13 @@
{#if showCreateModal}
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
<div
class="w-full max-w-md rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-6"
class="w-full max-w-md rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-6"
>
<div class="mb-4 flex items-center justify-between">
<h2 class="text-lg font-bold text-[hsl(var(--foreground))]">Neue Playlist</h2>
<h2 class="text-lg font-bold text-[hsl(var(--color-foreground))]">Neue Playlist</h2>
<button
onclick={() => (showCreateModal = false)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<X size={20} />
</button>
@ -142,7 +142,7 @@
bind:value={newName}
placeholder="Meine Playlist"
required
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div class="mb-6">
@ -153,21 +153,21 @@
bind:value={newDescription}
placeholder="Beschreibe deine Playlist..."
rows="3"
class="w-full resize-none rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full resize-none rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
></textarea>
</div>
<div class="flex justify-end gap-3">
<button
type="button"
onclick={() => (showCreateModal = false)}
class="px-4 py-2 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="px-4 py-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
Abbrechen
</button>
<button
type="submit"
disabled={!newName.trim() || isCreating}
class="rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90 disabled:opacity-50"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90 disabled:opacity-50"
>
{isCreating ? $_('common.creating') : $_('common.create')}
</button>

View file

@ -79,7 +79,7 @@
<div class="flex items-center gap-3">
<a
href="/music/playlists"
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowLeft size={20} />
</a>
@ -90,30 +90,30 @@
type="text"
bind:value={editName}
onkeydown={(e) => e.key === 'Enter' && saveName()}
class="rounded border border-[hsl(var(--border))] bg-transparent px-2 py-1 text-xl font-bold focus:outline-none focus:ring-1 focus:ring-[hsl(var(--primary))]"
class="rounded border border-[hsl(var(--color-border))] bg-transparent px-2 py-1 text-xl font-bold focus:outline-none focus:ring-1 focus:ring-[hsl(var(--color-primary))]"
/>
<button onclick={saveName} class="text-[hsl(var(--primary))]">
<button onclick={saveName} class="text-[hsl(var(--color-primary))]">
<Check size={18} />
</button>
<button
onclick={() => (isEditingName = false)}
class="text-[hsl(var(--muted-foreground))]"
class="text-[hsl(var(--color-muted-foreground))]"
>
<X size={18} />
</button>
</div>
{:else}
<button onclick={startEdit} class="group flex items-center gap-2">
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">
{playlist?.name || 'Playlist'}
</h1>
<PencilSimple
size={16}
class="text-[hsl(var(--muted-foreground))] opacity-0 group-hover:opacity-100"
class="text-[hsl(var(--color-muted-foreground))] opacity-0 group-hover:opacity-100"
/>
</button>
{/if}
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{songs.length}
{songs.length === 1 ? 'Song' : 'Songs'}
</p>
@ -123,7 +123,7 @@
{#if songs.length > 0}
<button
onclick={handlePlayAll}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90"
>
<Play size={16} weight="fill" />
Alle abspielen
@ -131,14 +131,14 @@
{/if}
<button
onclick={() => (showShare = true)}
class="rounded-lg p-2 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded-lg p-2 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
title="Kurzlink teilen"
>
<ShareNetwork size={20} />
</button>
<button
onclick={handleDeletePlaylist}
class="rounded-lg p-2 text-[hsl(var(--muted-foreground))] hover:text-red-500"
class="rounded-lg p-2 text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
title="Playlist loschen"
>
<Trash size={20} />
@ -149,12 +149,12 @@
<!-- Songs -->
{#if songs.length === 0}
<div class="flex flex-col items-center justify-center py-16">
<MusicNote size={48} class="mb-3 text-[hsl(var(--muted-foreground))]" />
<p class="text-[hsl(var(--muted-foreground))]">Keine Songs in dieser Playlist</p>
<MusicNote size={48} class="mb-3 text-[hsl(var(--color-muted-foreground))]" />
<p class="text-[hsl(var(--color-muted-foreground))]">Keine Songs in dieser Playlist</p>
</div>
{:else}
<div
class="overflow-hidden rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))]"
class="overflow-hidden rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))]"
>
{#each songs as song, index (song.id)}
<div
@ -167,15 +167,15 @@
handlePlaySong(song, index);
}
}}
class="group flex items-center gap-4 border-b border-[hsl(var(--border))] px-4 py-3 transition-colors last:border-b-0 hover:bg-[hsl(var(--muted))] {playerStore
class="group flex items-center gap-4 border-b border-[hsl(var(--color-border))] px-4 py-3 transition-colors last:border-b-0 hover:bg-[hsl(var(--color-muted))] {playerStore
.currentSong?.id === song.id
? 'bg-[hsl(var(--primary)/0.05)]'
? 'bg-[hsl(var(--color-primary)/0.05)]'
: ''}"
>
<div
class="relative flex h-10 w-10 shrink-0 items-center justify-center rounded bg-[hsl(var(--muted))]"
class="relative flex h-10 w-10 shrink-0 items-center justify-center rounded bg-[hsl(var(--color-muted))]"
>
<MusicNote size={20} class="text-[hsl(var(--muted-foreground))]" />
<MusicNote size={20} class="text-[hsl(var(--color-muted-foreground))]" />
{#if playerStore.currentSong?.id === song.id && playerStore.isPlaying}
<div class="absolute inset-0 flex items-center justify-center rounded bg-black/40">
<Pause size={20} weight="fill" class="text-white" />
@ -191,21 +191,21 @@
<div class="min-w-0 flex-1">
<p
class="truncate font-medium {playerStore.currentSong?.id === song.id
? 'text-[hsl(var(--primary))]'
: 'text-[hsl(var(--foreground))]'}"
? 'text-[hsl(var(--color-primary))]'
: 'text-[hsl(var(--color-foreground))]'}"
>
{song.title}
</p>
<p class="truncate text-sm text-[hsl(var(--muted-foreground))]">
<p class="truncate text-sm text-[hsl(var(--color-muted-foreground))]">
{song.artist ?? 'Unbekannt'}
</p>
</div>
<span class="text-sm text-[hsl(var(--muted-foreground))]">
<span class="text-sm text-[hsl(var(--color-muted-foreground))]">
{formatDuration(song.duration)}
</span>
<button
onclick={(e) => handleRemoveSong(song.id, e)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
title="Aus Playlist entfernen"
>
<Trash size={16} />

View file

@ -55,15 +55,15 @@
<div class="flex items-center gap-3">
<a
href="/music"
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowLeft size={20} />
</a>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Projekte</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Projekte</h1>
</div>
<button
onclick={() => (showCreateModal = true)}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90"
>
<Plus size={16} />
Neues Projekt
@ -72,13 +72,13 @@
{#if projectsCtx.value.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-16"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<Note size={48} class="mb-3 text-[hsl(var(--muted-foreground))]" />
<p class="mb-3 text-[hsl(var(--muted-foreground))]">Noch keine Projekte</p>
<Note size={48} class="mb-3 text-[hsl(var(--color-muted-foreground))]" />
<p class="mb-3 text-[hsl(var(--color-muted-foreground))]">Noch keine Projekte</p>
<button
onclick={() => (showCreateModal = true)}
class="text-sm text-[hsl(var(--primary))] hover:underline"
class="text-sm text-[hsl(var(--color-primary))] hover:underline"
>
Erstes Projekt erstellen
</button>
@ -87,23 +87,23 @@
<div class="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{#each projectsCtx.value as project (project.id)}
<div
class="group rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="group rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<div class="flex items-start justify-between">
<h3 class="font-medium text-[hsl(var(--foreground))]">{project.title}</h3>
<h3 class="font-medium text-[hsl(var(--color-foreground))]">{project.title}</h3>
<button
onclick={(e) => handleDelete(project.id, e)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] opacity-0 transition-opacity hover:text-red-500 group-hover:opacity-100"
>
<Trash size={16} />
</button>
</div>
{#if project.description}
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))] line-clamp-2">
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))] line-clamp-2">
{project.description}
</p>
{/if}
<p class="mt-2 text-xs text-[hsl(var(--muted-foreground))]">
<p class="mt-2 text-xs text-[hsl(var(--color-muted-foreground))]">
Aktualisiert {formatDate(project.updatedAt)}
</p>
</div>
@ -116,13 +116,13 @@
{#if showCreateModal}
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
<div
class="w-full max-w-md rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-6"
class="w-full max-w-md rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-6"
>
<div class="mb-4 flex items-center justify-between">
<h2 class="text-lg font-bold text-[hsl(var(--foreground))]">Neues Projekt</h2>
<h2 class="text-lg font-bold text-[hsl(var(--color-foreground))]">Neues Projekt</h2>
<button
onclick={() => (showCreateModal = false)}
class="rounded p-1 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded p-1 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<X size={20} />
</button>
@ -141,7 +141,7 @@
bind:value={newTitle}
placeholder="Mein Projekt"
required
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div class="mb-6">
@ -153,21 +153,21 @@
bind:value={newDescription}
placeholder="Beschreibe dein Projekt..."
rows="3"
class="w-full resize-none rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full resize-none rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-background))] px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
></textarea>
</div>
<div class="flex justify-end gap-3">
<button
type="button"
onclick={() => (showCreateModal = false)}
class="px-4 py-2 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="px-4 py-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
Abbrechen
</button>
<button
type="submit"
disabled={!newTitle.trim() || isCreating}
class="rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90 disabled:opacity-50"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90 disabled:opacity-50"
>
{isCreating ? $_('common.creating') : $_('common.create')}
</button>

View file

@ -257,18 +257,19 @@
display: flex;
flex-direction: column;
gap: 1.25rem;
color: hsl(var(--color-foreground));
}
.header h1 {
margin: 0 0 0.25rem;
font-size: 1.5rem;
}
.hint {
color: var(--text-muted, #888);
color: hsl(var(--color-muted-foreground));
margin: 0;
}
.block {
background: var(--surface, #fff);
border: 1px solid var(--border, #e5e5e5);
background: hsl(var(--color-background));
border: 1px solid hsl(var(--color-border));
border-radius: 0.75rem;
padding: 1rem;
display: flex;
@ -286,13 +287,13 @@
font-size: 1.1rem;
}
.sub {
color: var(--text-muted, #888);
color: hsl(var(--color-muted-foreground));
font-size: 0.85rem;
}
.mode-switch {
display: inline-flex;
gap: 0.25rem;
background: var(--surface-alt, #f4f4f4);
background: hsl(var(--color-muted));
padding: 0.25rem;
border-radius: 0.5rem;
width: fit-content;
@ -303,10 +304,11 @@
padding: 0.35rem 0.75rem;
border-radius: 0.35rem;
cursor: pointer;
color: var(--text, #333);
color: hsl(var(--color-muted-foreground));
}
.mode-switch button.active {
background: var(--surface, #fff);
background: hsl(var(--color-background));
color: hsl(var(--color-foreground));
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
}
.discover-form,
@ -318,17 +320,17 @@
.search-form input {
flex: 1;
padding: 0.5rem 0.75rem;
border: 1px solid var(--border, #e5e5e5);
border: 1px solid hsl(var(--color-border));
border-radius: 0.5rem;
background: var(--surface, #fff);
color: var(--text, #333);
background: hsl(var(--color-background));
color: hsl(var(--color-foreground));
}
button[type='submit'] {
padding: 0.5rem 1rem;
border: none;
border-radius: 0.5rem;
background: var(--accent, #10b981);
color: white;
background: hsl(var(--color-primary));
color: hsl(var(--color-primary-foreground, 0 0% 100%));
cursor: pointer;
}
button[type='submit']:disabled {
@ -336,9 +338,9 @@
cursor: not-allowed;
}
.secondary {
background: var(--surface-alt, #f4f4f4);
color: var(--text, #333);
border: 1px solid var(--border, #e5e5e5);
background: hsl(var(--color-muted));
color: hsl(var(--color-foreground));
border: 1px solid hsl(var(--color-border));
border-radius: 0.5rem;
padding: 0.35rem 0.75rem;
cursor: pointer;
@ -357,6 +359,7 @@
align-items: center;
gap: 0.5rem;
cursor: pointer;
color: hsl(var(--color-foreground));
}
.feed-title {
font-weight: 500;
@ -364,26 +367,26 @@
.feed-type,
.feed-src {
font-size: 0.75rem;
color: var(--text-muted, #888);
color: hsl(var(--color-muted-foreground));
}
.pin {
margin-left: auto;
background: transparent;
border: 1px solid var(--border, #e5e5e5);
border: 1px solid hsl(var(--color-border));
border-radius: 0.35rem;
padding: 0.15rem 0.55rem;
font-size: 0.75rem;
cursor: pointer;
color: var(--text, #333);
color: hsl(var(--color-foreground));
}
.pin.pinned {
background: var(--accent, #0891b2);
color: white;
background: hsl(var(--color-primary));
color: hsl(var(--color-primary-foreground, 0 0% 100%));
border-color: transparent;
}
.result {
padding: 0.75rem;
border: 1px solid var(--border, #eee);
border: 1px solid hsl(var(--color-border));
border-radius: 0.5rem;
display: flex;
flex-direction: column;
@ -391,7 +394,7 @@
}
.r-title {
font-weight: 600;
color: var(--text, #333);
color: hsl(var(--color-foreground));
text-decoration: none;
}
.r-title:hover {
@ -401,38 +404,38 @@
display: flex;
gap: 0.4rem;
font-size: 0.8rem;
color: var(--text-muted, #888);
color: hsl(var(--color-muted-foreground));
flex-wrap: wrap;
}
.r-excerpt {
margin: 0.25rem 0 0;
color: var(--text, #333);
color: hsl(var(--color-foreground));
font-size: 0.9rem;
}
.save {
align-self: flex-start;
background: transparent;
border: 1px solid var(--border, #e5e5e5);
border: 1px solid hsl(var(--color-border));
border-radius: 0.35rem;
padding: 0.25rem 0.65rem;
font-size: 0.8rem;
cursor: pointer;
color: var(--text, #333);
color: hsl(var(--color-foreground));
}
.save:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.error {
background: #fee;
border: 1px solid #fcc;
color: #900;
background: hsl(var(--color-destructive) / 0.1);
border: 1px solid hsl(var(--color-destructive) / 0.4);
color: hsl(var(--color-destructive));
padding: 0.5rem 0.75rem;
border-radius: 0.5rem;
font-size: 0.9rem;
}
.empty-hint {
color: var(--text-muted, #888);
color: hsl(var(--color-muted-foreground));
font-size: 0.9rem;
padding: 0.5rem 0;
}

View file

@ -745,7 +745,7 @@
.sidebar {
width: 220px;
flex-shrink: 0;
border-right: 1px solid hsl(var(--border));
border-right: 1px solid hsl(var(--color-border));
display: flex;
flex-direction: column;
padding: 1rem 0.75rem;
@ -757,18 +757,18 @@
align-items: center;
gap: 0.375rem;
padding: 0.5rem 0.75rem;
border: 1px dashed hsl(var(--border));
border: 1px dashed hsl(var(--color-border));
border-radius: 0.5rem;
background: transparent;
color: hsl(var(--muted-foreground));
color: hsl(var(--color-muted-foreground));
font-size: 0.8125rem;
cursor: pointer;
transition: all 0.15s;
}
.new-chat-btn:hover {
border-color: hsl(var(--border) / 0.8);
color: hsl(var(--foreground));
background: hsl(var(--muted));
border-color: hsl(var(--color-border) / 0.8);
color: hsl(var(--color-foreground));
background: hsl(var(--color-muted));
}
.conv-list {
display: flex;
@ -783,18 +783,18 @@
border: none;
border-radius: 0.375rem;
background: transparent;
color: hsl(var(--foreground));
color: hsl(var(--color-foreground));
font-size: 0.75rem;
cursor: pointer;
text-align: left;
transition: background 0.1s;
}
.conv-item:hover {
background: hsl(var(--muted));
background: hsl(var(--color-muted));
}
.conv-item.active {
background: hsl(var(--primary) / 0.1);
color: hsl(var(--primary));
background: hsl(var(--color-primary) / 0.1);
color: hsl(var(--color-primary));
}
.conv-title {
flex: 1;
@ -808,15 +808,15 @@
align-items: center;
padding: 0.125rem;
border-radius: 0.25rem;
color: hsl(var(--muted-foreground));
color: hsl(var(--color-muted-foreground));
cursor: pointer;
}
.conv-item:hover .conv-delete {
display: flex;
}
.conv-delete:hover {
color: hsl(var(--destructive));
background: hsl(var(--destructive) / 0.1);
color: hsl(var(--color-error));
background: hsl(var(--color-error) / 0.1);
}
.sidebar-toggle {
display: flex;
@ -827,12 +827,12 @@
border: none;
border-radius: 0.375rem;
background: transparent;
color: hsl(var(--muted-foreground));
color: hsl(var(--color-muted-foreground));
cursor: pointer;
}
.sidebar-toggle:hover {
background: hsl(var(--muted));
color: hsl(var(--foreground));
background: hsl(var(--color-muted));
color: hsl(var(--color-foreground));
}
.config-bar {
margin-bottom: 1rem;
@ -841,8 +841,8 @@
align-items: flex-end;
gap: 1rem;
border-radius: 0.75rem;
border: 1px solid hsl(var(--border));
background: hsl(var(--card));
border: 1px solid hsl(var(--color-border));
background: hsl(var(--color-card));
padding: 1rem;
}
.comparison-selector {
@ -852,8 +852,8 @@
align-items: center;
gap: 0.5rem;
border-radius: 0.75rem;
border: 1px solid hsl(var(--primary) / 0.2);
background: hsl(var(--primary) / 0.03);
border: 1px solid hsl(var(--color-primary) / 0.2);
background: hsl(var(--color-primary) / 0.03);
padding: 0.75rem 1rem;
}
.comparison-grid {

View file

@ -69,23 +69,23 @@
<!-- Header -->
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">
{selectedCollection ? selectedCollection.name : 'Alle Fragen'}
</h1>
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))]">
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))]">
{filteredQuestions.length} Frage{filteredQuestions.length !== 1 ? 'n' : ''}
</p>
</div>
<div class="flex gap-2">
<a
href="/questions/collections"
class="rounded-lg border border-[hsl(var(--border))] px-4 py-2 text-sm font-medium text-[hsl(var(--foreground))] transition-colors hover:bg-[hsl(var(--muted))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-foreground))] transition-colors hover:bg-[hsl(var(--color-muted))]"
>
Sammlungen
</a>
<a
href="/questions/new"
class="rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] transition-colors hover:opacity-90"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] transition-colors hover:opacity-90"
>
Neue Frage
</a>
@ -96,19 +96,19 @@
<div class="flex gap-3">
<div class="relative flex-1">
<MagnifyingGlass
class="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[hsl(var(--muted-foreground))]"
class="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-[hsl(var(--color-muted-foreground))]"
/>
<input
type="text"
bind:value={searchQuery}
placeholder="Fragen durchsuchen..."
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] py-2 pl-10 pr-4 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] py-2 pl-10 pr-4 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<select
bind:value={statusFilter}
class="rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
>
<option value="">Alle Status</option>
<option value="open">Offen</option>
@ -120,7 +120,7 @@
{#if collections.length > 0}
<select
bind:value={selectedCollectionId}
class="rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
>
<option value={null}>Alle Sammlungen</option>
{#each collections as collection}
@ -133,16 +133,16 @@
<!-- Questions List -->
{#if filteredQuestions.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-16"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<span class="mb-4 text-5xl">🔍</span>
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--foreground))]">Keine Fragen</h2>
<p class="mb-6 text-sm text-[hsl(var(--muted-foreground))]">
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--color-foreground))]">Keine Fragen</h2>
<p class="mb-6 text-sm text-[hsl(var(--color-muted-foreground))]">
Stelle deine erste Frage und lass die KI recherchieren.
</p>
<a
href="/questions/new"
class="rounded-lg bg-[hsl(var(--primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="rounded-lg bg-[hsl(var(--color-primary))] px-6 py-2.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
Neue Frage
</a>
@ -155,7 +155,7 @@
<a
href="/questions/{question.id}"
class="block rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="block rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<div class="flex items-start gap-4">
<div class="mt-1">
@ -167,12 +167,12 @@
</div>
<div class="min-w-0 flex-1">
<h3 class="font-medium text-[hsl(var(--foreground))] line-clamp-2">
<h3 class="font-medium text-[hsl(var(--color-foreground))] line-clamp-2">
{question.title}
</h3>
{#if question.description}
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))] line-clamp-2">
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))] line-clamp-2">
{question.description}
</p>
{/if}
@ -182,13 +182,13 @@
<div class="flex gap-1">
{#each question.tags.slice(0, 3) as tag}
<span
class="rounded-full bg-[hsl(var(--muted))] px-2 py-0.5 text-xs text-[hsl(var(--foreground))]"
class="rounded-full bg-[hsl(var(--color-muted))] px-2 py-0.5 text-xs text-[hsl(var(--color-foreground))]"
>
{tag}
</span>
{/each}
{#if question.tags.length > 3}
<span class="text-xs text-[hsl(var(--muted-foreground))]"
<span class="text-xs text-[hsl(var(--color-muted-foreground))]"
>+{question.tags.length - 3}</span
>
{/if}
@ -196,12 +196,12 @@
{/if}
<span
class="rounded-full bg-[hsl(var(--muted))] px-2 py-0.5 text-xs text-[hsl(var(--muted-foreground))]"
class="rounded-full bg-[hsl(var(--color-muted))] px-2 py-0.5 text-xs text-[hsl(var(--color-muted-foreground))]"
>
{depthLabels[question.researchDepth]}
</span>
<span class="text-xs text-[hsl(var(--muted-foreground))]">
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">
{formatDate(question.createdAt)}
</span>
</div>

View file

@ -258,8 +258,8 @@
{#if !question}
<div class="py-16 text-center">
<p class="text-[hsl(var(--muted-foreground))]">Frage nicht gefunden</p>
<a href="/questions" class="mt-4 inline-block text-[hsl(var(--primary))]">Zurueck</a>
<p class="text-[hsl(var(--color-muted-foreground))]">Frage nicht gefunden</p>
<a href="/questions" class="mt-4 inline-block text-[hsl(var(--color-primary))]">Zurueck</a>
</div>
{:else}
<div class="mx-auto max-w-3xl space-y-6">
@ -267,7 +267,7 @@
<div>
<a
href="/questions"
class="mb-4 inline-flex items-center gap-2 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="mb-4 inline-flex items-center gap-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<ArrowLeft class="h-4 w-4" />
Zurueck zu Fragen
@ -279,32 +279,32 @@
<input
type="text"
bind:value={editTitle}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2 text-xl font-bold text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2 text-xl font-bold text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
<textarea
bind:value={editDescription}
placeholder="Beschreibung..."
rows="2"
class="mt-2 w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="mt-2 w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
></textarea>
<div class="mt-2 flex gap-2">
<button
onclick={saveEdit}
class="rounded-lg bg-[hsl(var(--primary))] px-3 py-1.5 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="rounded-lg bg-[hsl(var(--color-primary))] px-3 py-1.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
Speichern
</button>
<button
onclick={() => (editing = false)}
class="rounded-lg border border-[hsl(var(--border))] px-3 py-1.5 text-sm text-[hsl(var(--foreground))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 py-1.5 text-sm text-[hsl(var(--color-foreground))]"
>
Abbrechen
</button>
</div>
{:else}
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">{question.title}</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{question.title}</h1>
{#if question.description}
<p class="mt-2 text-[hsl(var(--muted-foreground))]">{question.description}</p>
<p class="mt-2 text-[hsl(var(--color-muted-foreground))]">{question.description}</p>
{/if}
{/if}
@ -319,7 +319,7 @@
<!-- Depth -->
<span
class="rounded-full bg-[hsl(var(--muted))] px-2 py-0.5 text-xs text-[hsl(var(--muted-foreground))]"
class="rounded-full bg-[hsl(var(--color-muted))] px-2 py-0.5 text-xs text-[hsl(var(--color-muted-foreground))]"
>
{depthLabels[question.researchDepth] ?? question.researchDepth}
</span>
@ -328,7 +328,7 @@
{#if question.tags?.length}
{#each question.tags as tag}
<span
class="rounded-full bg-[hsl(var(--muted))] px-2 py-0.5 text-xs text-[hsl(var(--foreground))]"
class="rounded-full bg-[hsl(var(--color-muted))] px-2 py-0.5 text-xs text-[hsl(var(--color-foreground))]"
>
{tag}
</span>
@ -336,7 +336,7 @@
{/if}
<!-- Date -->
<span class="text-sm text-[hsl(var(--muted-foreground))]">
<span class="text-sm text-[hsl(var(--color-muted-foreground))]">
{formatDate(question.createdAt)}
</span>
</div>
@ -347,7 +347,7 @@
<div class="flex gap-2">
<button
onclick={startEditing}
class="rounded-lg border border-[hsl(var(--border))] p-2 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded-lg border border-[hsl(var(--color-border))] p-2 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
title={$_('common.edit')}
>
<PencilSimple class="h-4 w-4" />
@ -372,8 +372,8 @@
disabled={question.status === status}
class="rounded-lg border px-3 py-1.5 text-sm transition-colors
{question.status === status
? 'border-[hsl(var(--primary))] bg-[hsl(var(--primary)/0.1)] text-[hsl(var(--primary))]'
: 'border-[hsl(var(--border))] text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))]'}"
? 'border-[hsl(var(--color-primary))] bg-[hsl(var(--color-primary)/0.1)] text-[hsl(var(--color-primary))]'
: 'border-[hsl(var(--color-border))] text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))]'}"
>
{statusLabels[status]?.label}
</button>
@ -381,11 +381,13 @@
</div>
<!-- Deep Research -->
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-5">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-5"
>
<div class="flex items-start justify-between gap-4">
<div class="flex-1">
<h3 class="text-sm font-semibold text-[hsl(var(--foreground))]">Recherche</h3>
<p class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">
<h3 class="text-sm font-semibold text-[hsl(var(--color-foreground))]">Recherche</h3>
<p class="mt-1 text-xs text-[hsl(var(--color-muted-foreground))]">
{#if question.researchDepth === 'quick'}
Schnell · 5 Quellen · keine Volltext-Extraktion
{:else if question.researchDepth === 'standard'}
@ -398,14 +400,14 @@
{#if researchHandle}
<button
onclick={cancelResearch}
class="rounded-lg border border-[hsl(var(--border))] px-3 py-1.5 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 py-1.5 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
Stream beenden
</button>
{:else if (answers as Answer[]).some((a) => a.researchResultId)}
<button
onclick={rerunResearch}
class="inline-flex items-center gap-2 rounded-lg border border-[hsl(var(--border))] px-3 py-1.5 text-sm font-medium text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))]"
class="inline-flex items-center gap-2 rounded-lg border border-[hsl(var(--color-border))] px-3 py-1.5 text-sm font-medium text-[hsl(var(--color-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
<ArrowCounterClockwise class="h-4 w-4" />
Erneut recherchieren
@ -413,7 +415,7 @@
{:else}
<button
onclick={startResearchRun}
class="inline-flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-3 py-1.5 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90"
class="inline-flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-3 py-1.5 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90"
>
<MagnifyingGlass class="h-4 w-4" />
Recherche starten
@ -422,7 +424,7 @@
</div>
{#if researchPhase}
<div class="mt-3 flex items-center gap-2 text-sm text-[hsl(var(--muted-foreground))]">
<div class="mt-3 flex items-center gap-2 text-sm text-[hsl(var(--color-muted-foreground))]">
<CircleNotch class="h-4 w-4 animate-spin" />
<span>{phaseLabels[researchPhase] ?? researchPhase}</span>
{#if researchSourceCount !== null}
@ -434,22 +436,26 @@
<!-- Answers -->
<div class="space-y-4">
<h2 class="text-lg font-semibold text-[hsl(var(--foreground))]">
<h2 class="text-lg font-semibold text-[hsl(var(--color-foreground))]">
Antworten ({answers.length})
</h2>
{#if answers.length === 0}
<div class="rounded-xl border-2 border-dashed border-[hsl(var(--border))] p-8 text-center">
<div
class="rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] p-8 text-center"
>
<span class="mb-2 block text-4xl">📝</span>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
Noch keine Antworten. Fuege die erste Antwort hinzu.
</p>
</div>
{:else}
{#each answers as answer (answer.id)}
<div
class="rounded-xl border bg-[hsl(var(--card))] p-5
{answer.isAccepted ? 'border-green-300 dark:border-green-800' : 'border-[hsl(var(--border))]'}"
class="rounded-xl border bg-[hsl(var(--color-card))] p-5
{answer.isAccepted
? 'border-green-300 dark:border-green-800'
: 'border-[hsl(var(--color-border))]'}"
>
{#if answer.isAccepted}
<div
@ -466,7 +472,7 @@
/>
<div class="mt-4 flex items-center justify-between">
<span class="text-xs text-[hsl(var(--muted-foreground))]">
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">
{formatDate(answer.createdAt)}
</span>
<div class="flex gap-2">
@ -491,21 +497,23 @@
{/if}
<!-- Add Answer -->
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-5">
<h3 class="mb-3 text-sm font-semibold text-[hsl(var(--foreground))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-5"
>
<h3 class="mb-3 text-sm font-semibold text-[hsl(var(--color-foreground))]">
Antwort hinzufuegen
</h3>
<textarea
bind:value={newAnswer}
placeholder="Deine Antwort..."
rows="4"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-3 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-3 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
></textarea>
<div class="mt-3 flex justify-end">
<button
onclick={addAnswer}
disabled={savingAnswer || !newAnswer.trim()}
class="rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90 disabled:opacity-50"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90 disabled:opacity-50"
>
{savingAnswer ? 'Speichert...' : 'Antwort senden'}
</button>

View file

@ -98,19 +98,19 @@
<div>
<a
href="/questions"
class="mb-4 inline-flex items-center gap-2 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="mb-4 inline-flex items-center gap-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<ArrowLeft class="h-4 w-4" />
Zurueck zu Fragen
</a>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Sammlungen</h1>
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Sammlungen</h1>
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))]">
Organisiere deine Fragen in Sammlungen
</p>
</div>
<button
onclick={openCreateModal}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90"
>
<Plus class="h-5 w-5" />
Neue Sammlung
@ -120,16 +120,18 @@
<!-- Collections List -->
{#if collections.length === 0}
<div
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--border))] py-16"
class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[hsl(var(--color-border))] py-16"
>
<span class="mb-4 text-4xl">📁</span>
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--foreground))]">Keine Sammlungen</h2>
<p class="mb-4 text-sm text-[hsl(var(--muted-foreground))]">
<h2 class="mb-2 text-lg font-semibold text-[hsl(var(--color-foreground))]">
Keine Sammlungen
</h2>
<p class="mb-4 text-sm text-[hsl(var(--color-muted-foreground))]">
Erstelle deine erste Sammlung, um Fragen zu organisieren.
</p>
<button
onclick={openCreateModal}
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="flex items-center gap-2 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
<Plus class="h-5 w-5" />
Sammlung erstellen
@ -139,7 +141,7 @@
<div class="space-y-3">
{#each collections as collection (collection.id)}
<div
class="flex items-center gap-4 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 transition-all hover:border-[hsl(var(--primary)/0.3)]"
class="flex items-center gap-4 rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4 transition-all hover:border-[hsl(var(--color-primary)/0.3)]"
>
<!-- Icon & Color -->
<div
@ -152,21 +154,21 @@
<!-- Info -->
<div class="min-w-0 flex-1">
<div class="flex items-center gap-2">
<h3 class="font-medium text-[hsl(var(--foreground))]">{collection.name}</h3>
<h3 class="font-medium text-[hsl(var(--color-foreground))]">{collection.name}</h3>
{#if collection.isDefault}
<span
class="rounded-full bg-[hsl(var(--primary)/0.1)] px-2 py-0.5 text-xs text-[hsl(var(--primary))]"
class="rounded-full bg-[hsl(var(--color-primary)/0.1)] px-2 py-0.5 text-xs text-[hsl(var(--color-primary))]"
>
Standard
</span>
{/if}
</div>
{#if collection.description}
<p class="mt-0.5 truncate text-sm text-[hsl(var(--muted-foreground))]">
<p class="mt-0.5 truncate text-sm text-[hsl(var(--color-muted-foreground))]">
{collection.description}
</p>
{/if}
<p class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">
<p class="mt-1 text-xs text-[hsl(var(--color-muted-foreground))]">
{getQuestionCountByCollection(questions, collection.id)} Fragen
</p>
</div>
@ -175,7 +177,7 @@
<div class="flex items-center gap-2">
<button
onclick={() => openEditModal(collection)}
class="rounded-lg p-2 text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--muted))] hover:text-[hsl(var(--foreground))]"
class="rounded-lg p-2 text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-muted))] hover:text-[hsl(var(--color-foreground))]"
title={$_('common.edit')}
>
<PencilSimple class="h-4 w-4" />
@ -191,7 +193,7 @@
</button>
<button
onclick={() => (deleteConfirm = null)}
class="rounded-lg border border-[hsl(var(--border))] px-3 py-1 text-sm text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 py-1 text-sm text-[hsl(var(--color-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
Abbrechen
</button>
@ -199,7 +201,7 @@
{:else}
<button
onclick={() => (deleteConfirm = collection.id)}
class="rounded-lg p-2 text-[hsl(var(--muted-foreground))] hover:bg-red-50 hover:text-red-500 dark:hover:bg-red-900/20"
class="rounded-lg p-2 text-[hsl(var(--color-muted-foreground))] hover:bg-red-50 hover:text-red-500 dark:hover:bg-red-900/20"
title="Loeschen"
>
<Trash class="h-4 w-4" />
@ -216,9 +218,9 @@
{#if showModal}
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div
class="mx-4 w-full max-w-md rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-6"
class="mx-4 w-full max-w-md rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-6"
>
<h2 class="mb-4 text-lg font-semibold text-[hsl(var(--foreground))]">
<h2 class="mb-4 text-lg font-semibold text-[hsl(var(--color-foreground))]">
{editingCollection ? 'Sammlung bearbeiten' : 'Neue Sammlung'}
</h2>
@ -226,7 +228,7 @@
<div>
<label
for="collection-name"
class="mb-1 block text-sm font-medium text-[hsl(var(--foreground))]"
class="mb-1 block text-sm font-medium text-[hsl(var(--color-foreground))]"
>
Name
</label>
@ -235,14 +237,14 @@
type="text"
bind:value={formName}
placeholder="Sammlungsname"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<div>
<label
for="collection-desc"
class="mb-1 block text-sm font-medium text-[hsl(var(--foreground))]"
class="mb-1 block text-sm font-medium text-[hsl(var(--color-foreground))]"
>
Beschreibung
</label>
@ -251,14 +253,14 @@
bind:value={formDescription}
placeholder="Optionale Beschreibung"
rows="2"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
></textarea>
</div>
<div>
<label
for="collection-color"
class="mb-1 block text-sm font-medium text-[hsl(var(--foreground))]"
class="mb-1 block text-sm font-medium text-[hsl(var(--color-foreground))]"
>
Farbe
</label>
@ -266,7 +268,7 @@
id="collection-color"
type="color"
bind:value={formColor}
class="h-10 w-20 cursor-pointer rounded-lg border border-[hsl(var(--border))]"
class="h-10 w-20 cursor-pointer rounded-lg border border-[hsl(var(--color-border))]"
/>
</div>
</div>
@ -274,14 +276,14 @@
<div class="mt-6 flex justify-end gap-3">
<button
onclick={closeModal}
class="rounded-lg border border-[hsl(var(--border))] px-4 py-2 text-sm text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
{$_('common.cancel')}
</button>
<button
onclick={handleSave}
disabled={!formName.trim()}
class="rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90 disabled:opacity-50"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90 disabled:opacity-50"
>
{editingCollection ? $_('common.save') : $_('common.create')}
</button>

View file

@ -100,13 +100,13 @@
<div>
<a
href="/questions"
class="mb-4 inline-flex items-center gap-2 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="mb-4 inline-flex items-center gap-2 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<ArrowLeft class="h-4 w-4" />
Zurueck zu Fragen
</a>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">Neue Frage</h1>
<p class="mt-1 text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">Neue Frage</h1>
<p class="mt-1 text-sm text-[hsl(var(--color-muted-foreground))]">
Stelle eine Frage und lass die KI recherchieren
</p>
</div>
@ -122,7 +122,7 @@
<!-- Question Title -->
<div>
<label for="title" class="mb-2 block text-sm font-medium text-[hsl(var(--foreground))]">
<label for="title" class="mb-2 block text-sm font-medium text-[hsl(var(--color-foreground))]">
Deine Frage
</label>
<input
@ -130,21 +130,24 @@
id="title"
bind:value={title}
placeholder="Was moechtest du wissen?"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-3 text-lg text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-3 text-lg text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<!-- Description -->
<div>
<label for="description" class="mb-2 block text-sm font-medium text-[hsl(var(--foreground))]">
Kontext <span class="text-[hsl(var(--muted-foreground))]">(optional)</span>
<label
for="description"
class="mb-2 block text-sm font-medium text-[hsl(var(--color-foreground))]"
>
Kontext <span class="text-[hsl(var(--color-muted-foreground))]">(optional)</span>
</label>
<textarea
id="description"
bind:value={description}
placeholder="Zusaetzliche Details oder Kontext..."
rows="3"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-3 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-3 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
></textarea>
</div>
@ -153,14 +156,14 @@
<div>
<label
for="collection"
class="mb-2 block text-sm font-medium text-[hsl(var(--foreground))]"
class="mb-2 block text-sm font-medium text-[hsl(var(--color-foreground))]"
>
Sammlung
</label>
<select
id="collection"
bind:value={collectionId}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
>
<option value={undefined}>Keine Sammlung</option>
{#each collections as collection}
@ -172,19 +175,19 @@
<!-- Tags -->
<div>
<label for="tags" class="mb-2 block text-sm font-medium text-[hsl(var(--foreground))]">
<label for="tags" class="mb-2 block text-sm font-medium text-[hsl(var(--color-foreground))]">
Tags
</label>
<div class="mb-2 flex flex-wrap gap-2">
{#each tags as tag}
<span
class="inline-flex items-center gap-1 rounded-full bg-[hsl(var(--muted))] px-3 py-1 text-sm text-[hsl(var(--foreground))]"
class="inline-flex items-center gap-1 rounded-full bg-[hsl(var(--color-muted))] px-3 py-1 text-sm text-[hsl(var(--color-foreground))]"
>
{tag}
<button
type="button"
onclick={() => removeTag(tag)}
class="ml-1 text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="ml-1 text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
&times;
</button>
@ -197,13 +200,13 @@
bind:value={tagInput}
onkeydown={(e) => e.key === 'Enter' && (e.preventDefault(), addTag())}
placeholder="Tag eingeben und Enter druecken"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
/>
</div>
<!-- Research Depth -->
<div>
<span class="mb-2 block text-sm font-medium text-[hsl(var(--foreground))]">
<span class="mb-2 block text-sm font-medium text-[hsl(var(--color-foreground))]">
Recherchetiefe
</span>
<div class="grid grid-cols-3 gap-3">
@ -213,12 +216,12 @@
type="button"
onclick={() => (researchDepth = option.value)}
class="rounded-lg border-2 p-4 text-left transition-all {researchDepth === option.value
? 'border-[hsl(var(--primary))] bg-[hsl(var(--primary)/0.05)]'
: 'border-[hsl(var(--border))] hover:border-[hsl(var(--primary)/0.3)]'}"
? 'border-[hsl(var(--color-primary))] bg-[hsl(var(--color-primary)/0.05)]'
: 'border-[hsl(var(--color-border))] hover:border-[hsl(var(--color-primary)/0.3)]'}"
>
<OptionIcon class="mb-2 h-5 w-5 text-[hsl(var(--primary))]" />
<div class="font-medium text-[hsl(var(--foreground))]">{option.label}</div>
<div class="mt-1 text-xs text-[hsl(var(--muted-foreground))]">
<OptionIcon class="mb-2 h-5 w-5 text-[hsl(var(--color-primary))]" />
<div class="font-medium text-[hsl(var(--color-foreground))]">{option.label}</div>
<div class="mt-1 text-xs text-[hsl(var(--color-muted-foreground))]">
{option.description}
</div>
</button>
@ -228,13 +231,16 @@
<!-- Priority -->
<div>
<label for="priority" class="mb-2 block text-sm font-medium text-[hsl(var(--foreground))]">
<label
for="priority"
class="mb-2 block text-sm font-medium text-[hsl(var(--color-foreground))]"
>
Prioritaet
</label>
<select
id="priority"
bind:value={priority}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--primary))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none focus:ring-2 focus:ring-[hsl(var(--color-primary))]"
>
<option value="low">Niedrig</option>
<option value="normal">Normal</option>
@ -247,14 +253,14 @@
<div class="flex gap-3">
<a
href="/questions"
class="flex-1 rounded-lg border border-[hsl(var(--border))] px-4 py-3 text-center text-sm font-medium text-[hsl(var(--foreground))] hover:bg-[hsl(var(--muted))]"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] px-4 py-3 text-center text-sm font-medium text-[hsl(var(--color-foreground))] hover:bg-[hsl(var(--color-muted))]"
>
Abbrechen
</a>
<button
type="submit"
disabled={loading || !title.trim()}
class="flex-1 rounded-lg bg-[hsl(var(--primary))] px-4 py-3 text-sm font-medium text-[hsl(var(--primary-foreground))] hover:opacity-90 disabled:opacity-50"
class="flex-1 rounded-lg bg-[hsl(var(--color-primary))] px-4 py-3 text-sm font-medium text-[hsl(var(--color-primary-foreground))] hover:opacity-90 disabled:opacity-50"
>
{loading ? $_('common.creating') : 'Frage stellen'}
</button>

View file

@ -685,10 +685,10 @@
}
.card {
border: 1px solid var(--border, #e5e7eb);
border: 1px solid hsl(var(--color-border));
border-radius: 0.75rem;
padding: 1.25rem 1.5rem;
background: var(--surface, #fff);
background: hsl(var(--color-surface));
}
.card.danger {
border-color: rgba(220, 38, 38, 0.25);
@ -764,8 +764,8 @@
gap: 0.5rem;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
border: 1px solid var(--border, #e5e7eb);
background: var(--surface, #fff);
border: 1px solid hsl(var(--color-border));
background: hsl(var(--color-surface));
font-size: 0.9rem;
cursor: pointer;
font-weight: 500;
@ -779,7 +779,7 @@
}
.btn-primary {
background: var(--primary, #6366f1);
background: hsl(var(--color-primary));
color: white;
border-color: transparent;
}
@ -840,7 +840,7 @@
.zk-step {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid var(--border, #e5e7eb);
border-top: 1px solid hsl(var(--color-border));
}
.zk-step h3 {
@ -863,7 +863,7 @@
margin: 1rem 0;
padding: 1rem 1.25rem;
background: var(--surface-muted, #f9fafb);
border: 1px solid var(--border, #e5e7eb);
border: 1px solid hsl(var(--color-border));
border-radius: 0.5rem;
font-family: ui-monospace, SFMono-Regular, monospace;
font-size: 1rem;
@ -879,15 +879,15 @@
width: 100%;
margin: 0.75rem 0;
padding: 0.75rem 1rem;
border: 1px solid var(--border, #e5e7eb);
border: 1px solid hsl(var(--color-border));
border-radius: 0.5rem;
font-family: ui-monospace, SFMono-Regular, monospace;
font-size: 0.95rem;
background: var(--surface, #fff);
background: hsl(var(--color-surface));
}
.recovery-input:focus {
outline: 2px solid var(--primary, #6366f1);
outline: 2px solid hsl(var(--color-primary));
outline-offset: 1px;
}
@ -898,8 +898,8 @@
@media (prefers-color-scheme: dark) {
.card {
background: var(--surface, #1f2937);
border-color: var(--border, #374151);
background: hsl(var(--color-surface));
border-color: hsl(var(--color-border));
}
.table-list li {
background: var(--surface-muted, #111827);
@ -907,7 +907,7 @@
.recovery-code,
.recovery-input {
background: var(--surface-muted, #111827);
border-color: var(--border, #374151);
border-color: hsl(var(--color-border));
}
}
</style>

View file

@ -37,15 +37,19 @@
<!-- Today's Summary -->
<div class="flex gap-4">
<div class="flex-1 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">{$_('common.total')}</p>
<p class="duration-display text-2xl font-bold text-[hsl(var(--foreground))]">
<div
class="flex-1 rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">{$_('common.total')}</p>
<p class="duration-display text-2xl font-bold text-[hsl(var(--color-foreground))]">
{formatDurationCompact(todayTotal)}
</p>
</div>
<div class="flex-1 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">{$_('entry.billable')}</p>
<p class="duration-display text-2xl font-bold text-[hsl(var(--primary))]">
<div
class="flex-1 rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">{$_('entry.billable')}</p>
<p class="duration-display text-2xl font-bold text-[hsl(var(--color-primary))]">
{formatDurationCompact(todayBillable)}
</p>
</div>
@ -57,12 +61,12 @@
<!-- Today's Entries -->
<div>
<div class="mb-3 flex items-center justify-between">
<h2 class="text-sm font-medium text-[hsl(var(--muted-foreground))]">
<h2 class="text-sm font-medium text-[hsl(var(--color-muted-foreground))]">
{$_('entry.today')} ({formatDurationCompact(todayTotal)})
</h2>
<button
onclick={() => (showEntryForm = true)}
class="rounded-lg border border-[hsl(var(--border))] px-3 py-1.5 text-xs text-[hsl(var(--muted-foreground))] transition-colors hover:border-[hsl(var(--primary)/0.5)] hover:text-[hsl(var(--foreground))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 py-1.5 text-xs text-[hsl(var(--color-muted-foreground))] transition-colors hover:border-[hsl(var(--color-primary)/0.5)] hover:text-[hsl(var(--color-foreground))]"
>
+ {$_('entry.manual')}
</button>

View file

@ -107,10 +107,10 @@
<div class="space-y-6">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">{$_('nav.clients')}</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{$_('nav.clients')}</h1>
<button
onclick={() => (showCreateForm = !showCreateForm)}
class="rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
+ {$_('client.create')}
</button>
@ -122,20 +122,20 @@
e.preventDefault();
handleCreate();
}}
class="rounded-xl border border-[hsl(var(--primary)/0.3)] bg-[hsl(var(--card))] p-4 space-y-3"
class="rounded-xl border border-[hsl(var(--color-primary)/0.3)] bg-[hsl(var(--color-card))] p-4 space-y-3"
>
<div class="flex gap-2">
<input
type="text"
bind:value={newName}
placeholder={$_('client.name')}
class="flex-1 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2.5 text-sm text-[hsl(var(--foreground))] focus:border-[hsl(var(--primary))] focus:outline-none"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2.5 text-sm text-[hsl(var(--color-foreground))] focus:border-[hsl(var(--color-primary))] focus:outline-none"
/>
<input
type="text"
bind:value={newShortCode}
placeholder={$_('client.shortCode')}
class="w-24 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2.5 text-sm text-[hsl(var(--foreground))]"
class="w-24 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2.5 text-sm text-[hsl(var(--color-foreground))]"
/>
</div>
<div class="flex gap-2">
@ -143,7 +143,7 @@
type="email"
bind:value={newEmail}
placeholder={$_('client.email')}
class="flex-1 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2.5 text-sm text-[hsl(var(--foreground))]"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2.5 text-sm text-[hsl(var(--color-foreground))]"
/>
<div class="flex items-center gap-1">
<input
@ -152,9 +152,9 @@
min="0"
step="5"
placeholder="0"
class="w-20 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2.5 text-sm text-center text-[hsl(var(--foreground))]"
class="w-20 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2.5 text-sm text-center text-[hsl(var(--color-foreground))]"
/>
<span class="text-xs text-[hsl(var(--muted-foreground))]">/h</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">/h</span>
</div>
</div>
<div class="flex flex-wrap gap-2">
@ -174,12 +174,12 @@
<button
type="button"
onclick={() => (showCreateForm = false)}
class="flex-1 rounded-lg border border-[hsl(var(--border))] py-2 text-sm text-[hsl(var(--muted-foreground))]"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] py-2 text-sm text-[hsl(var(--color-muted-foreground))]"
>{$_('common.cancel')}</button
>
<button
type="submit"
class="flex-1 rounded-lg bg-[hsl(var(--primary))] py-2 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="flex-1 rounded-lg bg-[hsl(var(--color-primary))] py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>{$_('common.create')}</button
>
</div>
@ -188,7 +188,7 @@
{#if activeClients.length === 0 && !showCreateForm}
<div
class="rounded-xl border border-dashed border-[hsl(var(--border))] p-8 text-center text-[hsl(var(--muted-foreground))]"
class="rounded-xl border border-dashed border-[hsl(var(--color-border))] p-8 text-center text-[hsl(var(--color-muted-foreground))]"
>
<p>{$_('client.noClients')}</p>
</div>
@ -198,7 +198,7 @@
{@const projects = getClientProjects(client.id)}
{@const hours = getClientHours(client.id)}
<div
class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] overflow-hidden"
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] overflow-hidden"
>
{#if editingClientId === client.id}
<div class="p-4 space-y-3">
@ -210,7 +210,7 @@
editName = (e.target as HTMLInputElement).value;
autoSave({ name: editName });
}}
class="flex-1 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm"
/>
<input
type="text"
@ -220,7 +220,7 @@
autoSave({ shortCode: editShortCode || null });
}}
placeholder={$_('client.shortCode')}
class="w-24 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm"
class="w-24 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm"
/>
</div>
<div class="flex gap-2">
@ -232,7 +232,7 @@
autoSave({ email: editEmail || null });
}}
placeholder={$_('client.email')}
class="flex-1 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm"
/>
<div class="flex items-center gap-1">
<input
@ -247,9 +247,9 @@
editRate > 0 ? { amount: editRate, currency: 'EUR', per: 'hour' } : null,
});
}}
class="w-20 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-center"
class="w-20 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-center"
/>
<span class="text-xs text-[hsl(var(--muted-foreground))]">/h</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">/h</span>
</div>
</div>
<div class="flex flex-wrap gap-1.5">
@ -271,14 +271,15 @@
<div class="flex justify-end gap-2">
<button
onclick={() => handleArchive(client.id, true)}
class="text-xs text-[hsl(var(--muted-foreground))]">{$_('common.archive')}</button
class="text-xs text-[hsl(var(--color-muted-foreground))]"
>{$_('common.archive')}</button
>
<button onclick={() => handleDelete(client.id)} class="text-xs text-red-500"
>{$_('common.delete')}</button
>
<button
onclick={() => (editingClientId = null)}
class="text-xs text-[hsl(var(--primary))]">{$_('common.close')}</button
class="text-xs text-[hsl(var(--color-primary))]">{$_('common.close')}</button
>
</div>
</div>
@ -294,8 +295,8 @@
{client.shortCode || client.name.charAt(0).toUpperCase()}
</div>
<div class="min-w-0 flex-1">
<p class="font-medium text-[hsl(var(--foreground))]">{client.name}</p>
<p class="text-xs text-[hsl(var(--muted-foreground))]">
<p class="font-medium text-[hsl(var(--color-foreground))]">{client.name}</p>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
{projects.length}
{$_('nav.projects')}
{#if client.billingRate}
@ -304,7 +305,9 @@
{/if}
</p>
</div>
<span class="duration-display text-sm font-medium text-[hsl(var(--foreground))]">
<span
class="duration-display text-sm font-medium text-[hsl(var(--color-foreground))]"
>
{formatDurationCompact(hours)}
</span>
</button>
@ -318,7 +321,7 @@
<div>
<button
onclick={() => (showArchived = !showArchived)}
class="flex items-center gap-2 text-sm text-[hsl(var(--muted-foreground))]"
class="flex items-center gap-2 text-sm text-[hsl(var(--color-muted-foreground))]"
>
<CaretRight size={20} class="transition-transform {showArchived ? 'rotate-90' : ''}" />
{$_('project.archived')} ({archivedClients.length})
@ -327,7 +330,7 @@
<div class="mt-3 space-y-2">
{#each archivedClients as client}
<div
class="flex items-center justify-between rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 opacity-60"
class="flex items-center justify-between rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] px-4 py-3 opacity-60"
>
<div class="flex items-center gap-2">
<div
@ -340,7 +343,7 @@
</div>
<button
onclick={() => handleArchive(client.id, false)}
class="text-xs text-[hsl(var(--primary))]">{$_('common.unarchive')}</button
class="text-xs text-[hsl(var(--color-primary))]">{$_('common.unarchive')}</button
>
</div>
{/each}

View file

@ -47,8 +47,10 @@
{#if !client}
<div class="flex flex-col items-center justify-center py-20">
<p class="text-[hsl(var(--muted-foreground))]">Kunde nicht gefunden.</p>
<a href="/times/clients" class="mt-4 text-sm text-[hsl(var(--primary))]">{$_('common.back')}</a>
<p class="text-[hsl(var(--color-muted-foreground))]">Kunde nicht gefunden.</p>
<a href="/times/clients" class="mt-4 text-sm text-[hsl(var(--color-primary))]"
>{$_('common.back')}</a
>
</div>
{:else}
<div class="space-y-6">
@ -56,7 +58,7 @@
<div>
<a
href="/times/clients"
class="mb-3 inline-flex items-center gap-1 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="mb-3 inline-flex items-center gap-1 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<CaretLeft size={16} />
{$_('nav.clients')}
@ -70,8 +72,8 @@
{client.shortCode || client.name.charAt(0).toUpperCase()}
</div>
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">{client.name}</h1>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{client.name}</h1>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{#if client.shortCode}{client.shortCode} ·
{/if}
{#if client.email}{client.email} ·
@ -86,26 +88,38 @@
<!-- Stats -->
<div class="grid grid-cols-2 gap-3 sm:grid-cols-4">
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">{$_('report.totalHours')}</p>
<p class="duration-display mt-1 text-xl font-bold text-[hsl(var(--foreground))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">{$_('report.totalHours')}</p>
<p class="duration-display mt-1 text-xl font-bold text-[hsl(var(--color-foreground))]">
{formatDurationDecimal(totalDuration)}h
</p>
</div>
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">{$_('report.billableHours')}</p>
<p class="duration-display mt-1 text-xl font-bold text-[hsl(var(--primary))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
{$_('report.billableHours')}
</p>
<p class="duration-display mt-1 text-xl font-bold text-[hsl(var(--color-primary))]">
{formatDurationDecimal(billableDuration)}h
</p>
</div>
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">{$_('nav.projects')}</p>
<p class="mt-1 text-xl font-bold text-[hsl(var(--foreground))]">{clientProjects.length}</p>
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">{$_('nav.projects')}</p>
<p class="mt-1 text-xl font-bold text-[hsl(var(--color-foreground))]">
{clientProjects.length}
</p>
</div>
{#if billingValue() !== null}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">Wert</p>
<p class="mt-1 text-xl font-bold text-[hsl(var(--primary))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">Wert</p>
<p class="mt-1 text-xl font-bold text-[hsl(var(--color-primary))]">
{billingValue()!.toFixed(0)}
{client.billingRate!.currency}
</p>
@ -116,7 +130,7 @@
<!-- Projects -->
{#if clientProjects.length > 0}
<div>
<h2 class="mb-3 text-sm font-medium text-[hsl(var(--muted-foreground))]">
<h2 class="mb-3 text-sm font-medium text-[hsl(var(--color-muted-foreground))]">
{$_('nav.projects')}
</h2>
<div class="grid gap-2 sm:grid-cols-2 lg:grid-cols-3">
@ -124,16 +138,20 @@
{@const hours = getProjectHours(proj.id)}
<a
href="/times/projects/{proj.id}"
class="entry-item flex items-center gap-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4"
class="entry-item flex items-center gap-3 rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<div class="h-3 w-3 rounded-full" style="background-color: {proj.color}"></div>
<div class="min-w-0 flex-1">
<p class="text-sm font-medium text-[hsl(var(--foreground))]">{proj.name}</p>
<p class="text-sm font-medium text-[hsl(var(--color-foreground))]">{proj.name}</p>
{#if proj.isBillable}
<span class="text-xs text-[hsl(var(--primary))]">{$_('project.billable')}</span>
<span class="text-xs text-[hsl(var(--color-primary))]"
>{$_('project.billable')}</span
>
{/if}
</div>
<span class="duration-display text-sm font-medium text-[hsl(var(--foreground))]">
<span
class="duration-display text-sm font-medium text-[hsl(var(--color-foreground))]"
>
{formatDurationCompact(hours)}
</span>
</a>
@ -144,7 +162,7 @@
<!-- Entries -->
<div>
<h2 class="mb-3 text-sm font-medium text-[hsl(var(--muted-foreground))]">
<h2 class="mb-3 text-sm font-medium text-[hsl(var(--color-muted-foreground))]">
{$_('nav.entries')} ({formatDurationCompact(totalDuration)})
</h2>
<EntryList entries={clientEntries} />

View file

@ -62,10 +62,10 @@
<div class="space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">{$_('nav.entries')}</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{$_('nav.entries')}</h1>
<button
onclick={() => (showEntryForm = true)}
class="rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))] transition-colors hover:opacity-90"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))] transition-colors hover:opacity-90"
>
+ {$_('entry.manual')}
</button>
@ -77,8 +77,8 @@
<button
onclick={() => (dateFilter = period as any)}
class="rounded-lg px-3 py-1.5 text-sm transition-colors {dateFilter === period
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
: 'text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent)/0.1)]'}"
? 'bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))]'
: 'text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-accent)/0.1)]'}"
>
{period === 'week'
? $_('entry.thisWeek')
@ -90,15 +90,15 @@
<!-- Totals -->
<div class="ml-auto flex items-center gap-4 text-sm">
<span class="text-[hsl(var(--muted-foreground))]">
<span class="text-[hsl(var(--color-muted-foreground))]">
{$_('common.total')}:
<span class="duration-display font-medium text-[hsl(var(--foreground))]"
<span class="duration-display font-medium text-[hsl(var(--color-foreground))]"
>{formatDurationCompact(totalDuration)}</span
>
</span>
<span class="text-[hsl(var(--muted-foreground))]">
<span class="text-[hsl(var(--color-muted-foreground))]">
{$_('entry.billable')}:
<span class="duration-display font-medium text-[hsl(var(--primary))]"
<span class="duration-display font-medium text-[hsl(var(--color-primary))]"
>{formatDurationCompact(billableDuration)}</span
>
</span>

View file

@ -116,10 +116,10 @@
<div class="space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">{$_('nav.projects')}</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{$_('nav.projects')}</h1>
<button
onclick={() => (showCreateForm = !showCreateForm)}
class="rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
+ {$_('project.create')}
</button>
@ -132,18 +132,18 @@
e.preventDefault();
handleCreate();
}}
class="rounded-xl border border-[hsl(var(--primary)/0.3)] bg-[hsl(var(--card))] p-4 space-y-3"
class="rounded-xl border border-[hsl(var(--color-primary)/0.3)] bg-[hsl(var(--color-card))] p-4 space-y-3"
>
<input
type="text"
bind:value={newName}
placeholder={$_('project.name')}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2.5 text-sm text-[hsl(var(--foreground))] focus:border-[hsl(var(--primary))] focus:outline-none"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2.5 text-sm text-[hsl(var(--color-foreground))] focus:border-[hsl(var(--color-primary))] focus:outline-none"
/>
<div class="flex gap-2">
<select
bind:value={newClientId}
class="flex-1 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))]"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))]"
>
<option value="">{$_('project.internal')}</option>
{#each allClients.value.filter((c) => !c.isArchived) as client}
@ -151,12 +151,12 @@
{/each}
</select>
<label
class="flex items-center gap-2 rounded-lg border border-[hsl(var(--border))] px-3 py-2 text-sm"
class="flex items-center gap-2 rounded-lg border border-[hsl(var(--color-border))] px-3 py-2 text-sm"
>
<input
type="checkbox"
bind:checked={newIsBillable}
class="accent-[hsl(var(--primary))]"
class="accent-[hsl(var(--color-primary))]"
/>
{$_('project.billable')}
</label>
@ -179,12 +179,12 @@
<button
type="button"
onclick={() => (showCreateForm = false)}
class="flex-1 rounded-lg border border-[hsl(var(--border))] py-2 text-sm text-[hsl(var(--muted-foreground))]"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] py-2 text-sm text-[hsl(var(--color-muted-foreground))]"
>{$_('common.cancel')}</button
>
<button
type="submit"
class="flex-1 rounded-lg bg-[hsl(var(--primary))] py-2 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="flex-1 rounded-lg bg-[hsl(var(--color-primary))] py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>{$_('common.create')}</button
>
</div>
@ -194,7 +194,7 @@
<!-- Active Projects -->
{#if activeProjects.length === 0 && !showCreateForm}
<div
class="rounded-xl border border-dashed border-[hsl(var(--border))] p-8 text-center text-[hsl(var(--muted-foreground))]"
class="rounded-xl border border-dashed border-[hsl(var(--color-border))] p-8 text-center text-[hsl(var(--color-muted-foreground))]"
>
<p>{$_('project.noProjects')}</p>
</div>
@ -207,7 +207,7 @@
{@const hours = getProjectHours(project.id)}
{@const budgetPct = getBudgetPercent(project)}
<div
class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] overflow-hidden"
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] overflow-hidden"
>
<!-- Color bar -->
<div class="h-1" style="background-color: {project.color}"></div>
@ -222,7 +222,7 @@
editName = (e.target as HTMLInputElement).value;
autoSaveProject({ name: editName });
}}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none"
/>
<select
value={editClientId}
@ -230,7 +230,7 @@
editClientId = (e.target as HTMLSelectElement).value;
autoSaveProject({ clientId: editClientId || null });
}}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm"
>
<option value="">{$_('project.internal')}</option>
{#each allClients.value.filter((c) => !c.isArchived) as c}
@ -262,14 +262,14 @@
editIsBillable = !editIsBillable;
autoSaveProject({ isBillable: editIsBillable });
}}
class="accent-[hsl(var(--primary))]"
class="accent-[hsl(var(--color-primary))]"
/>
{$_('project.billable')}
</label>
<div class="flex gap-2">
<button
onclick={() => handleArchive(project.id, true)}
class="text-xs text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="text-xs text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>{$_('common.archive')}</button
>
<button onclick={() => handleDelete(project.id)} class="text-xs text-red-500"
@ -277,7 +277,7 @@
>
<button
onclick={() => (editingProjectId = null)}
class="text-xs text-[hsl(var(--primary))]">{$_('common.close')}</button
class="text-xs text-[hsl(var(--color-primary))]">{$_('common.close')}</button
>
</div>
</div>
@ -287,33 +287,35 @@
<button class="w-full p-4 text-left" onclick={() => startEditing(project)}>
<div class="flex items-start justify-between">
<div>
<p class="font-medium text-[hsl(var(--foreground))]">{project.name}</p>
<p class="text-xs text-[hsl(var(--muted-foreground))]">
<p class="font-medium text-[hsl(var(--color-foreground))]">{project.name}</p>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
{client?.name || $_('project.internal')}
{#if project.isBillable}
· {$_('project.billable')}
{/if}
</p>
</div>
<span class="duration-display text-sm font-medium text-[hsl(var(--foreground))]">
<span
class="duration-display text-sm font-medium text-[hsl(var(--color-foreground))]"
>
{formatDurationCompact(hours)}
</span>
</div>
{#if budgetPct !== null}
<div class="mt-3">
<div
class="flex items-center justify-between text-xs text-[hsl(var(--muted-foreground))]"
class="flex items-center justify-between text-xs text-[hsl(var(--color-muted-foreground))]"
>
<span>{$_('project.budget')}</span>
<span>{budgetPct}%</span>
</div>
<div class="mt-1 h-1.5 rounded-full bg-[hsl(var(--muted))]">
<div class="mt-1 h-1.5 rounded-full bg-[hsl(var(--color-muted))]">
<div
class="h-full rounded-full transition-all {budgetPct > 90
? 'bg-red-500'
: budgetPct > 75
? 'bg-amber-500'
: 'bg-[hsl(var(--primary))]'}"
: 'bg-[hsl(var(--color-primary))]'}"
style="width: {budgetPct}%"
></div>
</div>
@ -331,7 +333,7 @@
<div>
<button
onclick={() => (showArchived = !showArchived)}
class="flex items-center gap-2 text-sm text-[hsl(var(--muted-foreground))]"
class="flex items-center gap-2 text-sm text-[hsl(var(--color-muted-foreground))]"
>
<CaretRight size={20} class="transition-transform {showArchived ? 'rotate-90' : ''}" />
{$_('project.archived')} ({archivedProjects.length})
@ -341,15 +343,15 @@
<div class="mt-3 space-y-2">
{#each archivedProjects as project}
<div
class="flex items-center justify-between rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3 opacity-60"
class="flex items-center justify-between rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] px-4 py-3 opacity-60"
>
<div class="flex items-center gap-2">
<div class="h-3 w-3 rounded-full" style="background-color: {project.color}"></div>
<span class="text-sm text-[hsl(var(--foreground))]">{project.name}</span>
<span class="text-sm text-[hsl(var(--color-foreground))]">{project.name}</span>
</div>
<button
onclick={() => handleArchive(project.id, false)}
class="text-xs text-[hsl(var(--primary))]">{$_('common.unarchive')}</button
class="text-xs text-[hsl(var(--color-primary))]">{$_('common.unarchive')}</button
>
</div>
{/each}

View file

@ -87,8 +87,9 @@
{#if !project}
<div class="flex flex-col items-center justify-center py-20">
<p class="text-[hsl(var(--muted-foreground))]">Projekt nicht gefunden.</p>
<a href="/times/projects" class="mt-4 text-sm text-[hsl(var(--primary))]">{$_('common.back')}</a
<p class="text-[hsl(var(--color-muted-foreground))]">Projekt nicht gefunden.</p>
<a href="/times/projects" class="mt-4 text-sm text-[hsl(var(--color-primary))]"
>{$_('common.back')}</a
>
</div>
{:else}
@ -97,7 +98,7 @@
<div>
<a
href="/times/projects"
class="mb-3 inline-flex items-center gap-1 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="mb-3 inline-flex items-center gap-1 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
<CaretLeft size={16} />
{$_('nav.projects')}
@ -107,19 +108,19 @@
<div class="flex items-center gap-3">
<div class="h-4 w-4 rounded-full" style="background-color: {project.color}"></div>
<div>
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">{project.name}</h1>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{project.name}</h1>
<p class="text-sm text-[hsl(var(--color-muted-foreground))]">
{client?.name || $_('project.internal')}
{#if project.isBillable}
<span
class="ml-2 rounded bg-[hsl(var(--primary)/0.1)] px-1.5 py-0.5 text-xs text-[hsl(var(--primary))]"
class="ml-2 rounded bg-[hsl(var(--color-primary)/0.1)] px-1.5 py-0.5 text-xs text-[hsl(var(--color-primary))]"
>
{$_('project.billable')}
</span>
{/if}
{#if project.isArchived}
<span
class="ml-2 rounded bg-[hsl(var(--muted))] px-1.5 py-0.5 text-xs text-[hsl(var(--muted-foreground))]"
class="ml-2 rounded bg-[hsl(var(--color-muted))] px-1.5 py-0.5 text-xs text-[hsl(var(--color-muted-foreground))]"
>
{$_('project.archived')}
</span>
@ -130,13 +131,13 @@
<div class="flex gap-2">
<button
onclick={() => (isEditing ? (isEditing = false) : startEditing())}
class="rounded-lg border border-[hsl(var(--border))] px-3 py-1.5 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 py-1.5 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
{isEditing ? $_('common.close') : $_('common.edit')}
</button>
<button
onclick={handleArchive}
class="rounded-lg border border-[hsl(var(--border))] px-3 py-1.5 text-sm text-[hsl(var(--muted-foreground))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 py-1.5 text-sm text-[hsl(var(--color-muted-foreground))]"
>
{project.isArchived ? $_('common.unarchive') : $_('common.archive')}
</button>
@ -147,7 +148,7 @@
<!-- Edit Form -->
{#if isEditing}
<div
class="rounded-xl border border-[hsl(var(--primary)/0.3)] bg-[hsl(var(--card))] p-4 space-y-3"
class="rounded-xl border border-[hsl(var(--color-primary)/0.3)] bg-[hsl(var(--color-card))] p-4 space-y-3"
>
<input
type="text"
@ -157,7 +158,7 @@
save({ name: editName });
}}
placeholder={$_('project.name')}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))] focus:outline-none"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))] focus:outline-none"
/>
<input
type="text"
@ -167,7 +168,7 @@
save({ description: editDescription || null });
}}
placeholder={$_('project.description')}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))]"
/>
<div class="flex gap-2">
<select
@ -176,7 +177,7 @@
editClientId = (e.target as HTMLSelectElement).value;
save({ clientId: editClientId || null });
}}
class="flex-1 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm"
>
<option value="">{$_('project.internal')}</option>
{#each allClients.value.filter((c) => !c.isArchived) as c}
@ -184,7 +185,7 @@
{/each}
</select>
<label
class="flex items-center gap-2 rounded-lg border border-[hsl(var(--border))] px-3 py-2 text-sm"
class="flex items-center gap-2 rounded-lg border border-[hsl(var(--color-border))] px-3 py-2 text-sm"
>
<input
type="checkbox"
@ -193,14 +194,16 @@
editIsBillable = !editIsBillable;
save({ isBillable: editIsBillable });
}}
class="accent-[hsl(var(--primary))]"
class="accent-[hsl(var(--color-primary))]"
/>
{$_('project.billable')}
</label>
</div>
<div class="flex gap-2">
<div class="flex items-center gap-1">
<label for="times-project-budget" class="text-xs text-[hsl(var(--muted-foreground))]"
<label
for="times-project-budget"
class="text-xs text-[hsl(var(--color-muted-foreground))]"
>{$_('project.budget')} (h):</label
>
<input
@ -214,12 +217,13 @@
budget: editBudgetHours > 0 ? { type: 'hours', amount: editBudgetHours } : null,
});
}}
class="w-20 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-center text-sm"
class="w-20 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-center text-sm"
/>
</div>
<div class="flex items-center gap-1">
<label for="times-project-rate" class="text-xs text-[hsl(var(--muted-foreground))]"
>Rate:</label
<label
for="times-project-rate"
class="text-xs text-[hsl(var(--color-muted-foreground))]">Rate:</label
>
<input
id="times-project-rate"
@ -236,9 +240,9 @@
: null,
});
}}
class="w-20 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-center text-sm"
class="w-20 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-center text-sm"
/>
<span class="text-xs text-[hsl(var(--muted-foreground))]">/h</span>
<span class="text-xs text-[hsl(var(--color-muted-foreground))]">/h</span>
</div>
</div>
<div class="flex flex-wrap gap-1.5">
@ -262,51 +266,65 @@
<!-- Stats -->
<div class="grid grid-cols-2 gap-3 sm:grid-cols-4">
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">{$_('report.totalHours')}</p>
<p class="duration-display mt-1 text-xl font-bold text-[hsl(var(--foreground))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">{$_('report.totalHours')}</p>
<p class="duration-display mt-1 text-xl font-bold text-[hsl(var(--color-foreground))]">
{formatDurationDecimal(totalDuration)}h
</p>
</div>
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">{$_('report.billableHours')}</p>
<p class="duration-display mt-1 text-xl font-bold text-[hsl(var(--primary))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
{$_('report.billableHours')}
</p>
<p class="duration-display mt-1 text-xl font-bold text-[hsl(var(--color-primary))]">
{formatDurationDecimal(billableDuration)}h
</p>
</div>
{#if budgetHoursTotal}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">{$_('project.budget')}</p>
<p class="duration-display mt-1 text-xl font-bold text-[hsl(var(--foreground))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">{$_('project.budget')}</p>
<p class="duration-display mt-1 text-xl font-bold text-[hsl(var(--color-foreground))]">
{budgetHoursUsed.toFixed(1)} / {budgetHoursTotal}h
</p>
</div>
{/if}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">{$_('nav.entries')}</p>
<p class="mt-1 text-xl font-bold text-[hsl(var(--foreground))]">{projectEntries.length}</p>
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">{$_('nav.entries')}</p>
<p class="mt-1 text-xl font-bold text-[hsl(var(--color-foreground))]">
{projectEntries.length}
</p>
</div>
</div>
<!-- Budget Progress -->
{#if budgetPercent() !== null}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<div class="flex items-center justify-between text-sm">
<span class="text-[hsl(var(--muted-foreground))]">{$_('project.budget')}</span>
<span class="font-medium text-[hsl(var(--foreground))]">{budgetPercent()}%</span>
<span class="text-[hsl(var(--color-muted-foreground))]">{$_('project.budget')}</span>
<span class="font-medium text-[hsl(var(--color-foreground))]">{budgetPercent()}%</span>
</div>
<div class="mt-2 h-2.5 rounded-full bg-[hsl(var(--muted))]">
<div class="mt-2 h-2.5 rounded-full bg-[hsl(var(--color-muted))]">
<div
class="h-full rounded-full transition-all {budgetPercent()! > 90
? 'bg-red-500'
: budgetPercent()! > 75
? 'bg-amber-500'
: 'bg-[hsl(var(--primary))]'}"
: 'bg-[hsl(var(--color-primary))]'}"
style="width: {budgetPercent()}%"
></div>
</div>
{#if project.billingRate}
<p class="mt-2 text-xs text-[hsl(var(--muted-foreground))]">
<p class="mt-2 text-xs text-[hsl(var(--color-muted-foreground))]">
{project.billingRate.amount}
{project.billingRate.currency}/h · Wert: {(
(billableDuration / 3600) *
@ -320,7 +338,7 @@
<!-- Entries -->
<div>
<h2 class="mb-3 text-sm font-medium text-[hsl(var(--muted-foreground))]">
<h2 class="mb-3 text-sm font-medium text-[hsl(var(--color-muted-foreground))]">
{$_('nav.entries')} ({formatDurationCompact(totalDuration)})
</h2>
<EntryList entries={projectEntries} />

View file

@ -85,15 +85,15 @@
<div class="space-y-6">
<!-- Header -->
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">{$_('nav.reports')}</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{$_('nav.reports')}</h1>
<div class="flex items-center gap-2">
<div class="flex gap-1">
{#each ['week', 'month'] as p}
<button
onclick={() => (period = p as any)}
class="rounded-lg px-3 py-1.5 text-sm transition-colors {period === p
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
: 'text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent)/0.1)]'}"
? 'bg-[hsl(var(--color-primary))] text-[hsl(var(--color-primary-foreground))]'
: 'text-[hsl(var(--color-muted-foreground))] hover:bg-[hsl(var(--color-accent)/0.1)]'}"
>
{p === 'week' ? $_('entry.thisWeek') : $_('entry.thisMonth')}
</button>
@ -101,7 +101,7 @@
</div>
<button
onclick={() => exportEntriesToCSV(entries(), allProjects.value, allClients.value)}
class="rounded-lg border border-[hsl(var(--border))] px-3 py-1.5 text-sm text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]"
class="rounded-lg border border-[hsl(var(--color-border))] px-3 py-1.5 text-sm text-[hsl(var(--color-muted-foreground))] hover:text-[hsl(var(--color-foreground))]"
>
CSV Export
</button>
@ -110,47 +110,57 @@
<!-- Stats Grid -->
<div class="grid grid-cols-2 gap-3 sm:grid-cols-4">
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">{$_('report.totalHours')}</p>
<p class="duration-display mt-1 text-2xl font-bold text-[hsl(var(--foreground))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">{$_('report.totalHours')}</p>
<p class="duration-display mt-1 text-2xl font-bold text-[hsl(var(--color-foreground))]">
{formatDurationDecimal(totalDuration)}h
</p>
</div>
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">{$_('report.billableHours')}</p>
<p class="duration-display mt-1 text-2xl font-bold text-[hsl(var(--primary))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">{$_('report.billableHours')}</p>
<p class="duration-display mt-1 text-2xl font-bold text-[hsl(var(--color-primary))]">
{formatDurationDecimal(billableDuration)}h
</p>
</div>
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">{$_('report.avgPerDay')}</p>
<p class="duration-display mt-1 text-2xl font-bold text-[hsl(var(--foreground))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">{$_('report.avgPerDay')}</p>
<p class="duration-display mt-1 text-2xl font-bold text-[hsl(var(--color-foreground))]">
{formatDurationCompact(Math.round(avgPerDay))}
</p>
</div>
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<p class="text-xs text-[hsl(var(--muted-foreground))]">{$_('nav.entries')}</p>
<p class="mt-1 text-2xl font-bold text-[hsl(var(--foreground))]">{entryCount}</p>
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">{$_('nav.entries')}</p>
<p class="mt-1 text-2xl font-bold text-[hsl(var(--color-foreground))]">{entryCount}</p>
</div>
</div>
<!-- Billable Breakdown -->
{#if totalDuration > 0}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<h3 class="mb-3 text-sm font-medium text-[hsl(var(--foreground))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<h3 class="mb-3 text-sm font-medium text-[hsl(var(--color-foreground))]">
{$_('entry.billable')} vs. {$_('entry.notBillable')}
</h3>
<div class="flex h-4 overflow-hidden rounded-full">
<div
class="bg-[hsl(var(--primary))] transition-all"
class="bg-[hsl(var(--color-primary))] transition-all"
style="width: {(billableDuration / totalDuration) * 100}%"
></div>
<div
class="bg-[hsl(var(--muted))] transition-all"
class="bg-[hsl(var(--color-muted))] transition-all"
style="width: {(nonBillableDuration / totalDuration) * 100}%"
></div>
</div>
<div class="mt-2 flex justify-between text-xs text-[hsl(var(--muted-foreground))]">
<div class="mt-2 flex justify-between text-xs text-[hsl(var(--color-muted-foreground))]">
<span>{$_('entry.billable')}: {formatDurationCompact(billableDuration)}</span>
<span>{$_('entry.notBillable')}: {formatDurationCompact(nonBillableDuration)}</span>
</div>
@ -159,8 +169,10 @@
<!-- Hours by Project -->
{#if projectBreakdown().length > 0}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<h3 class="mb-3 text-sm font-medium text-[hsl(var(--foreground))]">
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<h3 class="mb-3 text-sm font-medium text-[hsl(var(--color-foreground))]">
{$_('report.byProject')}
</h3>
<div class="space-y-3">
@ -169,13 +181,13 @@
<div class="flex items-center justify-between text-sm">
<div class="flex items-center gap-2">
<div class="h-3 w-3 rounded-full" style="background-color: {item.color}"></div>
<span class="text-[hsl(var(--foreground))]">{item.name}</span>
<span class="text-[hsl(var(--color-foreground))]">{item.name}</span>
</div>
<span class="duration-display text-[hsl(var(--muted-foreground))]">
<span class="duration-display text-[hsl(var(--color-muted-foreground))]">
{formatDurationCompact(item.duration)}
</span>
</div>
<div class="mt-1 h-2 rounded-full bg-[hsl(var(--muted))]">
<div class="mt-1 h-2 rounded-full bg-[hsl(var(--color-muted))]">
<div
class="h-full rounded-full transition-all"
style="width: {(item.duration / maxProjectDuration) *
@ -190,21 +202,25 @@
<!-- Daily Hours -->
{#if period === 'week' && dailyBreakdown().length > 0}
<div class="rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4">
<h3 class="mb-3 text-sm font-medium text-[hsl(var(--foreground))]">{$_('report.byDay')}</h3>
<div
class="rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] p-4"
>
<h3 class="mb-3 text-sm font-medium text-[hsl(var(--color-foreground))]">
{$_('report.byDay')}
</h3>
<div class="flex items-end gap-2" style="height: 120px;">
{#each dailyBreakdown() as day}
<div class="flex flex-1 flex-col items-center gap-1">
<div class="w-full flex flex-col justify-end" style="height: 100px;">
<div
class="w-full rounded-t bg-[hsl(var(--primary))] transition-all"
class="w-full rounded-t bg-[hsl(var(--color-primary))] transition-all"
style="height: {maxDailyDuration > 0
? (day.duration / maxDailyDuration) * 100
: 0}%"
title={formatDurationCompact(day.duration)}
></div>
</div>
<span class="text-[10px] text-[hsl(var(--muted-foreground))]">{day.label}</span>
<span class="text-[10px] text-[hsl(var(--color-muted-foreground))]">{day.label}</span>
</div>
{/each}
</div>

View file

@ -70,10 +70,10 @@
<div class="space-y-6">
<div class="flex items-center justify-between">
<h1 class="text-2xl font-bold text-[hsl(var(--foreground))]">{$_('nav.templates')}</h1>
<h1 class="text-2xl font-bold text-[hsl(var(--color-foreground))]">{$_('nav.templates')}</h1>
<button
onclick={() => (showCreateForm = !showCreateForm)}
class="rounded-lg bg-[hsl(var(--primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="rounded-lg bg-[hsl(var(--color-primary))] px-4 py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>
+ {$_('template.create')}
</button>
@ -85,24 +85,24 @@
e.preventDefault();
handleCreate();
}}
class="rounded-xl border border-[hsl(var(--primary)/0.3)] bg-[hsl(var(--card))] p-4 space-y-3"
class="rounded-xl border border-[hsl(var(--color-primary)/0.3)] bg-[hsl(var(--color-card))] p-4 space-y-3"
>
<input
type="text"
bind:value={newName}
placeholder="Vorlagenname"
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2.5 text-sm text-[hsl(var(--foreground))] focus:border-[hsl(var(--primary))] focus:outline-none"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2.5 text-sm text-[hsl(var(--color-foreground))] focus:border-[hsl(var(--color-primary))] focus:outline-none"
/>
<input
type="text"
bind:value={newDescription}
placeholder={$_('entry.description')}
class="w-full rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-4 py-2.5 text-sm text-[hsl(var(--foreground))]"
class="w-full rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-4 py-2.5 text-sm text-[hsl(var(--color-foreground))]"
/>
<div class="flex gap-2">
<select
bind:value={newProjectId}
class="flex-1 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--input))] px-3 py-2 text-sm text-[hsl(var(--foreground))]"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] bg-[hsl(var(--color-input))] px-3 py-2 text-sm text-[hsl(var(--color-foreground))]"
>
<option value="">{$_('project.internal')}</option>
{#each allProjects.value.filter((p) => !p.isArchived) as proj}
@ -110,12 +110,12 @@
{/each}
</select>
<label
class="flex items-center gap-2 rounded-lg border border-[hsl(var(--border))] px-3 py-2 text-sm"
class="flex items-center gap-2 rounded-lg border border-[hsl(var(--color-border))] px-3 py-2 text-sm"
>
<input
type="checkbox"
bind:checked={newIsBillable}
class="accent-[hsl(var(--primary))]"
class="accent-[hsl(var(--color-primary))]"
/>
{$_('entry.billable')}
</label>
@ -124,12 +124,12 @@
<button
type="button"
onclick={() => (showCreateForm = false)}
class="flex-1 rounded-lg border border-[hsl(var(--border))] py-2 text-sm text-[hsl(var(--muted-foreground))]"
class="flex-1 rounded-lg border border-[hsl(var(--color-border))] py-2 text-sm text-[hsl(var(--color-muted-foreground))]"
>{$_('common.cancel')}</button
>
<button
type="submit"
class="flex-1 rounded-lg bg-[hsl(var(--primary))] py-2 text-sm font-medium text-[hsl(var(--primary-foreground))]"
class="flex-1 rounded-lg bg-[hsl(var(--color-primary))] py-2 text-sm font-medium text-[hsl(var(--color-primary-foreground))]"
>{$_('common.create')}</button
>
</div>
@ -138,7 +138,7 @@
{#if sortedTemplates.length === 0 && !showCreateForm}
<div
class="rounded-xl border border-dashed border-[hsl(var(--border))] p-8 text-center text-[hsl(var(--muted-foreground))]"
class="rounded-xl border border-dashed border-[hsl(var(--color-border))] p-8 text-center text-[hsl(var(--color-muted-foreground))]"
>
<p>{$_('template.noTemplates')}</p>
</div>
@ -149,7 +149,7 @@
? allProjects.value.find((p) => p.id === template.projectId)
: undefined}
<div
class="flex items-center gap-3 rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4 py-3"
class="flex items-center gap-3 rounded-xl border border-[hsl(var(--color-border))] bg-[hsl(var(--color-card))] px-4 py-3"
>
{#if project}
<div
@ -160,8 +160,8 @@
<div class="h-3 w-3 shrink-0 rounded-full bg-gray-400"></div>
{/if}
<div class="min-w-0 flex-1">
<p class="text-sm font-medium text-[hsl(var(--foreground))]">{template.name}</p>
<p class="text-xs text-[hsl(var(--muted-foreground))]">
<p class="text-sm font-medium text-[hsl(var(--color-foreground))]">{template.name}</p>
<p class="text-xs text-[hsl(var(--color-muted-foreground))]">
{template.description || $_('timer.noDescription')}
{#if project}
· {project.name}{/if}
@ -174,13 +174,13 @@
<button
onclick={() => useTemplate(template)}
disabled={timerStore.isRunning}
class="rounded-lg bg-[hsl(var(--primary))] px-3 py-1.5 text-xs font-medium text-[hsl(var(--primary-foreground))] disabled:opacity-50"
class="rounded-lg bg-[hsl(var(--color-primary))] px-3 py-1.5 text-xs font-medium text-[hsl(var(--color-primary-foreground))] disabled:opacity-50"
>
{$_('timer.start')}
</button>
<button
onclick={() => deleteTemplate(template.id)}
class="rounded-lg p-1.5 text-[hsl(var(--muted-foreground))] hover:text-red-500"
class="rounded-lg p-1.5 text-[hsl(var(--color-muted-foreground))] hover:text-red-500"
>
<X size={16} />
</button>