52 Commits

Author SHA1 Message Date
25830bb99e Fix calendar event color not updating after display calendar change
eventDidMount only fires once when FullCalendar first mounts a DOM element.
When event data refetches with a new calendarColor, the existing DOM element
is reused and --event-color CSS variable stays stale.

Fix: renderEventContent now uses a ref callback (syncColor) to walk up to
the parent .umbra-event element and update --event-color on every render,
ensuring background, hover, and dot colors reflect the current calendar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:13:34 +08:00
a68ec0e23e Add display calendar support: model, router, service, types, visibility filter
Previously unstaged changes required for the display calendar feature:
- EventInvitation model: display_calendar_id column
- Event invitations router: display-calendar PUT endpoint
- Event invitation service: display calendar update logic
- CalendarPage: respect display_calendar_id in visibility filter
- Types: display_calendar_id on CalendarEvent interface

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 19:03:22 +08:00
496666ec5a Fix 'calendar no longer available' for invited events
The shared-calendar removal guard checks allCalendarIds, which only
contains the user's own + shared calendars. Invited events belong to
the inviter's calendar, triggering a false positive. Skip the check
for invited events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 13:41:20 +08:00
bafda61958 Fix invited events hidden by calendar visibility filter
Invited events belong to the inviter's calendar, which doesn't exist
in the invitee's calendar list. The visibleCalendarIds filter was
removing them. Now invited events bypass this filter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 13:36:55 +08:00
8652c9f2ce Implement event invitation feature (invite, RSVP, per-occurrence override, leave)
Full-stack implementation of event invitations allowing users to invite connected
contacts to calendar events. Invitees can respond Going/Tentative/Declined, with
per-occurrence overrides for recurring series. Invited events appear on the invitee's
calendar with a Users icon indicator. LeaveEventDialog replaces delete for invited events.

Backend: Migration 054 (2 tables + notification types), EventInvitation model with
lazy="raise", service layer, dual-router (events + event-invitations), cascade on
disconnect, events/dashboard queries extended with OR for invited events.

Frontend: Types, useEventInvitations hook, InviteeSection (view list + RSVP buttons +
invite search), LeaveEventDialog, event invite toast with 3 response buttons, calendar
eventContent render with Users icon for invited events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 02:47:27 +08:00
be1fdc4551 Calendar backend optimisations: safety caps, shared calendar fix, query consolidation
Phase 1: Recurrence safety — MAX_OCCURRENCES=730 hard cap, adaptive 90-day
horizon for daily events (interval<7), RecurrenceRule cross-field validation,
ID bounds on location_id/calendar_id schemas.

Phase 2: Dashboard correctness — shared calendar events now included in
/dashboard and /upcoming via get_accessible_calendar_ids helper. Project stats
consolidated into single GROUP BY query (saves 1 DB round-trip).

Phase 3: Write performance — bulk db.add_all() for child events, removed
redundant SELECT in this_and_future delete path.

Phase 4: Frontend query efficiency — staleTime: 30_000 on calendar events
query eliminates redundant refetches on mount/view switch. Backend LIMIT 2000
safety guard on events endpoint.

Phase 5: Rate limiting — nginx limit_req zone on /api/events (30r/m) to
prevent DB flooding via recurrence amplification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 01:31:48 +08:00
050e0c7141 Fix QA findings: remove duplicate migration, formatting, static classNames
- Remove migration 054 (duplicate of 035 which already has all 3 indexes,
  including a superior partial index for starred events)
- Fix handleEventDidMount indentation and missing semicolons
- Replace eventClassNames arrow function with static UMBRA_EVENT_CLASSES array
- Correct misleading subquery comment in dashboard.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 01:02:07 +08:00
29c91cd706 Fix weekend header mismatch and wrong date format in day headers
Header mismatch: Removed weekend tint from column headers — the white
overlay replaced the standard header bg (hsl 0 0% 8% / 0.65), creating
a non-flush look. Weekend differentiation now comes from body cells only.

Date format: dayHeaderFormat was applied globally, causing month view
headers to show dates like "Sat 10/1" instead of just "Sat". Moved to
per-view formats: month shows weekday only, week shows weekday + d/m,
day shows full weekday + day + month name.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:57:17 +08:00
e18c94cd83 Fix weekend tint visibility, dot spacing, and event FOUC
Weekend tint: hsl(0 0% 6%) was lighter than page bg #0a0a0a (imperceptible).
Changed to hsl(0 0% 2%) = #050505 for visible darkening. Added rule for
fc-daygrid-day-frame to paint above FC6 internal layers.

Dot spacing: Reduced padding from 1px 4px to 1px 2px for tighter edge gap.

FOUC fix: Moved umbra-event class from eventDidMount (post-paint) to
eventClassNames (synchronous pre-mount). eventDidMount now only sets
the --event-color CSS custom property.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:43:33 +08:00
d6f5975fb9 Add dot indicator to timed month events in custom eventContent
eventContent replaces FC's default inner markup including the dot span.
Render a manual fc-daygrid-event-dot with border-color: var(--event-color).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:35:40 +08:00
0e35d473eb Refine calendar events: dot-only month timed, title-first week, no left border
- Month timed events: dot + title only, hover reveals translucent card
- Month all-day events: keep translucent fill
- Time right-aligned in month view (ml-auto)
- Week/day view: title on top, time underneath for better scanning
- Remove 2px left accent border from all events
- Set color:'transparent' on FC event data to prevent inline style conflicts
- Recurring repeat icon preserved in all views

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:24:20 +08:00
dec2c5d526 Fix event colors: remove inline backgroundColor/borderColor from event data
The previous commit failed to remove inline color props due to CRLF line
endings. FullCalendar was still setting inline styles that override CSS.
calendarColor is now correctly in extendedProps for the eventDidMount callback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:09:14 +08:00
c473e7e235 Calendar visual overhaul: translucent events, AU locale, typography hierarchy
- Import en-AU locale (object, not string) for proper date format (day/month)
- Add 12-hour time format (9:00 AM), side-by-side overlap columns
- Replace flat opaque event rectangles with translucent color-mix fills (12% opacity)
- Add 2px left accent border per calendar color via CSS custom property
- Implement eventContent render hook with typography hierarchy (time secondary, title primary)
- Add recurring event indicator (Repeat icon) next to recurring event titles
- Add now-indicator 6px pulse dot with prefers-reduced-motion respect
- Add subtle weekend column tint (hsl 0 0% 6%)
- Mobile: hide time spans in month view custom eventContent
- Update +more popover to inherit translucent event styling
- Document calendar event patterns in stylesheet.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 02:02:21 +08:00
a94485b138 Address code review findings across all phases
Phase 1 fixes:
- W-01: Add start_period: 30s to backend healthcheck for migration window
- W-03: Narrow .dockerignore *.md to specific files (preserve alembic/README)

Phase 2 fixes:
- C-01: Wrap Argon2id calls in totp.py (disable, regenerate, backup verify,
  backup store) — missed in initial AC-2 pass
- S-01: Extract async wrappers (ahash_password, averify_password,
  averify_password_with_upgrade) into services/auth.py, refactor all
  callers to use them instead of manual run_in_executor boilerplate
- W-01: Fix ntfy dedup regression — commit per category instead of per-user
  to preserve dedup records if a later category fails

Phase 4 fixes:
- C-01: Fix optimistic drag-and-drop cache key to include date range
- C-02: Replace toISOString() with format() to avoid UTC date shift in
  visible range calculation
- W-02: Initialize visibleRange from current month to eliminate unscoped
  first fetch + immediate refetch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:19:33 +08:00
2ab7121e42 Phase 4: Frontend performance optimizations
- AW-2: Scope calendar events fetch to visible date range via start/end
  query params, leveraging existing backend support
- AW-3: Reduce calendar events poll from 5s to 30s (personal organiser
  doesn't need 12 API calls/min)
- AS-4: Gate shared-calendar polling on hasSharedCalendars — saves 12
  wasted API calls/min for personal-only users
- AS-2: Lazy-load all route components with React.lazy() — only
  AdminPortal was previously lazy, now all 10 routes are code-split
- AS-1: Add Vite manualChunks to split FullCalendar (~400KB), React,
  TanStack Query, and UI libs into separate cacheable chunks
- AS-3: Extract clockNow into isolated ClockDisplay memo component —
  prevents all 8 dashboard widgets from re-rendering every minute

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:12:33 +08:00
2770a9e88e Address remaining QA suggestions S-02 through S-06
S-02: Confirmed drift-3 is used by auth/AmbientBackground — not dead code.
S-03: Extracted noise SVG data URI to module-level NOISE_SVG constant.
S-04: Added will-change: transform to drift orbs for GPU layer promotion.
S-05: Documented the 9 AM snooze default in getMinutesUntilTomorrowMorning.
S-06: Made calendar toolbar bg-card/95 with backdrop-blur-md for better
      readability over the transparent FullCalendar grid.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 18:35:44 +08:00
39a42d08ec Fix phantom dropdown arrow next to Today button on desktop
The mobile view Select had md:hidden on the <select> element, but the
Select component wraps it in a <div> with an absolute ChevronDown icon
that remained visible. Moved md:hidden to a wrapper div so the entire
Select (including the chevron) is hidden on desktop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:31:44 +08:00
4e91944956 Fix code review findings: sort dropdown, overlay ref, CalendarPage
- C-01: Simplify EntityTable sort dropdown to toggle-based (select
  column, re-select to flip direction), add aria-label
- W-01: Convert CalendarPage mobile overlay to MobileDetailOverlay
- W-02: Use ref for onClose in MobileDetailOverlay to prevent
  listener churn from inline arrow functions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 03:46:40 +08:00
a737f06e85 Action deferred QA items: shared overlay, sort, touch, a11y
- S-01/W-06/S-02/S-04: Extract MobileDetailOverlay shared component
  with Escape key, body scroll lock, and ARIA dialog attributes.
  Refactored Todos, Reminders, People, Locations, ProjectDetail.
- W-02: Add specificity contract comment to mobile-scale CSS
- W-03: Enforce 10px floor for text-[9px] on mobile
- W-05: Add sort dropdown to EntityTable mobile card view
- S-03: Export MOBILE/DESKTOP breakpoint constants from useMediaQuery,
  updated all 8 consumer files to use constants
- S-06: Bump KanbanBoard TouchSensor tolerance from 5 to 8
- S-07: Hover state audit — no action needed, hoverOnlyWhenSupported
  in Tailwind config already handles touch devices correctly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 03:43:25 +08:00
0fc2d05085 Fix calendar view dropdown clipping and title overlap on mobile
Add pr-8 to mobile view Select to prevent text clipping under chevron.
Add min-w-0 flex-shrink to calendar title h2 to prevent nav arrow overlap.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 02:14:35 +08:00
4d5052d731 Action QA findings: fix all critical/warning/suggestion items
Critical fixes:
- C-01: DatePicker isMobile now actually used for bottom sheet positioning
- C-02: Calendar title always visible (text-sm on mobile, text-lg on sm+)
- C-03: Mobile card text-[10px] → text-xs (meets 12px minimum)

Warning fixes:
- W-01: useMediaQuery SSR-safe (typeof window guard)
- W-02: KanbanBoard TouchSensor added (was lost during branch ops)
- W-03: Removed duplicate isMobile query, derived from !isDesktop
- W-04: Search restored on mobile for Calendar/Reminders/Projects (w-32 sm:w-52)
- W-05: SheetClose added to CalendarSidebar mobile Sheet
- W-06: Button icon uses min-h/min-w for touch targets instead of h-11

Suggestion fixes:
- S-01: Removed deprecated WebkitOverflowScrolling from KanbanBoard
- S-02: Added role/tabIndex/onKeyDown to EntityTable mobile card wrappers
- S-03: Added overflow-y-auto to mobile event detail panel

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 17:16:47 +08:00
f7ec04241b Phase 4: mobile polish and touch fallbacks
4a. Touch fallbacks for group-hover actions:
  - 9 occurrences across 5 files changed from opacity-0 group-hover:opacity-100
    to opacity-100 md:opacity-0 md:group-hover:opacity-100
  - CalendarSidebar (3), SharedCalendarSection (2), TaskDetailPanel (2),
    NotificationsPage (1), CopyableField (1)
  - Action buttons now always visible on touch, hover-revealed on desktop

4b. FullCalendar mobile touch:
  - Wheel navigation disabled on touch devices (ontouchstart check)
  - Prevents scroll hijacking on mobile, allows native scroll

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 17:04:44 +08:00
b05adf7f12 Phase 3: complex component mobile adaptations
3a. CalendarSidebar mobile collapse:
  - Desktop sidebar + resize handle hidden below lg breakpoint
  - Mobile Sheet overlay with PanelLeft toggle in toolbar
  - Template selection closes mobile sidebar automatically

3b. KanbanBoard touch support:
  - TouchSensor added alongside PointerSensor (200ms delay)
  - Column min-width reduced on mobile (160px vs 200px)
  - iOS smooth scroll enabled on horizontal container

3c. EntityTable mobile card view:
  - mobileCardRender optional prop renders cards instead of table on mobile
  - PeoplePage: card with name, category, email, phone
  - LocationsPage: card with name, category, address
  - TodosPage/RemindersPage use custom list components, not EntityTable

3d. DatePicker mobile bottom sheet:
  - Renders as full-width bottom sheet on mobile (< 768px)
  - Safe area inset padding for iOS home indicator
  - Desktop positioned dropdown unchanged

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 17:03:14 +08:00
d0477b1c13 Phase 2: toolbar responsive patterns
- All page toolbars now flex-wrap on mobile with min-h instead of fixed h-16
- Segmented button filters (priority, status, view) hidden on mobile, replaced
  with compact Select dropdowns
- Search inputs hidden on mobile where CategoryFilterBar already has search
- CategoryFilterBar wraps to full-width row on mobile (order-last)
- Action buttons show icon-only on mobile, full text on md+
- Calendar title hidden on xs screens for space
- Desktop layout completely unchanged (md:flex-nowrap restores original)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 16:56:08 +08:00
1c16df4db0 Phase 1: mobile responsive foundation
- useMediaQuery hook extracted from CalendarPage inline pattern
- h-screen → h-dvh for mobile address bar viewport fix
- px-6 → px-4 md:px-6 on all page containers/toolbars (14 files)
- Input/Select text-base on mobile to prevent iOS auto-zoom
- Sheet full-width on mobile, max-w-[540px] on sm+
- Button icon size touch-friendly (44px mobile, 40px desktop)
- Tailwind hoverOnlyWhenSupported: true (fixes 157 hover interactions)
- PWA meta tags (apple-mobile-web-app-capable, theme-color)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 16:51:53 +08:00
ff81ef7c14 Fix calendar sidebar resize lag at fast drag speeds
Replace per-mousemove setSidebarWidth() calls (triggering full React re-renders
including FullCalendar) with direct DOM style mutation during drag. React state
is committed only once on mouseup, eliminating all mid-drag re-renders and
localStorage writes that caused the lag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:47:47 +08:00
59c89c904c Resizable calendar sidebar with localStorage persistence
Sidebar width adjustable via click-and-drag (180–400px range, default 224px).
Width persists to localStorage across sessions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:58:50 +08:00
dd862bfa48 Fix QA review findings: 3 critical, 5 warnings, 1 suggestion
Critical:
- C-01: Populate member_count in GET /calendars for shared calendars
- C-02: Differentiate 423 lock errors in drag-drop onError (show lock-specific toast)
- C-03: Add expired lock purge to APScheduler housekeeping job

Warnings:
- W-01: Replace setattr loop with explicit field assignment in update_member
- W-02: Cap sync `since` param to 7 days to prevent unbounded scans
- W-05: Remove cosmetic isShared toggle (is_shared is auto-managed by invite flow)
- W-06: Populate preferred_name in _build_member_response from user model
- W-07: Add releaseMutation to release callback dependency array

Suggestion:
- S-06: Remove unused ConvertToSharedRequest schema

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:41:08 +08:00
3dcf9d1671 Fix isSharedEvent excluding calendar owners — lock banner never appeared for owners
The selectedEventIsShared check used `permissionMap.get(...) !== 'owner'` which
excluded calendar owners from all shared-event behavior (lock polling, lock
acquisition, lock banner display). Replaced with a sharedCalendarIds set that
includes both owned shared calendars (via cal.is_shared) and memberships.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 17:41:47 +08:00
b401fd9392 Phase 6: Real-time sync, drag-drop guards, security fix, invite bug fix, UI polish
- Event polling (5s refetchInterval) so collaborators see changes without refresh
- Lock status polling in EventDetailPanel view mode — proactive lock banner
- Per-event editable flag blocks drag on read-only shared events
- Read-only permission guard in handleEventDrop/handleEventResize
- M-01 security fix: block non-owners from moving events off shared calendars (403)
- Fix invite response type (backend returns list, not wrapper object)
- Remove is_shared from CalendarCreate/CalendarUpdate input schemas
- New PermissionToggle segmented control (Eye/Pencil/Shield icons)
- CalendarMemberRow restructured into spacious two-line card layout
- CalendarForm dialog widened (sm:max-w-2xl), polished invite card with accent border
- SharedCalendarSettings dialog widened (sm:max-w-lg)
- CalendarMemberList max-height increased (max-h-48 → max-h-72)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:46:15 +08:00
14fc085009 Phase 5: Shared calendar polish — scoped polling, admin stats, dual panel fix, edge case handling
- Scope shared calendar polling to CalendarPage only (other consumers no longer poll)
- Add admin sharing stats card (owned/member/invites sent/received) in UserDetailSection
- Fix dual EventDetailPanel mount via JS media query breakpoint (replaces CSS hidden)
- Auto-close panel + toast when shared calendar is removed while viewing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 15:38:52 +08:00
eedfaaf859 Phase 4: Event locking + permission gating for shared calendars
- useEventLock hook with auto-release on unmount/event change
- EventLockBanner component for locked event display
- EventDetailPanel: lock acquire on edit, release on save/cancel, permission-gated edit/delete buttons
- CalendarPage: permission map from owned+shared calendars, per-event editable gating

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 05:24:43 +08:00
4e3fd35040 Phase 3: Frontend core for shared calendars
Types: CalendarPermission, SharedCalendarMembership, CalendarMemberInfo,
CalendarInvite, EventLockInfo. Calendar type extended with is_shared.

Hooks: useCalendars extended with shared calendar polling (5s).
useSharedCalendars for member CRUD, invite responses, color updates.
useConnections cascade invalidation on disconnect.

New components: PermissionBadge, CalendarMemberSearch,
CalendarMemberRow, CalendarMemberList, SharedCalendarSection,
SharedCalendarSettings (non-owner dialog with color, members, leave).

Modified: CalendarForm (sharing toggle, member management for owners),
CalendarSidebar (shared calendars section with localStorage visibility),
CalendarPage (shared calendar ID integration in event filtering),
NotificationToaster (calendar_invite toast with accept/decline),
NotificationsPage (calendar_invite inline actions + type icons).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 04:59:13 +08:00
2e2466bfa6 Fix People search: alignment, focus ring, and name matching
- Wrap CategoryFilterBar in flex-1 min-w-0 so search aligns right
- Add first_name, last_name, nickname to People search filter
- Add ring-inset to all header search inputs (People, Todos,
  Locations, Reminders, Calendar) to prevent focus ring clipping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:35:40 +08:00
057b43627b Smooth calendar resize during panel transitions
Replace single delayed updateSize() with requestAnimationFrame loop
that continuously resizes FullCalendar throughout the 300ms CSS
transition, eliminating the visual desync between panel and calendar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:13:40 +08:00
8945295e2a Replace Sheet overlays with inline detail panels across all pages
- Calendar: move view selector left, inline EventDetailPanel with view/edit/create
  modes, fix resize on panel close, remove all Sheet/Dialog usage
- Todos: add TodoDetailPanel with inline view/edit/create, replace CategoryFilterBar
  with shared component (drag-and-drop categories), 55/45 split layout
- Reminders: add ReminderDetailPanel with inline view/edit/create, 55/45 split layout
- Dashboard: all widget items now deep-link to destination page AND open the
  relevant item's detail panel (events, todos, reminders)
- Fix TS errors: unused imports, undefined→null coalescing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:43:06 +08:00
898ecc407a Stage 7: final polish — transitions, navigation, calendar panel
- Add animate-fade-in page transitions to all pages
- Persist sidebar collapsed state in localStorage
- Add two-click logout confirmation using useConfirmAction
- Restructure Todos header: replace <select> with pill filters, move search right
- Move Reminders search right-aligned with spacer
- Add event search dropdown + Create Event button to Calendar toolbar
- Add search input to Projects header with name/description filtering
- Fix CategoryFilterBar search focus ring clipping with ring-inset
- Create EventDetailPanel: read-only event view with copyable fields,
  recurrence display, edit/delete actions, location name resolution
- Refactor CalendarPage to 55/45 split-panel layout matching People/Locations
- Add mobile overlay panel for calendar event details
- Add navigation state handler for CalendarPage (date/view from dashboard)
- Add navigation state handler for ProjectsPage (status filter from dashboard)
- Make all dashboard widgets navigable: stat cards → pages, week timeline
  days → calendar day view, upcoming items → source pages, countdown items
  → calendar, today's events/todos/reminders → respective pages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 22:08:08 +08:00
27003374e3 Fix QA review warnings: model server_default, calendar_id coercion
- EventTemplate model: add server_default=func.now() to created_at
  to match migration 011 and prevent autogenerate drift
- CalendarPage: use nullish coalescing for template calendar_id
  instead of || 0 which produced an invalid falsy ID

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 13:50:31 +08:00
f64e181fbe Fix template form: location picker auto-open and event title wording
- LocationPicker: skip initial mount effect so dropdown doesn't auto-open
  when form loads with an existing location value
- EventForm: separate templateData/templateName props from event prop so
  template-based creation shows "Create Event from X Template" title
  instead of "Edit Event", and correctly uses Create button + no Delete
- CalendarPage: pass templateName through to EventForm

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 13:10:31 +08:00
80f3f3ed10 Calendar enhancements: scroll navigation, birthday color editing, event templates
- Add wheel scroll navigation in month view (debounced, prevents rapid scrolling)
- Allow editing color on system calendars (Birthdays) - name field disabled
- Event templates: full CRUD backend (model, schema, router, migration 011)
- Event templates: sidebar section with create/edit/delete, click to pre-fill EventForm
- Register event_templates router at /api/event-templates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:34:16 +08:00
56876841c7 Fix scope dialog button alignment and night briefing showing wrong day
- Replace DialogFooter with plain div for vertical button layout in scope dialog
- Add today's remaining items to night briefing (before 5 AM) before tomorrow preview

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 02:13:16 +08:00
e22cad1d86 Fix QA findings: firstDay reactivity, state revert, helper extraction
- W1: Add key prop to FullCalendar so firstDay change triggers remount
- W2: Revert firstDayOfWeek toggle state on API failure
- S1: Extract _rule_int helper in recurrence service to reduce duplication

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 02:02:15 +08:00
f66ffba0ef Fix calendar not resizing when sidebar collapses
Use ResizeObserver on the calendar container to call
FullCalendar.updateSize() when the sidebar transition
changes the available width.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 01:39:23 +08:00
c62f8bc2a2 Add first day of week setting and fix calendar header alignment
- Add first_day_of_week column to settings (0=Sunday, 1=Monday)
- Add Calendar section in Settings with toggle button
- Pass firstDay to FullCalendar from settings
- Align calendar toolbar and sidebar header to h-16 (matches UMBRA header)
- Remove border/padding wrapper from calendar grid for full-width layout

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 01:33:45 +08:00
5701e067dd Fix issues from QA review: critical bugs, warnings, and accessibility
- C1: Nominatim search already uses run_in_executor (non-blocking)
- C2: Ensure target event is deleted in "this_and_future" scope
- W3: Add Field constraints (ge/le) on RecurrenceRule fields
- W4: Add safety cleanup for body overflow on Sheet unmount
- W5: Block drag-drop/resize on recurring events (must use scope dialog)
- W6: Discard stale LocationPicker responses via request ID
- S8: Add role="dialog" and aria-modal to Sheet component

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 01:22:57 +08:00
f826d05c60 Fix recurrence edit/delete scope and simplify weekly UI
- Weekly recurrence no longer requires manual weekday selection;
  auto-derives from event start date
- EventForm now receives and forwards editScope prop to API
  (edit_scope in PUT body, scope query param in DELETE)
- CalendarPage passes scope through proper prop instead of _editScope hack
- Backend this_and_future: inherits parent's recurrence_rule when child
  has none, properly regenerates children after edit
- Backend: parent-level edits now delete+regenerate all children

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 01:08:00 +08:00
d811890509 Add Sheet forms, recurrence UI, all-day fix, LocationPicker
- Sheet component: slide-in panel replacing Dialog for all forms
- EventForm: structured recurrence picker, all-day end-date offset fix,
  LocationPicker with OSM search integration
- CalendarPage: scope dialog for editing/deleting recurring events
- TodoForm/ReminderForm/LocationForm: migrated to Sheet with 2-col layouts
- LocationPicker: debounced search combining local DB + Nominatim results
- Backend: /locations/search endpoint with OSM proxy
- CSS: slimmer all-day event bars in calendar grid
- Types: RecurrenceRule interface, extended CalendarEvent fields

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 00:42:12 +08:00
5b056cf674 Add calendar redesign frontend with multi-calendar UI
- Custom toolbar replacing FullCalendar defaults (nav, today, view switcher)
- Calendar sidebar with visibility toggles, color dots, add/edit support
- CalendarForm dialog for creating/editing calendars with color swatches
- EventForm updated to use calendar dropdown instead of color picker
- CSS overrides: accent-tinted today highlight, now indicator, rounded event pills
- Types updated for Calendar interface and mixed id types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 19:14:06 +08:00
c5adc316ef resolved event create bug 2026-02-19 20:44:14 +08:00
d5080f59af fixed day view visual bug 2026-02-19 20:38:11 +08:00