From e56485f21edd1c1e3055a8aa9334527de974e868 Mon Sep 17 00:00:00 2001 From: Till-JS <101404291+Till-JS@users.noreply.github.com> Date: Wed, 3 Dec 2025 11:54:17 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(calendar):=20improve=20header?= =?UTF-8?q?=20UI=20and=20add=20quick=20event=20overlay?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Compact header styling with smaller buttons and reduced padding - Add configurable hour filter display in header - Add QuickEventOverlay component for fast event creation - Add allDayDisplayMode to event metadata types - Extend settings store with filter hours configuration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../components/calendar/CalendarHeader.svelte | 87 ++- .../src/lib/components/event/EventForm.svelte | 24 + .../components/event/QuickEventOverlay.svelte | 499 ++++++++++++++++++ .../web/src/lib/stores/settings.svelte.ts | 20 +- .../packages/shared/src/types/event.ts | 7 + 5 files changed, 606 insertions(+), 31 deletions(-) create mode 100644 apps/calendar/apps/web/src/lib/components/event/QuickEventOverlay.svelte diff --git a/apps/calendar/apps/web/src/lib/components/calendar/CalendarHeader.svelte b/apps/calendar/apps/web/src/lib/components/calendar/CalendarHeader.svelte index 0b74ce8ce..c9de12e9d 100644 --- a/apps/calendar/apps/web/src/lib/components/calendar/CalendarHeader.svelte +++ b/apps/calendar/apps/web/src/lib/components/calendar/CalendarHeader.svelte @@ -60,18 +60,18 @@
- @@ -123,7 +123,7 @@ display: flex; align-items: center; justify-content: space-between; - padding: 1rem 1.5rem; + padding: 0.5rem 1rem; border-bottom: 1px solid hsl(var(--color-border)); background: hsl(var(--color-surface)); } @@ -131,16 +131,50 @@ .header-left { display: flex; align-items: center; - gap: 1rem; + gap: 0.5rem; + } + + .today-btn { + padding: 0.25rem 0.625rem; + border: 1px solid hsl(var(--color-border)); + background: transparent; + border-radius: var(--radius-sm); + font-size: 0.75rem; + font-weight: 500; + color: hsl(var(--color-foreground)); + cursor: pointer; + transition: all 150ms ease; + } + + .today-btn:hover { + background: hsl(var(--color-muted)); } .nav-buttons { display: flex; - gap: 0.25rem; + gap: 0.125rem; + } + + .nav-btn { + padding: 0.25rem; + border: none; + background: transparent; + border-radius: var(--radius-sm); + color: hsl(var(--color-muted-foreground)); + cursor: pointer; + transition: all 150ms ease; + display: flex; + align-items: center; + justify-content: center; + } + + .nav-btn:hover { + background: hsl(var(--color-muted)); + color: hsl(var(--color-foreground)); } .header-title { - font-size: 1.25rem; + font-size: 1rem; font-weight: 600; color: hsl(var(--color-foreground)); margin: 0; @@ -149,22 +183,22 @@ .header-right { display: flex; align-items: center; - gap: 1rem; + gap: 0.75rem; } .view-selector { display: flex; background: hsl(var(--color-muted)); border-radius: var(--radius-md); - padding: 0.25rem; + padding: 0.125rem; } .view-btn { - padding: 0.5rem 1rem; + padding: 0.25rem 0.625rem; border: none; background: transparent; border-radius: var(--radius-sm); - font-size: 0.875rem; + font-size: 0.75rem; font-weight: 500; color: hsl(var(--color-muted-foreground)); cursor: pointer; @@ -183,15 +217,15 @@ .filter-toggles { display: flex; - gap: 0.5rem; + gap: 0.25rem; } .filter-toggle { - padding: 0.5rem 0.75rem; + padding: 0.25rem 0.5rem; border: 1px solid hsl(var(--color-border)); background: transparent; - border-radius: var(--radius-md); - font-size: 0.75rem; + border-radius: var(--radius-sm); + font-size: 0.6875rem; font-weight: 600; color: hsl(var(--color-muted-foreground)); cursor: pointer; @@ -209,14 +243,11 @@ border-color: hsl(var(--color-primary)); } - .btn-icon { - padding: 0.5rem; - } - @media (max-width: 640px) { .calendar-header { flex-direction: column; - gap: 1rem; + gap: 0.5rem; + padding: 0.5rem; } .header-left { @@ -225,7 +256,7 @@ } .header-title { - font-size: 1rem; + font-size: 0.875rem; } } diff --git a/apps/calendar/apps/web/src/lib/components/event/EventForm.svelte b/apps/calendar/apps/web/src/lib/components/event/EventForm.svelte index 3d65e2ace..35309054d 100644 --- a/apps/calendar/apps/web/src/lib/components/event/EventForm.svelte +++ b/apps/calendar/apps/web/src/lib/components/event/EventForm.svelte @@ -20,6 +20,9 @@ let location = $state(event?.location || ''); let isAllDay = $state(event?.isAllDay || false); let calendarId = $state(event?.calendarId || ''); + let allDayDisplayMode = $state<'default' | 'header' | 'block'>( + event?.metadata?.allDayDisplayMode || 'default' + ); // Set default calendar when calendars are loaded $effect(() => { @@ -73,6 +76,11 @@ const startDateTime = new Date(`${startDate}T${isAllDay ? '00:00' : startTime}`); const endDateTime = new Date(`${endDate}T${isAllDay ? '23:59' : endTime}`); + // Build metadata with display mode if not default + const metadata = isAllDay && allDayDisplayMode !== 'default' + ? { ...(event?.metadata || {}), allDayDisplayMode: allDayDisplayMode as 'header' | 'block' } + : event?.metadata; + const data: CreateEventInput | UpdateEventInput = { title: title.trim(), description: description.trim() || undefined, @@ -81,6 +89,7 @@ startTime: startDateTime.toISOString(), endTime: endDateTime.toISOString(), calendarId, + metadata, }; submitting = true; @@ -117,6 +126,21 @@
+ {#if isAllDay} +
+ + +
+ {/if} +
diff --git a/apps/calendar/apps/web/src/lib/components/event/QuickEventOverlay.svelte b/apps/calendar/apps/web/src/lib/components/event/QuickEventOverlay.svelte new file mode 100644 index 000000000..44490769c --- /dev/null +++ b/apps/calendar/apps/web/src/lib/components/event/QuickEventOverlay.svelte @@ -0,0 +1,499 @@ + + + + + + + + diff --git a/apps/calendar/apps/web/src/lib/stores/settings.svelte.ts b/apps/calendar/apps/web/src/lib/stores/settings.svelte.ts index 9dd20307a..7cd9e1568 100644 --- a/apps/calendar/apps/web/src/lib/stores/settings.svelte.ts +++ b/apps/calendar/apps/web/src/lib/stores/settings.svelte.ts @@ -9,6 +9,7 @@ import type { CalendarViewType } from '@calendar/shared'; // Settings types export type WeekStartDay = 0 | 1; // 0 = Sunday, 1 = Monday export type TimeFormat = '24h' | '12h'; +export type AllDayDisplayMode = 'header' | 'block'; // header = separate row, block = full day block in grid export interface CalendarAppSettings { // View settings @@ -20,6 +21,7 @@ export interface CalendarAppSettings { filterHoursEnabled: boolean; // Filter visible hours dayStartHour: number; // First visible hour (0-23) dayEndHour: number; // Last visible hour (0-23) + allDayDisplayMode: AllDayDisplayMode; // How to display all-day events // UI settings sidebarCollapsed: boolean; @@ -35,7 +37,10 @@ const DEFAULT_SETTINGS: CalendarAppSettings = { showOnlyWeekdays: false, showWeekNumbers: false, timeFormat: '24h', - hideEarlyHours: false, + filterHoursEnabled: false, + dayStartHour: 6, + dayEndHour: 20, + allDayDisplayMode: 'header', sidebarCollapsed: false, defaultEventDuration: 60, defaultReminder: 15, @@ -94,8 +99,17 @@ export const settingsStore = { get timeFormat() { return settings.timeFormat; }, - get hideEarlyHours() { - return settings.hideEarlyHours; + get filterHoursEnabled() { + return settings.filterHoursEnabled; + }, + get dayStartHour() { + return settings.dayStartHour; + }, + get dayEndHour() { + return settings.dayEndHour; + }, + get allDayDisplayMode() { + return settings.allDayDisplayMode; }, get defaultEventDuration() { return settings.defaultEventDuration; diff --git a/apps/calendar/packages/shared/src/types/event.ts b/apps/calendar/packages/shared/src/types/event.ts index 6f988977d..b5589294c 100644 --- a/apps/calendar/packages/shared/src/types/event.ts +++ b/apps/calendar/packages/shared/src/types/event.ts @@ -7,6 +7,11 @@ export interface EventAttendee { status?: 'accepted' | 'declined' | 'tentative' | 'pending'; } +/** + * How to display all-day events + */ +export type AllDayDisplayMode = 'header' | 'block'; + /** * Event metadata stored in JSONB */ @@ -23,6 +28,8 @@ export interface EventMetadata { priority?: 'low' | 'normal' | 'high'; /** Tags/labels for the event */ tags?: string[]; + /** Override for all-day display mode (uses global setting if not set) */ + allDayDisplayMode?: AllDayDisplayMode; } /**