Commit Graph
34 Commits
Author SHA1 Message Date
jake d75d40e63a Fix avatar conditional rendering in NavBar and AccountSection
- Remove `{userImage && ...}` guards around AvatarImage; pass `src`
  directly so AvatarFallback renders naturally when src is undefined
- In AccountSection, clear `src` during pending state instead of
  conditionally rendering the image element; guard destructive overlay
  color on `!isPending` so it doesn't flash while uploading
2026-03-08 11:39:46 -04:00
jake 1407571b64 Add inline name editing to account settings
- Add `updateNameAction` server action that calls `auth.api.updateUser`
  with validation (non-empty, max 100 chars)
- Replace static name display in AccountSection with an inline edit
  mode: click pencil icon to activate, Enter/blur to save, Escape to
  cancel
- Use auto-sizing ghost input (invisible span grid trick) so the field
  matches text width
- Animate between display and edit states with AnimatePresence fade;
  show Spinner while transition is pending
2026-03-08 11:11:44 -04:00
jake 84c4356a9f Add user avatar upload to account settings and nav bar
- Add `uploadAvatarAction` and `removeAvatarAction` server actions;
  store files under `DATA_DIR/avatars/{userId}.{ext}` via Bun.write
- Add `GET /api/avatars/[userId]` route that scans the avatar dir with
  Bun.Glob and serves with immutable Cache-Control (cache-busted via
  query param)
- Refactor AccountSection with hover overlay, AnimatePresence fade,
  and file input; click avatar to upload (no image) or remove (has image)
- Replace NavBar user initial badge with Avatar + DropdownMenu showing
  name, email, settings link, and sign-out
- Thread `userEmail` and `userImage` props through AuthenticatedShell
  → NavBar and SettingsPage → AccountSection
2026-03-08 11:02:33 -04:00
jake 7ed172d675 Replace Jotai atoms with local state, add Suspense for PPR compatibility
- Delete filterable-row, backup-schedule, integrations, and system-health
  atom files; replace with useState/useTransition + server action calls
- Replace /api/explore/discover route with discoverByGenre server action;
  refactor FilterableTitleRow to call it directly via useTransition
- Wrap PagesLayout and AuthLayout children in Suspense to fix dynamic
  rendering errors during PPR static generation; remove StoreProvider
- Sequence ExplorePage session fetch before TMDB calls to prevent
  build-time requests during static generation
- Drop unnecessary "use client" directives from dashboard and person
  components that have no client-side hooks or browser API usage
- Improve Dockerfile: add bun install layer cache mount, copy
  .next/cache from builder, reorder ENV declarations
2026-03-07 16:00:19 -05:00
jake 383f0b819b Polish mobile layouts, extract ExpandableText, fix image sizing
- Stack title-hero poster/metadata vertically on mobile (flex-col md:flex-row),
  switch backdrop tall breakpoint from sm to md
- Extract bio expand/collapse logic into reusable ExpandableText component;
  use it in PersonHero and TitleHero
- Fix ContinueWatchingCard image to use fill + sizes instead of fixed
  width/height to avoid layout shift
- Add fade-out gradient on genre chip scrollbar edge on mobile
- Show scaled-down ambient glow on mobile instead of hiding it entirely
- Humanize knownForDepartment labels (Acting→Actor, Directing→Director, etc.)
- Change cast member name from truncate to line-clamp-2 for wrapping
- Hide tooltip arrow via [&>:last-child]:hidden instead of color-matching it
- Apply safe-area-inset padding to HeroBanner content on notched devices
2026-03-07 12:09:51 -05:00
jake 8d8c49a7f0 Replace API route handlers with server actions across the app
- Delete 10 API routes (stats, system-health, update-check, jobs/trigger,
  backup/restore, person, titles import/resolve, registration/status) and
  move logic into server actions in lib/actions/settings.ts and
  lib/actions/watchlist.ts
- Refactor SystemHealthCards to accept initialData prop and hydrate Jotai
  atoms via useHydrateAtoms; replace useSystemHealth SWR hook with a
  lightweight useSystemHealthRefresh that calls getSystemHealthAction
- Convert BackupRestoreSection to use restoreBackupAction + useTransition
  instead of a raw fetch call
- Split SystemStatusCard, BackgroundJobsCard, and StorageCard into
  standalone components reading from systemHealthDataAtom
- Make SetupPage async with connection() to opt into dynamic rendering
2026-03-06 17:31:14 -05:00
jake 73b07f5ff6 Refactor auth session handling and server actions across app
- Overhaul `lib/auth/server.ts` and `lib/auth/session.ts`; update all API
  routes and server actions to use the revised session pattern
- Refactor server actions (settings, titles, watchlist, setup) for
  consistency with new auth layer
- Extract `SetupForm` into its own client component with `useActionState`,
  animated steps, and copyable env snippets
- Move landing page redirect logic into `app/page.tsx`; slim down
  `LandingPage` component
- Add `proxy.ts` for local dev proxying
- Minor cleanup to `NavBar`, `MobileTabBar`, and `TitleCard`
2026-03-06 17:05:49 -05:00
jakeandClaude Opus 4.6 7a3052250d Optimize frontend: memoization, modern React hooks, reduced render overhead
- useTimeAgo: replace per-instance intervals with shared useSyncExternalStore ticker
- useTiltEffect: gate to fine-pointer devices, skip motion graphs on touch
- Carousel: bake WheelGesturesPlugin into primitive, remove from all consumers
- TitleSeasons: use memoized Set + precomputed progress map instead of Array.includes
- useSearch: stabilize results identity with useMemo
- CommandPalette: hoist shortcut grouping to module scope, stabilize effect deps
- StarRating: hoist static spring transition to module constant
- Settings toggles: use useOptimistic + useTransition for auto-rollback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:15:08 -05:00
jake 49cea568a9 Add Sonarr and Radarr list integrations
- New `lists` service fetches Sonarr/Radarr library via their REST APIs
  and auto-imports matching titles from TMDB into the user's watchlist
- `app/api/lists/[token]/route.ts` webhook endpoint triggers a list sync
- Unified `IntegrationCard` component replaces `WebhookCard`, handling
  both webhook-style (Plex/Jellyfin/Emby) and list-style (Sonarr/Radarr)
  integrations with per-type config forms
- Schema migration adds `sonarr` and `radarr` to the integration type
  enum and a `listConnections` table for list-based integrations
- 212 tests added for the lists service covering import, deduplication,
  and error handling
2026-03-06 15:34:34 -05:00
jakeandClaude Opus 4.6 a263a6536a Show disabled jobs properly in admin health dashboard
- Backup and update check jobs show as "disabled" (not "succeeded") when
  their respective settings are off
- Disabled jobs excluded from "x of y jobs healthy" count, sorted to
  bottom of list, with schedule/next-run hidden and trigger button disabled
- Active jobs sorted by next run time
- "Never run" uses amber dot to differentiate from gray "disabled" dot
- Extract RefreshButton component and add it to all three health cards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 13:02:04 -05:00
jakeandClaude Opus 4.6 ac1cbddf0a Fix SSR crash from window access in webhook-card component
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 19:17:35 -05:00
jakeandClaude Opus 4.6 436f3d7fad Fix Select trigger display values and add TMDB attribution
Base UI Select.Value renders raw values by default — add children render
functions to map values to human-readable labels (e.g. "this_month" →
"This Month", "0" → "unlimited"). Switch inline select underlines from
border-bottom to text-decoration for proper baseline alignment. Add TMDB
attribution with logo and disclaimer to settings footer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 19:15:52 -05:00
jakeandClaude Opus 4.6 fc982bee1b Comprehensive Web Interface Guidelines audit: accessibility, motion, and typography
- Add global MotionConfig reducedMotion="user" in root layout
- Add color-scheme: dark, theme-color meta, skip-to-main-content link
- Add aria-hidden={true} to ~60+ decorative icons across 33 files
- Add aria-label to all icon-only buttons (logout, star rating, play, delete, etc.)
- Replace transition-all with specific properties (11 files)
- Add motion-safe: prefix for CSS hover transforms and animate-pulse
- Add prefers-reduced-motion: reduce override in globals.css
- Add useReducedMotion guard to status-dot pulsing animation
- Fix focus-visible styles on auth form inputs (focus → focus-visible, stronger ring)
- Add text-balance to all headings (13 files)
- Replace "..." with "…" (U+2026) in all loading/placeholder text
- Add autocomplete, spellCheck, role="alert" to auth form
- Add aria-label to Switch controls and filmography select
- Fix heading hierarchy (h3 → h2 where h2 was skipped)
- Add break-words to title overview, overscroll-contain to drawer
- Add confirmation dialog for destructive library removal
- Add group-focus-within:opacity-100 for keyboard-accessible backup actions
- Add role="radio" + aria-checked to star rating buttons
- Add aria-label to carousel region, role="img" to TMDB logo SVG
- Add text-foreground to native select for dark mode consistency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 18:20:25 -05:00
jakeandClaude Opus 4.6 f36ca0cbf8 Optimize performance: batch DB ops, N+1 fixes, Suspense streaming, and Jotai best practices
- Add composite indexes on userEpisodeWatches and userMovieWatches for hot queries
- Batch episode tracking: wrap season/batch watches in single transaction (~8 queries vs 8*N)
- Fix N+1 patterns in credits, recommendations, and filmography with batch prefetch+insert
- Stream TV season hydration via Suspense instead of blocking page render
- Optimize webhook logs (per-connection LIMIT 10) and system health queries
- Merge genre filter waterfalls into single Promise.all fetch
- Migrate deprecated Jotai loadable() to unwrap()
- Replace isolated createStore()+Provider with useHydrateAtoms on root store
- Remove unnecessary atomWithStorage SSR guards (handled by Jotai internally)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:33:27 -05:00
jakeandClaude Opus 4.6 59e9dd6775 Add SWR for system health and command palette search fetching
Replace manual fetch/useState/useEffect patterns with SWR hooks in
system health section and command palette search, gaining automatic
caching, request deduplication, and stale-while-revalidate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 12:24:23 -05:00
jake 107eb9a9e7 Overhaul system health section with job trigger controls and live timestamps
Rebuild the background jobs card as a sortable table showing each job's
schedule, last run time (live-updating via a new useTimeAgo hook), last
duration, and a manual trigger button backed by a new POST
/api/admin/jobs/trigger route. Extract StatusDot into a shared
component. Add cronToHuman() to display schedule patterns as readable
strings (e.g. "Every 6h", "Daily at 03:00"). Replace static
formatDistanceToNow calls throughout the health section with a
LiveTimeAgo component that refreshes every 30 seconds. Also swap a
handful of section icons for better visual matches across settings cards.
2026-03-05 11:52:37 -05:00
jakeandClaude Opus 4.6 b474d07d97 Fix Emby icon SVG to use updated Simple Icons path
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 10:36:53 -05:00
jakeandClaude Opus 4.6 a5791b150f Add version update check feature for admin users
Checks current version against latest GitHub release every 6 hours via
cron job, caches result in appSettings, and surfaces updates through a
toast notification (once per browser session) and an animated badge in
the settings footer. Includes an admin toggle to enable/disable checks
(enabled by default). Also renames ServerSection to RegistrationSection
now that update checks are in their own card.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 10:36:05 -05:00
jake dfc0f67527 Migrate component state to Jotai atoms across settings, explore, and dashboard
Replace ad-hoc useState/useEffect fetch patterns with Jotai atoms and
loadables in StatsDisplay, FilterableTitleRow, BackupScheduleSection,
IntegrationsSection, and CommandPalette. Each component now gets a
scoped Jotai Provider with a pre-initialized store so server-rendered
initial values hydrate correctly. Async data fetching moves into
atom-level loadables, eliminating manual loading flags and cancellation
logic throughout.
2026-03-05 10:09:47 -05:00
jakeandClaude Opus 4.6 bafa45ec79 Add Emby webhook integration and remove unused username field
Add Emby as a third media server integration alongside Plex and Jellyfin.
Emby webhooks use a similar payload format (JSON with nested Item object
and ProviderIds). Also removes the mediaServerUsername field from all
webhook connections since it was never used for authentication — the
token-in-URL is the sole auth mechanism. This simplifies the connection
UX to a single "Connect" button.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 00:50:32 -05:00
jakeandClaude Opus 4.6 bcf9a3c751 Add quick-add watchlist button and consolidate server actions into lib/actions
Move all server actions from scattered page-level files into shared lib/actions/
directory (settings.ts, titles.ts, watchlist.ts) so they can be reused across
the app. Add a hover-triggered plus button on explore page title cards that lets
users add titles to their watchlist without navigating away.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 21:44:23 -05:00
jakeandClaude Opus 4.6 7a64ced5e0 Add system health dashboard and split settings into separate cards
Add admin-only system health API and UI showing database stats, TMDB
connection status, background job history, storage usage, and environment
variables (with redacted secrets). Split monolithic backup card into
three focused cards (backups, schedule, restore) with dedicated section
headers for Server, Security, and Backups.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 18:30:22 -05:00
jakeandClaude Opus 4.6 0a0e7dfc47 Add unlimited option to backup retention setting
- Add "unlimited" (value 0) to the keeping-last dropdown
- Skip pruning when retention is set to unlimited
- Show toast confirmation when changing retention
- Update description to read "Keeping unlimited backups" or "Keeping last N backups"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 16:31:51 -05:00
jakeandClaude Opus 4.6 0794bca301 Add next backup countdown and download action to backup toasts
- Show "Next backup in X" in scheduled backups description
- Add download button to manual backup success toast
- Remove redundant schedule description from header
- Reorder day-of-week picker above time picker for weekly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 16:28:50 -05:00
jakeandClaude Opus 4.6 6cfe3a0439 Add configurable backup schedule with admin UI
- Make backup frequency (6h/12h/1d/7d), time, and day-of-week
  configurable via settings, stored in appSettings key-value table
- Add rescheduleBackup() to dynamically update cron job at runtime
- Differentiate manual (sofa-manual-*) vs scheduled (sofa-scheduled-*)
  backup filenames with source icons and tooltips in the backup list
- Pruning now only targets scheduled backups, leaving manual ones intact
- Merge backup-actions.ts into actions.ts, use shadcn DropdownMenu for
  all selectors (consistent with dashboard stats cards)
- Fix hydration mismatch on relative backup timestamps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:43:40 -05:00
jakeandClaude 83456c6a21 Migrate from Node.js built-ins to Bun-native APIs (#1)
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-04 12:49:38 -05:00
jakeandClaude Opus 4.6 7d69262d0e Polish settings UI, add tooltips, replace timeAgo with date-fns
Clean up settings section design: consolidate backup cards, add
hover-reveal actions with tooltips, improve icon sizing across
components, and replace custom timeAgo helper with date-fns
formatDistanceToNow in webhook card.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 20:23:52 -05:00
jakeandClaude Opus 4.6 b90129a981 Fix hydration mismatch and nativeButton warning in backup section
- Use date-fns format() instead of toLocaleDateString() for deterministic
  server/client date rendering
- Set nativeButton={false} on download link Button to suppress base-ui warning

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 20:15:29 -05:00
jakeandClaude Opus 4.6 e59364e126 Add database backup/restore feature with admin UI
- Backup service using VACUUM INTO for WAL-safe atomic snapshots
- Server-side storage in DATA_DIR/backups with download/upload API routes
- Restore with integrity validation and automatic pre-restore safety backup
- Scheduled daily backups via cron with configurable retention
- Admin-only settings UI consolidated under single Server section
- Clean up account section sign-out button, fix switch sub-pixel rendering

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 20:01:10 -05:00
jakeandClaude Opus 4.6 d9b408128b Remove unnecessary await/async from sync bun:sqlite db calls
drizzle-orm/bun-sqlite is fully synchronous — all queries return values
directly, not promises. Remove await from all db calls, drop async from
functions that no longer need it, simplify Promise.all patterns that
wrapped sync operations, and fix setSetting() which was missing .run()
(previously masked by await triggering execution via thenable).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 19:04:40 -05:00
jake ecc6a942a9 Move media server icons into settings and polish webhook card
Co-locate PlexIcon/JellyfinIcon under settings/_components/icons.tsx.
Update Plex icon to CoreUI Brands SVG with corrected viewBox. Add
animated height transitions to collapsible panels, fix open-state
padding, and turn the Plex Pass notice into a linked external reference
with an inline icon.
2026-03-03 12:49:23 -05:00
jakeandClaude Opus 4.6 03917e0a1d Add version and commit info footer to settings page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 12:17:36 -05:00
jakeandClaude Opus 4.6 f6ee32ca1e Add confirmation toasts to settings actions and fix webhook card spacing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 11:10:28 -05:00
jakeandClaude Opus 4.6 8487c3d7b1 Convert settings page to server component with server actions
Replace client-side data fetching with server-side queries and server
actions, eliminating 3 API route files. Extract page into granular
client components (account, integrations, server, webhook card) with
optimistic updates. Rename sections: Media Servers → Integrations,
Administration → Server. Add admin badge to account section.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 09:32:18 -05:00