mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-16 23:19:40 +02:00
Merge branch 'dev-1' into dev
This commit is contained in:
commit
d41d060bb3
1770 changed files with 168028 additions and 31031 deletions
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue