Guidance for Claude Code working in this repository.
This is the Pandatech React + Vite template. It is the starting point for new frontend projects: clone, rename, prune, build. Replace this section with the actual project description after you derive a real project from the template.
- Node 25 (Docker builds use
node:25-alpine) - TypeScript 6.0.x
- Vite 8 (Rolldown bundler under the hood)
- React 19.2
- React Router 7 (data routers,
lazy:route loaders) - Redux Toolkit 2 (with RTK Query)
- Ant Design 6 +
@ant-design/icons6 - ESLint 9 (pinned, NOT 10)
- nginx 1.29 in production runtime
@types/nodetracks the Node major used in Docker
Three plugins block ESLint 10:
eslint-plugin-react@7.37(max ESLint 9.7)eslint-plugin-react-hooks@7.0(max ESLint 9)eslint-plugin-jsx-a11y@6.10(max ESLint 9)
Move them all together when they support 10. Do not bump ESLint individually.
typescript-eslint@8.58 peer is >=4.8.4 <6.1.0. So ^6.0.x is fine, ^6.1.x is not. Move both together.
npm run dev-- Vite dev server (HTTPS onreact.pandatech.it:5173if certs are present, plain HTTP otherwise)npm run build--tsc -b && vite buildnpm run preview-- preview the production buildnpm run lint-- ESLint on the whole treenpm run lint:fix-- ESLint with--fixnpm run format/format:check-- Prettiernpm run type-check--tsc --noEmit
Definition of "clean": npm run type-check, npm run build, and npm run lint must all exit 0 with zero warnings.
src/
├── api/ RTK Query base + endpoint slices
├── assets/ Static resources
├── components/ Reusable UI (incl. RouteErrorBoundary)
├── hooks/ Custom hooks (typed store hooks)
├── layouts/ Layout shells
├── pages/ Route components (lazy-loaded)
├── router/ Router config + path constants
├── store/ Redux store
├── styles/ Global CSS + antd theme
└── types/ Shared TS types
vite.config.ts auto-discovers every top-level subfolder of src/. When you add a new folder, also add the matching paths entry in tsconfig.app.json:
"newFolder": ["./src/newFolder"],
"newFolder/*": ["./src/newFolder/*"]Both must agree or imports will fail at build time.
src/main.tsxmounts Provider (Redux) → ConfigProvider (antd theme) → AntdApp (message/notification context) → Suspense (lazy-route fallback shows a centered<Spin>) → RouterProvider.src/router/router.tsxdeclares the routes. The root layout route has anerrorElement(components/RouteErrorBoundary) so route-level throws render a friendly page instead of blanking the app.- Routes use the React Router 7
lazy:loader pattern (async () => ({ Component })) so each page lands in its own chunk. src/router/paths.tsis the single source of truth for route paths. ImportRouterPathsrather than hard-coding strings.
src/store/store.tsregisters the single RTK Queryapireducer.setupListenersis wired so per-queryrefetchOnFocus/refetchOnReconnectopt-ins work.src/api/api.tsdefines thebaseQuery(with bearer token fromlocalStorage) andbaseQueryWithReauthwhich serializes refresh attempts via anasync-mutex. This is template scaffolding; swap in the real auth flow per project (see "Things to avoid" below for the security note onlocalStorage).src/api/exampleApi.tsis a placeholder showing theinjectEndpointspattern. Delete once real endpoints exist.- Add new feature endpoints with
api.injectEndpoints({...})in their own file undersrc/api/. The baseapislice ownstagTypes; declare invalidation tags there as the project grows. - Use
useAppDispatch/useAppSelectorfromhooks/storeHooksinstead of the untyped versions fromreact-redux.
- Declared in
src/vite-env.d.ts(currently onlyVITE_BASE_URL). src/api/api.tsthrows at module init ifVITE_BASE_URLis missing. There is no silent fallback. Add the env var to.env.localfor local dev, and to the CIbuild-args(or container env) for builds..env.localis git-tracked as the project's local-dev template; never put real secrets there. Other.env*variants are gitignored.
- Flat config:
eslint.config.js eslint-plugin-import-x(NOTeslint-plugin-import)- TS-aware lint via
typescript-eslint'srecommendedTypeChecked - React Compiler rules off (codebase has not been migrated)
no-consoleallowserrorandwarnonly- Prettier integrated via
eslint-plugin-prettier. The inline prettier options ineslint.config.jsmirror.prettierrc(includingendOfLine: "auto") so the CLI and ESLint agree on Windows checkouts. vite-plugin-checkersurfaces TS + ESLint errors in the dev overlay (useFlatConfig: true)
react-hooks/exhaustive-deps: off. Many components intentionally narrow effect deps; the rule's auto-fix is too eager. Re-evaluate per project if the codebase stays disciplined.react-hooks/immutability,refs,set-state-in-effect,preserve-manual-memoization,purity: off. These are React Compiler rules; opt in once the codebase is migrated.@typescript-eslint/no-unused-vars: off. TypeScript already enforces this vianoUnusedLocals/noUnusedParametersintsconfig.app.json. Avoiding double diagnostics.@typescript-eslint/no-explicit-any,no-unsafe-call,no-unsafe-assignment,no-unsafe-member-access: off. Practical concession for the antd / RTK Query type surface; tighten per project if domain code can hold the line.@typescript-eslint/no-floating-promises: off.react-routeruseNavigate()returns aPromise<void>; the codebase wraps it invoid navigate(...)instead of awaiting. Re-enable once you have a project-wide pattern.
- Dev server:
https://react.pandatech.it:5173(HTTPS auto-enabled whencert/*.pemfiles exist; seecert/README.md). - Production: multi-stage Docker,
node:25-alpinebuilds,nginx:1.29-alpineserves on port 3000. nginx.confhandles gzip, immutable 1-year cache for/assets/, no-cache forindex.html, SPA fallback, plus a baseline of security headers (CSP is intentionally NOT set; add per project).vite.config.tsmanualChunkssplitsantd-vendor,redux-vendor,react-vendor, and a genericvendorchunk so app code changes do not invalidate the long-cached vendor bundles.chunkSizeWarningLimitis 800 kB to absorb antd.
.github/workflows/ci.yml builds + pushes to the Pandatech Nexus registry on push to development, qa, staging, main. The development branch also kubectl applys kubernetes/deployment.yaml. Action versions are pinned: actions/checkout@v6, docker/login-action@v4, docker/build-push-action@v7. The Cleanup step is the canonical pattern with IMAGE_REF variable, [ -n ] guard, xargs -r. Per-branch env is set in the "Configure environment" step (VITE_BASE_URL, K8S_*, DEPLOYMENT_ENV).
Required secrets: nexus_username, nexus_password. Self-hosted runner (runs-on: self-hosted) with kubeconfig at /home/runner/.kube/config.
The Nexus registry is HTTP-only and whitelisted in the host docker daemon's insecure-registries. If you ever add another docker/setup-buildx-action@v4 step, you MUST set with: { driver: docker } so the buildx kit talks to the local daemon instead of spinning up a docker-container builder that bypasses the whitelist.
- Do NOT add
--legacy-peer-depsor--forceto install commands. If install fails, diagnose the peer-dep error and fix the version mismatch. - Do NOT bring back
vite-plugin-pwa,vite-plugin-eslint*,vite-plugin-chunk-split,vite-plugin-imagemin,eslint-plugin-import(useimport-x),react-quill(usereact-quill-new),@types/dompurify,@types/react-router-dom. - Do NOT add
baseUrltotsconfig.app.json. Usepathsonly. - Do NOT import Node built-ins in client code. They are only valid in
vite.config.ts(covered bytsconfig.node.json). - Do NOT use
forwardRef. React 19 takesrefas a regular prop. - Do NOT silently reformat the codebase to a different prettier style. Adjust the eslint config if the project's style differs from the template default.
- Do NOT skip git hooks (
--no-verify,--no-gpg-sign). - The token in
localStorage(seesrc/api/api.ts) is template scaffolding for cookie-less APIs. If your backend issues httpOnly cookies, swap the bearer header forcredentials: 'include'and delete thelocalStoragecalls before shipping; tokens inlocalStorageare XSS-readable.