Category chips were rendering as a separate flex row that got pushed to the
far right (between search and add button). Flatten the layout so chips appear
inline immediately after the Categories toggle pill, separated by a divider.
Remove redundant wrapper divs from TodosPage, PeoplePage, LocationsPage —
CategoryFilterBar now owns its own flex-1 sizing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
- 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>
Scale down all content text on mobile via .mobile-scale CSS class (excludes
navbar/UMBRA title). Hide calendar event times in month view (Google Calendar
style). Restructure CategoryFilterBar so categories display on a separate row
when toggled instead of being hidden behind the search bar. Reduce dashboard
widget density with hidden badges and tighter spacing on small screens.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- EntityTable: add useMediaQuery hook, mobileCardRender prop, and mobile card path
that replaces the table on screens <768px when a renderer is provided
- PeoplePage: add mobileCardRender showing name, category, email, phone
- LocationsPage: add mobileCardRender showing name, category, address
Note: TodosPage and RemindersPage use custom list components (TodoList,
ReminderList), not EntityTable directly — no changes needed there.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Delete person now severs the bidirectional connection when the person
is an umbral contact — removes both UserConnection rows and detaches
the counterpart's Person record. Fixes "Already connected" error
when trying to reconnect after deleting an umbral contact.
New PUT /people/{id}/unlink endpoint converts an umbral contact to a
standard contact (detaches linked fields) while also severing the
bidirectional connection, keeping the Person in the contact list.
Frontend: EntityDetailPanel gains extraActions prop. PeoplePage renders
an "Unlink" button in the panel footer for umbral contacts. Delete
mutation now also invalidates connections query.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements the full User Connections & Notification Centre feature:
Phase 1 - Database: migrations 039-043 adding umbral_name to users,
profile/social fields to settings, notifications table, connection
request/user_connection tables, and linked_user_id to people.
Phase 2 - Notifications: backend CRUD router + service + 90-day purge,
frontend NotificationsPage with All/Unread filter, bell icon in sidebar
with unread badge polling every 60s.
Phase 3 - Settings: profile fields (phone, mobile, address, company,
job_title), social card with accept_connections toggle and per-field
sharing defaults, umbral name display with CopyableField.
Phase 4 - Connections: timing-safe user search, send/accept/reject flow
with atomic status updates, bidirectional UserConnection + Person records,
in-app + ntfy notifications, per-receiver pending cap, nginx rate limiting.
Phase 5 - People integration: batch-loaded shared profiles (N+1 prevention),
Ghost icon for umbral contacts, Umbral filter pill, split Add Person button,
shared field indicators (synced labels + Lock icons), disabled form inputs
for synced fields on umbral contacts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add fullWidth field option to PanelField interface. Short fields
render in a grid grid-cols-2 layout; fullWidth fields (address, notes)
render below at full width. Add icons to People and Locations fields
for consistency with other detail panels.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
Remove the collapsible search behavior that hid the input behind an
icon when ≥4 categories existed. Search should always be visible and
match the standard header pattern (w-52 input with icon).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
- EntityTable: replace flat rows with grouped sections (label + rows),
each group gets its own section header for visual clarity
- EntityDetailPanel: add favourite/frequent toggle star button in header,
add multiline flag for notes with whitespace-pre-wrap
- CopyableField: use inline-flex to keep copy icon close to text
- PeoplePage: compute initials from first_name/last_name (not nickname),
build row groups from active filters, add toggle favourite mutation
- LocationsPage: build row groups from active filters, add toggle
frequent mutation, pass favourite props to detail panel
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bugs fixed:
- formatUpdatedAt treats naive UTC timestamps as UTC (append Z before parsing)
- PersonForm/LocationForm X button now inline with star toggle, matching panel style
- LocationForm contact placeholder changed from +44 to +61
QA suggestions actioned:
- CategoryAutocomplete: replace blur setTimeout with onPointerDown preventDefault
- CategoryFilterBar: replace hardcoded 600px maxWidth with 100vw
- Location "other" category shows dash instead of styled badge
- Delete dead legacy constants files (people/constants.ts, locations/constants.ts)
- EntityTable rows: add tabIndex, Enter/Space keyboard navigation, focus ring
- Replace Record<string, unknown> casts with typed keyof accessors
- Add email validation (field_validator) to Person and Location schemas
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove `name` from PersonUpdate schema (always computed, prevents bypass)
- Auto-split legacy `name` into first/last on create when only name provided
- Expand backend search to cover first_name, last_name, nickname, email, company
- Add server_default=text('false') to is_favourite and is_frequent model columns
- Add .catch() to clipboard API call in CopyableField
- Extract duplicate renderHeader into shared function in PeoplePage
- Add Escape key handler to close mobile detail panel overlays
- Extract calculate() out of useTableVisibility effects to single function
- Guard getInitials against empty string input
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>