Skip to content

a list of issues from migrate to tanstack #27

@chiptus

Description

@chiptus

6 GitHub Issues to Create
Issue 1: Use z.coerce for query string schema fields
Title: Use z.coerce for query string schema fields
Labels: bug, type-safety

Body:

Priority: High (data integrity issue)
Related: Copilot review - src/lib/searchSchemas.ts lines 17-22

Problem

filterSortSearchSchema uses z.number() and z.boolean() for query-string values (e.g. minRating, use24Hour, sortLocked). TanStack Router's default search parsing yields strings, so these fields will fail validation on page load/deep links and fall back to the .catch(...) defaults. Values don't round-trip correctly via the URL.

Current Code

minRating: z.number().catch(0),
use24Hour: z.boolean().catch(true),
sortLocked: z.boolean().catch(false),

Solution
Use z.coerce.number() and z.coerce.boolean() so values are properly parsed from query strings:

minRating: z.coerce.number().catch(0),
use24Hour: z.coerce.boolean().catch(true),
sortLocked: z.coerce.boolean().catch(false),

Testing
Navigate to sets page with query params: ?minRating=3&use24Hour=false
Verify params are preserved in URL
Verify filter state reflects the query params
Navigate away and back - params should persist
Files
src/lib/searchSchemas.ts

---

## Issue 2: Fix schedule route schema mismatch causing param loss

**Title:** Fix schedule route schema mismatch causing param loss  
**Labels:** `bug`, `routing`

**Body:**
```markdown
**Priority:** High (data loss)  
**Related:** Copilot review - `src/routes/festivals/$festivalSlug/editions/$editionSlug/schedule.tsx` lines 8-16

## Problem
The `/schedule` route validates search params with `filterSortSearchSchema`, but its children (`/schedule/timeline` and `/schedule/list`) validate with `timelineSearchSchema`. When redirecting from `/schedule` to `/schedule/timeline`, `location.search` will only include keys accepted by the parent route's `validateSearch`, so timeline-specific params like `day`, `time`, `view` can be dropped if someone lands on `/schedule?day=2024-06-15`.

### Current Code
```typescript
export const Route = createFileRoute(
  "/festivals/$festivalSlug/editions/$editionSlug/schedule",
)({
  component: ScheduleTab,
  validateSearch: filterSortSearchSchema, // ❌ Wrong schema
  beforeLoad: ({ params, location }) => {
    if (location.pathname.endsWith("/schedule")) {
      throw redirect({
        to: "/festivals/$festivalSlug/editions/$editionSlug/schedule/timeline",
        params,
        search: location.search as Record<string, unknown>,
      });
    }
  },
});

Solution
Switch to timelineSearchSchema so the redirect preserves all timeline search params:

export const Route = createFileRoute(
  "/festivals/$festivalSlug/editions/$editionSlug/schedule",
)({
  component: ScheduleTab,
  validateSearch: timelineSearchSchema, // ✅ Use child schema
  beforeLoad: ({ params, location }) => {
    if (location.pathname.endsWith("/schedule")) {
      throw redirect({
        to: "/festivals/$festivalSlug/editions/$editionSlug/schedule/timeline",
        params,
        search: location.search as Record<string, unknown>,
      });
    }
  },
});

Testing
Navigate directly to /schedule?day=2024-06-15&view=timeline
Verify redirect preserves all query params
Verify timeline displays the correct day/view
Files
src/routes/festivals/$festivalSlug/editions/$editionSlug/schedule.tsx

---

## Issue 3: Fix missing MouseEvent type import in FestivalSelection

**Title:** Fix missing MouseEvent type import in FestivalSelection  
**Labels:** `bug`, `type-safety`

**Body:**
```markdown
**Priority:** Medium (type checking failure)  
**Related:** Copilot review - `src/pages/FestivalSelection.tsx` lines 104-111

## Problem
`handleClick` uses the `React.MouseEvent` type, but this file doesn't import the `React` namespace (only `useEffect`). With the modern JSX transform, `React` isn't in scope automatically, so this will fail typechecking.

### Current Code
```typescript
import { useEffect } from "react"; // No React namespace import

function handleClick(e: React.MouseEvent) { // ❌ React not in scope
  // ...
}

Solution
Import the needed type:

import { useEffect, type MouseEvent } from "react";

function handleClick(e: MouseEvent) {
  // ...
}

Files
src/pages/FestivalSelection.tsx

---

## Issue 4: Simplify FestivalEditionProvider error handling

**Title:** Simplify FestivalEditionProvider error handling  
**Labels:** `enhancement`, `ux`, `cleanup`

**Body:**
```markdown
**Related:** Copilot review comment #7

## Problem
`FestivalEditionProvider` has error handling code that:
1. Renders error UI
2. Navigates away via useEffect
3. Causes flash before redirect
4. Is likely unreachable due to route-level preloading in `beforeLoad`

The edition is preloaded in the route's `beforeLoad` via `ensureQueryData`. If preload fails, the route never renders (loader throws). The provider's error handling is unreachable in practice.

## Solution
- Remove `useEffect` that navigates on error (lines 53-62 in `FestivalEditionContext.tsx`)
- Remove error UI rendering (lines 64-77)
- Keep simple loading check: `if (editionQuery.isLoading) return <LoadingFallback />`
- Let route-level error boundaries handle actual errors

## Files
- `src/contexts/FestivalEditionContext.tsx`

Issue 5: Move EditionSelection auto-redirect to beforeLoad
Title: Move EditionSelection auto-redirect to beforeLoad
Labels: enhancement, ux

Body:

**Related:** Copilot review comment #8

## Problem
`EditionSelection` component renders "Redirecting..." state then navigates via `useEffect`, causing a flash before redirect when there's only one edition.

## Solution
Move redirect logic to route's `beforeLoad`:
1. Fetch editions via `queryClient.ensureQueryData`
2. If exactly 1 edition exists, `throw redirect({ ... })`
3. Component never renders when redirecting - no flash

### Implementation
```typescript
// In route file: src/routes/festivals/$festivalSlug/index.tsx
beforeLoad: async ({ params, context }) => {
  const editions = await context.queryClient.ensureQueryData({
    queryKey: editionsKeys.forFestival(params.festivalSlug),
    queryFn: () => fetchEditionsForFestival(params.festivalSlug),
  });
  
  if (editions.length === 1) {
    throw redirect({
      to: "/festivals/$festivalSlug/editions/$editionSlug/sets",
      params: { festivalSlug: params.festivalSlug, editionSlug: editions[0].slug },
    });
  }
}

Remove useEffect redirect from EditionSelection.tsx component.

Files
Create/update: src/routes/festivals/$festivalSlug/index.tsx
Update: src/pages/EditionSelection.tsx

---

## Issue 6: Move FestivalSelection auto-redirect to beforeLoad

**Title:** Move FestivalSelection auto-redirect to beforeLoad  
**Labels:** `enhancement`, `ux`

**Body:**
```markdown
**Related:** Copilot review comment #9

## Problem
`FestivalSelection` has two issues:
1. `useEffect` calls `handleFestivalClick` but doesn't include it in deps (ESLint warning)
2. Duplicate subdomain logic in both effect and `FestivalCard.handleClick`
3. Renders content before redirecting when there's only one festival

## Solution
Move redirect logic to route `beforeLoad`:
1. Fetch festivals via `queryClient.ensureQueryData`
2. If exactly 1 festival exists, handle redirect there
3. Component never renders when redirecting - no flash

### Implementation
```typescript
// In route file: src/routes/index.tsx
beforeLoad: async ({ context }) => {
  const festivals = await context.queryClient.ensureQueryData({
    queryKey: festivalsKeys.all(),
    queryFn: () => fetchFestivals(),
  });
  
  if (festivals.length === 1) {
    // Fetch editions for the single festival
    const editions = await context.queryClient.ensureQueryData({
      queryKey: editionsKeys.forFestival(festivals[0].slug),
      queryFn: () => fetchEditionsForFestival(festivals[0].slug),
    });
    
    if (editions.length === 1) {
      throw redirect({
        to: "/festivals/$festivalSlug/editions/$editionSlug/sets",
        params: { 
          festivalSlug: festivals[0].slug, 
          editionSlug: editions[0].slug 
        },
      });
    } else {
      throw redirect({
        to: "/festivals/$festivalSlug",
        params: { festivalSlug: festivals[0].slug },
      });
    }
  }
}

Remove useEffect from FestivalSelection.tsx, keep click handler for user-initiated clicks.

Files
Update: src/routes/index.tsx
Update: src/pages/FestivalSelection.tsx

---

**Recommendation:** Create issues #1-3 first (high priority bugs), then #4-6 (UX improvements).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions