Merge branch 'dev-1' into dev

This commit is contained in:
Wuesteon 2025-12-05 17:57:26 +01:00
commit d41d060bb3
1770 changed files with 168028 additions and 31031 deletions

View file

@ -53,6 +53,8 @@
size?: HeaderSize;
/** Whether to show bottom border */
bordered?: boolean;
/** Center the title (with actions on the right, back on the left) */
centered?: boolean;
/** Back navigation href (shows back arrow button) */
backHref?: string;
/** Icon snippet (before title) */
@ -72,6 +74,7 @@
description,
size = 'md',
bordered = false,
centered = false,
backHref,
icon,
breadcrumb,
@ -103,18 +106,23 @@
>
<!-- Breadcrumb -->
{#if breadcrumb}
<div class="page-header__breadcrumb mb-2 text-sm text-theme-secondary">
<div
class="page-header__breadcrumb mb-2 text-sm text-theme-secondary {centered
? 'text-center'
: ''}"
>
{@render breadcrumb()}
</div>
{/if}
<div class="flex items-center justify-between gap-4">
<div class="flex items-center gap-3 min-w-0">
<!-- Back Button -->
{#if centered}
<!-- Centered Layout -->
<div class="relative flex items-center justify-center min-h-[2.5rem]">
<!-- Back Button (left) -->
{#if backHref}
<a
href={backHref}
class="page-header__back flex-shrink-0 p-1.5 -ml-1.5 rounded-lg text-theme-secondary hover:text-theme hover:bg-theme-secondary/10 transition-colors"
class="absolute left-0 p-1.5 rounded-lg text-theme-secondary hover:text-theme hover:bg-theme-secondary/10 transition-colors"
aria-label="Zurück"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@ -128,16 +136,14 @@
</a>
{/if}
<!-- Icon -->
{#if icon}
<div class="page-header__icon flex-shrink-0 text-theme-secondary">
{@render icon()}
</div>
{/if}
<!-- Title & Description -->
<div class="min-w-0">
<h1 class="font-semibold text-theme {sizeClasses[size].title} truncate">
<!-- Centered Title & Description -->
<div class="text-center">
{#if icon}
<div class="page-header__icon inline-block text-theme-secondary mb-1">
{@render icon()}
</div>
{/if}
<h1 class="font-semibold text-theme {sizeClasses[size].title}">
{title}
</h1>
{#if description}
@ -146,15 +152,64 @@
</Text>
{/if}
</div>
</div>
<!-- Actions -->
{#if actions}
<div class="page-header__actions flex-shrink-0 flex items-center gap-2">
{@render actions()}
<!-- Actions (right) -->
{#if actions}
<div class="absolute right-0 flex items-center gap-2">
{@render actions()}
</div>
{/if}
</div>
{:else}
<!-- Default Layout -->
<div class="flex items-center justify-between gap-4">
<div class="flex items-center gap-3 min-w-0">
<!-- Back Button -->
{#if backHref}
<a
href={backHref}
class="page-header__back flex-shrink-0 p-1.5 -ml-1.5 rounded-lg text-theme-secondary hover:text-theme hover:bg-theme-secondary/10 transition-colors"
aria-label="Zurück"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 19l-7-7 7-7"
/>
</svg>
</a>
{/if}
<!-- Icon -->
{#if icon}
<div class="page-header__icon flex-shrink-0 text-theme-secondary">
{@render icon()}
</div>
{/if}
<!-- Title & Description -->
<div class="min-w-0">
<h1 class="font-semibold text-theme {sizeClasses[size].title} truncate">
{title}
</h1>
{#if description}
<Text variant="muted" class="mt-1">
{description}
</Text>
{/if}
</div>
</div>
{/if}
</div>
<!-- Actions -->
{#if actions}
<div class="page-header__actions flex-shrink-0 flex items-center gap-2">
{@render actions()}
</div>
{/if}
</div>
{/if}
<!-- Tabs/Navigation -->
{#if tabs}

View file

@ -259,6 +259,23 @@
// Icon SVG paths
const icons: Record<string, string> = {
// Clock app icons
bell: 'M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9',
clock: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z',
timer: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z',
stopwatch: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0zM9 3h6m-3-1v2',
activity: 'M13 10V3L4 14h7v7l9-11h-7z',
target:
'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm0-14c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm0 10c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z',
globe:
'M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9',
// Todo app icons
inbox:
'M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4',
check: 'M5 13l4 4L19 7',
checkCircle: 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z',
plus: 'M12 4v16m8-8H4',
// Original icons
mic: 'M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z',
calendar:
'M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z',

View file

@ -1,32 +1,54 @@
<script lang="ts">
import type { UserSettingsStore, NavPosition, ThemeMode } from '@manacore/shared-theme';
import type {
UserSettingsStore,
NavPosition,
ThemeMode,
WeekStartDay,
} from '@manacore/shared-theme';
import { getAvailableRoutes, getDefaultRoute } from '@manacore/shared-theme';
import SettingsSection from './SettingsSection.svelte';
import SettingsCard from './SettingsCard.svelte';
interface Props {
/** User settings store instance */
userSettings: UserSettingsStore;
/** App ID for start page selection */
appId?: string;
/** Whether to show navigation settings */
showNavigation?: boolean;
/** Whether to show theme settings */
showTheme?: boolean;
/** Whether to show language settings */
showLanguage?: boolean;
/** Whether to show general settings (start page, sounds, week start) */
showGeneral?: boolean;
/** Section title */
title?: string;
/** Section description */
description?: string;
/** Translation function (optional, falls back to German) */
t?: (key: string) => string;
}
let {
userSettings,
appId,
showNavigation = true,
showTheme = true,
showLanguage = true,
showGeneral = true,
title = 'App-Einstellungen',
description = 'Diese Einstellungen gelten für alle Mana Apps',
t = (key: string) => key,
}: Props = $props();
// Available routes for start page selection
const availableRoutes = $derived(appId ? getAvailableRoutes(appId) : []);
const defaultRoute = $derived(appId ? getDefaultRoute(appId) : '/');
const currentStartPage = $derived(
appId ? userSettings.general?.startPages?.[appId] || defaultRoute : '/'
);
// Navigation position handler
async function handleNavPositionChange(position: NavPosition) {
await userSettings.updateGlobal({
@ -60,6 +82,24 @@
await userSettings.updateGlobal({ locale });
}
// Start page handler
async function handleStartPageChange(e: Event) {
const target = e.target as HTMLSelectElement;
if (appId) {
await userSettings.setStartPage(appId, target.value);
}
}
// Week start handler
async function handleWeekStartChange(day: WeekStartDay) {
await userSettings.updateGeneral({ weekStartsOn: day });
}
// Sounds handler
async function handleSoundsChange(enabled: boolean) {
await userSettings.updateGeneral({ soundsEnabled: enabled });
}
const colorSchemes = [
{ id: 'ocean', label: 'Ozean', color: 'bg-blue-500' },
{ id: 'nature', label: 'Natur', color: 'bg-green-500' },
@ -259,6 +299,104 @@
</div>
</div>
{/if}
{#if showGeneral}
<!-- General Settings -->
<div
class="space-y-4 {showLanguage || showTheme || showNavigation
? 'pt-4 border-t border-[hsl(var(--border))]'
: ''}"
>
<h3
class="text-xs font-semibold text-[hsl(var(--muted-foreground))] uppercase tracking-wider"
>
Allgemein
</h3>
{#if appId && availableRoutes.length > 1}
<!-- Start Page Selector -->
<div class="flex items-center justify-between py-2">
<div>
<p class="font-medium text-[hsl(var(--foreground))]">Startseite</p>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
Welche Seite beim Öffnen der App angezeigt wird
</p>
</div>
<select
class="px-3 py-1.5 text-sm font-medium rounded-lg bg-[hsl(var(--muted))] text-[hsl(var(--foreground))] border-none cursor-pointer appearance-none pr-8 bg-[url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23666%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpolyline%20points%3D%226%209%2012%2015%2018%209%22%3E%3C%2Fpolyline%3E%3C%2Fsvg%3E')] bg-no-repeat bg-[right_0.5rem_center] bg-[length:1rem]"
value={currentStartPage}
onchange={handleStartPageChange}
>
{#each availableRoutes as route}
<option value={route.path}>
{t(route.labelKey)}
</option>
{/each}
</select>
</div>
{/if}
<!-- Week Start Day -->
<div
class="flex items-center justify-between py-2 {appId && availableRoutes.length > 1
? 'border-t border-[hsl(var(--border))]'
: ''}"
>
<div>
<p class="font-medium text-[hsl(var(--foreground))]">Wochenstart</p>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
Erster Tag der Woche in Kalendern
</p>
</div>
<div class="flex gap-2">
<button
class="px-3 py-1.5 text-sm font-medium rounded-lg transition-colors {userSettings
.general?.weekStartsOn === 'monday'
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
: 'bg-[hsl(var(--muted))] hover:bg-[hsl(var(--muted))]/80 text-[hsl(var(--foreground))]'}"
onclick={() => handleWeekStartChange('monday')}
>
Montag
</button>
<button
class="px-3 py-1.5 text-sm font-medium rounded-lg transition-colors {userSettings
.general?.weekStartsOn === 'sunday'
? 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))]'
: 'bg-[hsl(var(--muted))] hover:bg-[hsl(var(--muted))]/80 text-[hsl(var(--foreground))]'}"
onclick={() => handleWeekStartChange('sunday')}
>
Sonntag
</button>
</div>
</div>
<!-- Sounds Toggle -->
<div
class="flex items-center justify-between py-2 border-t border-[hsl(var(--border))]"
>
<div>
<p class="font-medium text-[hsl(var(--foreground))]">Sounds</p>
<p class="text-sm text-[hsl(var(--muted-foreground))]">
Sound-Effekte in allen Apps
</p>
</div>
<button
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors {(userSettings
.general?.soundsEnabled ?? true)
? 'bg-[hsl(var(--primary))]'
: 'bg-gray-200 dark:bg-gray-700'}"
onclick={() => handleSoundsChange(!(userSettings.general?.soundsEnabled ?? true))}
>
<span
class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform {(userSettings
.general?.soundsEnabled ?? true)
? 'translate-x-6'
: 'translate-x-1'}"
></span>
</button>
</div>
</div>
{/if}
</div>
{#if userSettings.syncing}