Skip to content

fix: clear the lint backlog, gate lint in CI - #138

Merged
goetchstone merged 5 commits into
mainfrom
lint/backlog
Oct 27, 2026
Merged

goetchstone merged 5 commits into
mainfrom
lint/backlog

Conversation

@goetchstone

Copy link
Copy Markdown
Owner

Why

npm run lint crashed on main for months, and CI never ran it. The ESLint upgrade in #134 made it run again, and it found 20 errors and 4 warnings. One of them was hiding a real bug.

The bug

/dashboard/invoices/[id]/edit called redirect() inside a try whose catch called notFound(). Both work by throwing, so opening the edit page for any non-draft invoice showed a 404 instead of redirecting to the invoice. Confirmed on main's production image: 404. On this branch: 307 to /dashboard/invoices/<id>.

Changes

Rule Fix
react-hooks/error-boundaries (8, in 4 detail pages) await call().catch(() => notFound()), then branch and render outside the catch. Missing or malformed IDs still 404.
react-hooks/set-state-in-effect (availability editor ×2) The selected service is derived (picked || first). Edits live in a draft tagged with the fetch it came from, so a refetch after save or a service switch shows the server's hours without an effect.
react-hooks/set-state-in-effect (useIsMobile) Deleted hooks/use-mobile.ts and its only caller, components/ui/sidebar.tsx (723 lines), a scaffold component nothing imports.
@next/next/no-html-link-for-pages (5) next/link on contact, support/track, terms, and the consent banner.
@next/next/no-html-link-for-pages (3, CSV exports) Added download. These are /api routes that send Content-Disposition: attachment, and client routing would be wrong for them.
react/no-unescaped-entities, 2× unused vars Fixed; saveFile drops its unused mimeType parameter.
@next/next/no-img-element (2 warnings) Turned off in eslint.config.mjs. The image optimizer is disabled (DECISIONS 2026-09-30), so next/image would render the same <img>.

No eslint-disable comments were added.

Gate: the lint script is now eslint --max-warnings=0, and CI runs it in the test job after type-check. CLAUDE.md, the FRAMEWORK pre-commit checklist, and /boot now include it. A LESSONS entry records the redirect bug.

Verification (Node 24.22.1, production image)

  • npm run lint exits 0. tsc, vitest (124/124), and the build pass. The full end-to-end regression passes: pages, login, PDFs, lead emails.
  • Detail pages with real CUIDs:
    • draft invoice: edit page 200
    • sent invoice: edit page 307 to the invoice
    • invoice, client, and ticket detail: 200
    • missing or garbage IDs: 404
  • Availability editor in a real browser:
    • loads the saved Mon–Fri 9–5
    • Saturday opened, Monday closed, and a start time changed all held; Save showed "Saved"
    • the rows in the database matched, and a reload showed them
    • an end-before-start time shows the warning and disables Save
  • Links: contact "Book now" goes to /book; terms "Privacy Policy" goes to /privacy. The closed-ticket page renders its /support link, and the export links carry download. No console errors.

🤖 Generated with Claude Code

redirect() threw inside a try whose catch called notFound(). Fetch
with .catch(notFound) and render outside, as react-hooks requires.
The selected service and editable rows come from query data; no
effects copying it into state.
Internal links use next/link; CSV exports are download links.
@goetchstone
goetchstone merged commit 7b3b426 into main Oct 27, 2026
8 checks passed
@goetchstone
goetchstone deleted the lint/backlog branch October 27, 2026 10:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant