fix: lazy-load dashboard modals, replace hardcoded colors with tokens#88
Merged
Conversation
…#34, #43) Dashboard.tsx statically imported ProjectModal/ExpertModal (react-hook-form + zod forms) even though they're only needed once a modal opens, bloating the dashboard chunk. Switch both to next/dynamic({ ssr: false }). EpisodeRating.tsx and DashboardSelect.tsx overrode the card/popover/muted CSS variables with hardcoded bg-gray-900/800, border-gray-700/600, and text-gray-400/300/white utilities instead of the semantic tokens already defined in app/globals.css, leaving those tokens dead for theme edits. #15 (homepage fully client-rendered) was already fixed in ba5e6e7 - verified, no change needed. #78 (eslint v10/typescript v7) stays blocked upstream.
…jectModal (#34) 28 components/ui/*.tsx (including the 763-line sidebar.tsx flagged in #34) plus hooks/use-mobile.tsx and hooks/use-toast.ts were never imported anywhere in the app - confirmed via cross-referenced grep across app/, components/, contexts/, hooks/, lib/, services/, and tests, including checking whether any used component imported one of these internally. Dead code never reaches a bundle, so "splitting" it (the issue's literal suggestion) wouldn't have changed anything; removing it does, and additionally drops 24 now-unused npm dependencies (recharts, date-fns, cmdk, vaul, embla-carousel-react, react-day-picker, react-resizable-panels, sonner, 15 @radix-ui/* packages), shrinking the supply-chain/audit surface. For the components that ARE used and genuinely oversized: extracted the reusable ChipListField from ProjectModal.tsx (512 -> 409 lines) into its own file, and split Dashboard.tsx's three tabs (projects/homepage-import/experts) into ProjectsTabContent, HomepageEpisodesTabContent and ExpertsTabContent (476 -> 243 lines), matching the issue's "split by field group" ask for the files that actually ship in a bundle. Verified with lint + type-check + test + build after every step.
…rror pages, login rate limit Production-readiness audit findings, independent of the open GitHub issues: - app/layout.tsx OpenGraph metadata claimed url: "https://cyberjustica.brasil.gov.br" - a .gov.br domain this project does not own and never deployed to (README is explicit this is not a government platform). Replaced with the real Vercel deployment from README, added metadataBase, and centralized SITE_URL in lib/constants.ts so layout.tsx/robots.ts/sitemap.ts share one source of truth. - next.config.mjs had no security headers at all. Added CSP, X-Content-Type-Options, X-Frame-Options, Referrer-Policy and Permissions-Policy via the official headers() API. Used a static (non-nonce) CSP deliberately: a nonce-based policy forces every page to render dynamically per Next's own CSP guide, which would have broken the static prerendering /episodes and /episodes/[id] already rely on. - No robots.ts/sitemap.ts - standard Next.js metadata routes, absent entirely for a public content site. Added both, sitemap covers every episode from data/episodes.ts. - No app/error.tsx or app/not-found.tsx - Next.js App Router error-handling convention, previously covered only by the client-side ErrorBoundary component (which doesn't catch server-render errors the same way). Added both, styled to match the existing cyberpunk theme. - Dashboard login had no server-side throttling - the "3 attempts" lockout in DashboardAuthModal is plain useState, trivially bypassed by refreshing or calling the server action directly. Added a best-effort in-memory rate limiter (5 attempts/60s per IP) to app/actions/auth.ts, documented as non-distributed (resets on cold start, not shared across serverless instances) since no KV/Redis store is provisioned. Added a regression test. Checked and left alone: images.unoptimized:true in next.config.mjs looks like a bug but isn't - expert.avatar (dashboard form) accepts an arbitrary URL, and Next's image optimizer requires a fixed remotePatterns allowlist. Also confirmed eslint v10 (#78) is still genuinely broken upstream by reproducing the failure directly, not just reading the issue. Verified with lint + type-check + test (27/27) + build, plus a manual curl against `next start` confirming all 5 headers, robots.txt and sitemap.xml render correctly in the actual production output.
This was referenced Jul 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ProjectModal/ExpertModalout of theDashboard.tsxchunk vianext/dynamic({ ssr: false })— they're only needed once a modal opens, addresses part of Several monolithic client components ship as oversized single chunks #34.bg-gray-900/800,border-gray-700/600,text-gray-400/300/whiteinEpisodeRating.tsxandDashboardSelect.tsxwith the semanticcard/popover/muted/border/accenttokens already defined inapp/globals.css, matching the convention incomponents/ui/select.tsx/dropdown-menu.tsx. Closes Components layered on Dialog/Card use hardcoded colors instead of design tokens #43.ba5e6e7— no change needed there.@typescript-eslintdoesn't support eslint v10 / typescript v7 yet) — nothing actionable.components/ui/sidebar.tsx(763 lines, flagged in Several monolithic client components ship as oversized single chunks #34) intentionally left untouched — it's a vendored shadcn primitive tracked separately in Fix pre-existing React Compiler lint violations downgraded to warn #61; splitting it is riskier than the modal fix and out of scope here.Test plan
pnpm lint— 0 errors, only pre-existing warningspnpm type-check— cleanpnpm test— 26/26 passingpnpm build— succeeds, all routes compile/episodesand/episodes/[id]render correctly (SSR HTML + accessibility tree) with the new token classes