mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-21 11:06:43 +02:00
✨ feat: add global start page setting across all apps
- Add GeneralSettings types (startPages, weekStartsOn, soundsEnabled) - Create app-routes.ts with available routes for 12 apps - Extend UserSettingsStore with general settings support - Update GlobalSettingsSection with start page selector UI - Add start page redirect logic to all app layouts: - Clock, Calendar, Todo, Zitare, Picture - Manadeck, Presi, Chat, Manacore - Create user-settings stores for Clock and Todo apps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
5b3c5ff4fb
commit
bbe540c3f1
16 changed files with 602 additions and 22 deletions
|
|
@ -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' },
|
||||
|
|
@ -102,7 +142,9 @@
|
|||
{#if showNavigation}
|
||||
<!-- Navigation Settings -->
|
||||
<div class="space-y-4">
|
||||
<h3 class="text-xs font-semibold text-[hsl(var(--muted-foreground))] uppercase tracking-wider">
|
||||
<h3
|
||||
class="text-xs font-semibold text-[hsl(var(--muted-foreground))] uppercase tracking-wider"
|
||||
>
|
||||
Navigation
|
||||
</h3>
|
||||
|
||||
|
|
@ -135,10 +177,14 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between py-2 border-t border-[hsl(var(--border))]">
|
||||
<div
|
||||
class="flex items-center justify-between py-2 border-t border-[hsl(var(--border))]"
|
||||
>
|
||||
<div>
|
||||
<p class="font-medium text-[hsl(var(--foreground))]">Sidebar eingeklappt</p>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))]">Standard-Zustand der Sidebar</p>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))]">
|
||||
Standard-Zustand der Sidebar
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
class="relative inline-flex h-6 w-11 items-center rounded-full transition-colors {userSettings
|
||||
|
|
@ -161,15 +207,21 @@
|
|||
|
||||
{#if showTheme}
|
||||
<!-- Theme Settings -->
|
||||
<div class="space-y-4 {showNavigation ? 'pt-4 border-t border-[hsl(var(--border))]' : ''}">
|
||||
<h3 class="text-xs font-semibold text-[hsl(var(--muted-foreground))] uppercase tracking-wider">
|
||||
<div
|
||||
class="space-y-4 {showNavigation ? 'pt-4 border-t border-[hsl(var(--border))]' : ''}"
|
||||
>
|
||||
<h3
|
||||
class="text-xs font-semibold text-[hsl(var(--muted-foreground))] uppercase tracking-wider"
|
||||
>
|
||||
Erscheinungsbild
|
||||
</h3>
|
||||
|
||||
<div class="flex items-center justify-between py-2">
|
||||
<div>
|
||||
<p class="font-medium text-[hsl(var(--foreground))]">Farbmodus</p>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))]">Hell, Dunkel oder automatisch</p>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))]">
|
||||
Hell, Dunkel oder automatisch
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex gap-1">
|
||||
{#each ['light', 'dark', 'system'] as mode}
|
||||
|
|
@ -186,10 +238,14 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between py-2 border-t border-[hsl(var(--border))]">
|
||||
<div
|
||||
class="flex items-center justify-between py-2 border-t border-[hsl(var(--border))]"
|
||||
>
|
||||
<div>
|
||||
<p class="font-medium text-[hsl(var(--foreground))]">Farbschema</p>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))]">Akzentfarbe der Benutzeroberfläche</p>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))]">
|
||||
Akzentfarbe der Benutzeroberfläche
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
{#each colorSchemes as scheme}
|
||||
|
|
@ -214,14 +270,18 @@
|
|||
? 'pt-4 border-t border-[hsl(var(--border))]'
|
||||
: ''}"
|
||||
>
|
||||
<h3 class="text-xs font-semibold text-[hsl(var(--muted-foreground))] uppercase tracking-wider">
|
||||
<h3
|
||||
class="text-xs font-semibold text-[hsl(var(--muted-foreground))] uppercase tracking-wider"
|
||||
>
|
||||
Sprache
|
||||
</h3>
|
||||
|
||||
<div class="flex items-center justify-between py-2">
|
||||
<div>
|
||||
<p class="font-medium text-[hsl(var(--foreground))]">Anzeigesprache</p>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))]">Sprache der Benutzeroberfläche</p>
|
||||
<p class="text-sm text-[hsl(var(--muted-foreground))]">
|
||||
Sprache der Benutzeroberfläche
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex gap-1">
|
||||
{#each languages as lang}
|
||||
|
|
@ -239,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