mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:01:08 +02:00
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:
parent
115afea519
commit
cd22e42afc
61 changed files with 1308 additions and 1120 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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'}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">✓</span>
|
||||
{:else}
|
||||
<span class="text-[hsl(var(--muted-foreground))]">✗</span>
|
||||
<span class="text-[hsl(var(--color-muted-foreground))]">✗</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'}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
>▲</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"
|
||||
>▼</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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<style>
|
||||
.apps-page-wrapper {
|
||||
background-color: hsl(var(--background));
|
||||
background-color: hsl(var(--color-background));
|
||||
min-height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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} />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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} />
|
||||
|
|
|
|||
|
|
@ -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 · {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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
>✎</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"
|
||||
>×</button
|
||||
>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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))]">×{item.quantity}</span
|
||||
<span class="text-sm text-[hsl(var(--color-muted-foreground))]"
|
||||
>×{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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
>×{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"
|
||||
>×</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>
|
||||
|
|
|
|||
|
|
@ -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))]"
|
||||
>✎</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"
|
||||
>×</button
|
||||
>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -45,6 +45,6 @@
|
|||
min-height: 100%;
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
background-color: hsl(var(--background));
|
||||
background-color: hsl(var(--color-background));
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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} />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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} />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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))]"
|
||||
>
|
||||
×
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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} />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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} />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue