From b720f64d2fee6c1e0742444ec4942bbf0ba646ca Mon Sep 17 00:00:00 2001 From: Till-JS <101404291+Till-JS@users.noreply.github.com> Date: Sun, 14 Dec 2025 15:19:22 +0100 Subject: [PATCH] feat(calendar): replace calendar dropdown with horizontal pills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace select dropdown with horizontal scrolling pill buttons in QuickEventOverlay - Each pill shows colored dot + calendar name for quick visual selection - Add setAsDefault method to calendars store - Improve settings page calendar editing with SettingsSection/SettingsCard components 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../components/event/QuickEventOverlay.svelte | 111 ++- .../web/src/lib/stores/calendars.svelte.ts | 17 + .../src/routes/(app)/settings/+page.svelte | 902 ++++++++++++------ 3 files changed, 683 insertions(+), 347 deletions(-) diff --git a/apps/calendar/apps/web/src/lib/components/event/QuickEventOverlay.svelte b/apps/calendar/apps/web/src/lib/components/event/QuickEventOverlay.svelte index f20908b53..759dc3cf1 100644 --- a/apps/calendar/apps/web/src/lib/components/event/QuickEventOverlay.svelte +++ b/apps/calendar/apps/web/src/lib/components/event/QuickEventOverlay.svelte @@ -361,15 +361,6 @@ }); } - // Update draft when calendar changes - function handleCalendarChange(e: Event) { - const target = e.target as HTMLSelectElement; - calendarId = target.value; - if (!isEditMode) { - eventsStore.updateDraftEvent({ calendarId: target.value }); - } - } - // Update draft when all-day changes function handleAllDayToggle() { isAllDay = !isAllDay; @@ -711,26 +702,31 @@ - -
-
-
-
-
- Kalender - {#if calendarsStore.calendars.length > 0} - - {:else} - Standardkalender wird erstellt - {/if} -
+ +
+ {#if calendarsStore.calendars.length > 0} +
+ {#each calendarsStore.calendars as cal} + + {/each} +
+ {:else} + Standardkalender wird erstellt + {/if}
@@ -1225,6 +1221,63 @@ border-radius: 50%; } + /* Calendar pills */ + .calendar-pills-container { + padding: 0.5rem 0; + border-bottom: 1px solid hsl(var(--color-border)); + } + + .calendar-pills-scroll { + display: flex; + gap: 0.5rem; + overflow-x: auto; + scrollbar-width: none; + -ms-overflow-style: none; + padding: 0 1.25rem 2px; + } + + .calendar-pills-scroll::-webkit-scrollbar { + display: none; + } + + .calendar-pill { + display: flex; + align-items: center; + gap: 0.375rem; + padding: 0.375rem 0.75rem; + border: 1px solid hsl(var(--color-border)); + border-radius: 9999px; + background: transparent; + color: hsl(var(--color-muted-foreground)); + font-size: 0.8125rem; + font-weight: 500; + white-space: nowrap; + cursor: pointer; + transition: all 150ms; + flex-shrink: 0; + } + + .calendar-pill:hover { + background: hsl(var(--color-muted) / 0.3); + color: hsl(var(--color-foreground)); + } + + .calendar-pill.active { + background: hsl(var(--color-primary) / 0.1); + border-color: hsl(var(--color-primary) / 0.3); + color: hsl(var(--color-primary)); + } + + .calendar-pill-dot { + width: 10px; + height: 10px; + border-radius: 50%; + flex-shrink: 0; + } + + .calendar-pill-name { + } + .row-content { flex: 1; min-width: 0; diff --git a/apps/calendar/apps/web/src/lib/stores/calendars.svelte.ts b/apps/calendar/apps/web/src/lib/stores/calendars.svelte.ts index bde636f28..1f8fc7515 100644 --- a/apps/calendar/apps/web/src/lib/stores/calendars.svelte.ts +++ b/apps/calendar/apps/web/src/lib/stores/calendars.svelte.ts @@ -115,6 +115,23 @@ export const calendarsStore = { return this.updateCalendar(id, { isVisible: !calendar.isVisible }); }, + /** + * Set a calendar as the default + */ + async setAsDefault(id: string) { + const result = await api.updateCalendar(id, { isDefault: true }); + + if (result.data) { + // Update local state: set this one as default, remove default from others + calendars = getCalendarsArray().map((c) => ({ + ...c, + isDefault: c.id === id, + })); + } + + return result; + }, + /** * Get calendar by ID */ diff --git a/apps/calendar/apps/web/src/routes/(app)/settings/+page.svelte b/apps/calendar/apps/web/src/routes/(app)/settings/+page.svelte index 2c08a4f34..d73b78b68 100644 --- a/apps/calendar/apps/web/src/routes/(app)/settings/+page.svelte +++ b/apps/calendar/apps/web/src/routes/(app)/settings/+page.svelte @@ -7,15 +7,32 @@ import type { TimeFormat, AllDayDisplayMode } from '$lib/stores/settings.svelte'; import { calendarsStore } from '$lib/stores/calendars.svelte'; import { toast } from '$lib/stores/toast.svelte'; - import { GlobalSettingsSection } from '@manacore/shared-ui'; + import { GlobalSettingsSection, SettingsSection, SettingsCard } from '@manacore/shared-ui'; import type { CalendarViewType, Calendar } from '@calendar/shared'; // Calendar management state let editingCalendar = $state(null); + let editName = $state(''); + let editColor = $state(''); + let editIsDefault = $state(false); let showNewCalendarForm = $state(false); let newCalendarName = $state(''); let newCalendarColor = $state('#3b82f6'); + function startEditing(calendar: Calendar) { + editingCalendar = calendar; + editName = calendar.name; + editColor = calendar.color || '#3b82f6'; + editIsDefault = calendar.isDefault || false; + } + + function cancelEditing() { + editingCalendar = null; + editName = ''; + editColor = ''; + editIsDefault = false; + } + onMount(async () => { if (!authStore.isAuthenticated) { goto('/login'); @@ -59,8 +76,23 @@ toast.success('Kalender gelöscht'); } - async function handleUpdateCalendar(calendar: Calendar, name: string, color: string) { - const result = await calendarsStore.updateCalendar(calendar.id, { name, color }); + async function handleUpdateCalendar() { + if (!editingCalendar || !editName.trim()) return; + + // If setting as default and it wasn't before, use setAsDefault + if (editIsDefault && !editingCalendar.isDefault) { + const defaultResult = await calendarsStore.setAsDefault(editingCalendar.id); + if (defaultResult?.error) { + toast.error(`Fehler: ${defaultResult.error.message}`); + return; + } + } + + // Update name and color + const result = await calendarsStore.updateCalendar(editingCalendar.id, { + name: editName.trim(), + color: editColor, + }); if (result.error) { toast.error(`Fehler: ${result.error.message}`); @@ -68,7 +100,7 @@ } toast.success('Kalender aktualisiert'); - editingCalendar = null; + cancelEditing(); } function handleViewChange(view: CalendarViewType) { @@ -124,335 +156,422 @@ -
-
-

Meine Kalender

- -
+ + {#snippet icon()} + + + + {/snippet} + +
+
+ +
- {#if showNewCalendarForm} -
-
{ - e.preventDefault(); - handleCreateCalendar(); - }} - > -
- - -
-
- - -
-
-
- {/if} - -
- {#each calendarsStore.calendars as calendar} -
- {#if editingCalendar?.id === calendar.id} + {#if showNewCalendarForm} +
{ e.preventDefault(); - const form = e.target as HTMLFormElement; - const name = (form.elements.namedItem('name') as HTMLInputElement).value; - const color = (form.elements.namedItem('color') as HTMLInputElement).value; - handleUpdateCalendar(calendar, name, color); + handleCreateCalendar(); }} >
- - + +
- +
- {:else} -
- - {calendar.name} - {#if calendar.isDefault} - Standard - {/if} -
-
- - {#if !calendar.isDefault} -
+ {/if} + +
+ {#each calendarsStore.calendars as calendar} + {#if editingCalendar?.id === calendar.id} +
+
{ + e.preventDefault(); + handleUpdateCalendar(); + }} > - Löschen - - {/if} +
+
+ + +
+ +
+ +
+ + {editColor} +
+
+
+ + + +
+ + +
+
+
+ {:else} +
+
+ + {calendar.name} + {#if calendar.isDefault} + Standard + {/if} +
+
+ + {#if !calendar.isDefault} + + {/if} +
+
+ {/if} + {/each} + + {#if calendarsStore.calendars.length === 0} +
+

Keine Kalender vorhanden

{/if}
- {/each} - - {#if calendarsStore.calendars.length === 0} -
-

Keine Kalender vorhanden

-
- {/if} -
-
+
+ + -
- -
+ -
-

Kalender-Ansicht

- -
-
- Standard-Ansicht - Ansicht beim Ă–ffnen des Kalenders -
- -
- -
-
- Zeitformat - Anzeige der Uhrzeiten -
-
- - -
-
- -
- -
- -
- -
- -
- -
- - {#if settingsStore.filterHoursEnabled} -
-
- Sichtbare Stunden - Zeitbereich der in der Kalenderansicht angezeigt wird -
-
-
- - + + {/snippet} + +
+
+
+ Standard-Ansicht + Ansicht beim Ă–ffnen des Kalenders
- – -
- - handleViewChange(e.currentTarget.value as CalendarViewType)} + > + {#each Object.entries(viewLabels) as [value, label]} + + {/each} + +
+ +
+
+ Zeitformat + Anzeige der Uhrzeiten +
+
+ + +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ + {#if settingsStore.filterHoursEnabled} +
+
+ Sichtbare Stunden + Zeitbereich der in der Kalenderansicht angezeigt wird +
+
+
+ + +
+ – +
+ + +
+
+
+ {/if} + +
+
+ Ganztägige Termine + Wie sollen ganztägige Termine angezeigt werden? +
+
+ +
- {/if} - -
-
- Ganztägige Termine - Wie sollen ganztägige Termine angezeigt werden? -
-
- - -
-
-
+ + -
-

Termine

+ + {#snippet icon()} + + + + {/snippet} + +
+
+
+ Standard-Dauer + Voreingestellte Dauer fĂĽr neue Termine +
+ +
-
-
- Standard-Dauer - Voreingestellte Dauer fĂĽr neue Termine +
+
+ Standard-Erinnerung + Voreingestellte Erinnerung fĂĽr neue Termine +
+ +
- -
- -
-
- Standard-Erinnerung - Voreingestellte Erinnerung fĂĽr neue Termine -
- -
-
+ + -
-

Konto

+ + {#snippet icon()} + + + + {/snippet} + +
+
+
+ E-Mail + {authStore.user?.email || '-'} +
+
-
-
- E-Mail - {authStore.user?.email || '-'} +
+ +
-
- -
- -
-
+ +