Persist dashboard periods and explore genre filters in URL via nuqs

- Add `nuqs` dependency; define `dashboardSearchParams` and
  `exploreSearchParams` loaders in new `search-params.ts` files
- Thread `moviePeriod`/`episodePeriod` from URL into `StatsSection`
  and `StatsDisplay`, replacing local `useState` with `useQueryStates`
- Rename `getUserStats` fields `moviesThisMonth`/`episodesThisWeek` →
  `movieCount`/`episodeCount` and accept period args so SSR stats
  match the active URL params
- Pre-fetch genre-filtered results server-side on the explore page
  when `movieGenre`/`tvGenre` params are present; pass as
  `initialGenreItems` to `FilterableTitleRow` so first paint is
  already populated
- Render genre chips as `<Link>` elements (with click-handler
  fallback) so genre selections produce shareable, bookmarkable URLs
This commit is contained in:
2026-03-08 14:32:09 -04:00
parent 0838e5fb74
commit 2bb5af042b
12 changed files with 213 additions and 70 deletions
+4 -4
View File
@@ -122,8 +122,8 @@ describe("getUserStats", () => {
insertStatus("user-1", titleId, "in_progress");
const stats = getUserStats("user-1");
expect(stats.moviesThisMonth).toBe(2);
expect(stats.episodesThisWeek).toBe(1);
expect(stats.movieCount).toBe(2);
expect(stats.episodeCount).toBe(1);
expect(stats.librarySize).toBe(3);
expect(stats.completed).toBe(2);
});
@@ -131,8 +131,8 @@ describe("getUserStats", () => {
test("returns zeros when no data", () => {
insertUser();
const stats = getUserStats("user-1");
expect(stats.moviesThisMonth).toBe(0);
expect(stats.episodesThisWeek).toBe(0);
expect(stats.movieCount).toBe(0);
expect(stats.episodeCount).toBe(0);
expect(stats.librarySize).toBe(0);
expect(stats.completed).toBe(0);
});
+11 -7
View File
@@ -144,15 +144,19 @@ export function getWatchHistory(
}
export interface DashboardStats {
moviesThisMonth: number;
episodesThisWeek: number;
movieCount: number;
episodeCount: number;
librarySize: number;
completed: number;
}
export function getUserStats(userId: string): DashboardStats {
const moviesThisMonth = getWatchCount(userId, "movies", "this_month");
const episodesThisWeek = getWatchCount(userId, "episodes", "this_week");
export function getUserStats(
userId: string,
moviePeriod: TimePeriod = "this_month",
episodePeriod: TimePeriod = "this_week",
): DashboardStats {
const movieCount = getWatchCount(userId, "movies", moviePeriod);
const episodeCount = getWatchCount(userId, "episodes", episodePeriod);
const [statusCounts] = db
.select({
@@ -164,8 +168,8 @@ export function getUserStats(userId: string): DashboardStats {
.all();
return {
moviesThisMonth,
episodesThisWeek,
movieCount,
episodeCount,
librarySize: statusCounts?.librarySize ?? 0,
completed: statusCounts?.completed ?? 0,
};