Items 1-5 of the agreed frontend upgrades. The one-time Prettier reformatting of files not touched here is in the FOLLOW-UP commit so this one stays reviewable. Testing (vitest + @testing-library) - vitest 3 + @testing-library/react + jsdom. test / test:watch / test:ui scripts; setup.ts stubs matchMedia + ResizeObserver for AntD under jsdom. - 15 tests, 4 files: RequireRole, LoginPage, measurements API client, extractApiError. Route-level code splitting - Every page converted to React.lazy() behind a Suspense boundary in App.tsx. Main bundle 1490 KB -> 714 KB; ECharts isolated in the DashboardPage chunk and only fetched when a dashboard is opened. Global 401 handling - api/client.ts response interceptor redirects any 401 from a non-/auth path to /login?returnTo=<current>; LoginPage honours the param. - Shared extractApiError replaces four near-identical per-page copies (UsersPage, AdminCustomersPage, MyProfilePage, TariffDrawer). Query devtools - @tanstack/react-query-devtools, lazy + import.meta.env.DEV-gated so a production build dead-code-eliminates it. Verified: no devtools code in any shipped .js chunk. ESLint + Prettier - ESLint 9 flat config (js + typescript-eslint + react-hooks + react-refresh, eslint-config-prettier last). Prettier config tuned to the existing style. lint / lint:fix / format / format:check scripts + lint-staged config. - Fixed one real react-hooks/exhaustive-deps warning (useMemo on DashboardsPage's `dashboards`). - End state: eslint 0 problems, tsc clean, 15/15 tests pass. Pre-commit hook (lint-staged) is installed locally in .git/hooks/ -- not version-controlled, see follow-up note. Husky was deliberately not used: it needs a core.hooksPath change and the monorepo layout makes it awkward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
56 lines
1.8 KiB
JavaScript
56 lines
1.8 KiB
JavaScript
import js from '@eslint/js';
|
|
import globals from 'globals';
|
|
import reactHooks from 'eslint-plugin-react-hooks';
|
|
import reactRefresh from 'eslint-plugin-react-refresh';
|
|
import tseslint from 'typescript-eslint';
|
|
import prettier from 'eslint-config-prettier';
|
|
|
|
// ESLint 9 flat config. Order matters: `prettier` must come last so it can
|
|
// switch off any stylistic rules that would fight the formatter — ESLint owns
|
|
// code-correctness, Prettier owns formatting.
|
|
export default tseslint.config(
|
|
{ ignores: ['dist', 'node_modules', 'coverage'] },
|
|
{
|
|
files: ['**/*.{ts,tsx}'],
|
|
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
|
languageOptions: {
|
|
ecmaVersion: 2022,
|
|
globals: globals.browser,
|
|
},
|
|
plugins: {
|
|
'react-hooks': reactHooks,
|
|
'react-refresh': reactRefresh,
|
|
},
|
|
rules: {
|
|
...reactHooks.configs.recommended.rules,
|
|
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
|
|
},
|
|
},
|
|
{
|
|
// Test + setup files also touch Node globals (process, etc.) and use
|
|
// jsdom; allow both global sets.
|
|
files: ['**/*.test.{ts,tsx}', 'src/test/**/*.ts'],
|
|
languageOptions: {
|
|
globals: { ...globals.browser, ...globals.node },
|
|
},
|
|
},
|
|
{
|
|
// Context files intentionally export a Provider component *and* its hook
|
|
// from the same module — the standard React Context pattern. The
|
|
// react-refresh rule flags this; the trade-off (marginally less optimal
|
|
// HMR) isn't worth splitting every context into two files.
|
|
files: ['src/hooks/**/*.tsx'],
|
|
rules: {
|
|
'react-refresh/only-export-components': 'off',
|
|
},
|
|
},
|
|
{
|
|
// Config files run in Node.
|
|
files: ['*.config.{js,ts}'],
|
|
languageOptions: {
|
|
globals: globals.node,
|
|
},
|
|
},
|
|
prettier,
|
|
);
|