Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/test-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

jobs:
lint:
name: Lint Frontend
name: Lint & Format Frontend
runs-on: ubuntu-latest
defaults:
run:
Expand All @@ -26,9 +26,12 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Run ESLint
- name: Run lint
run: npm run lint

- name: Check formatting
run: npm run format:check

- name: Run TypeScript check
run: npx tsc --noEmit

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
29 changes: 18 additions & 11 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ All frontend commands run from `frontend/`:
cd frontend
npm run dev # Start Next.js dev server
npm run build # Production build
npm run lint # ESLint 9 (flat config)
npm run lint # Biome lint
npm run format # Biome format
npm run check # Biome lint + format
```

Docker-based dev environment (requires mise):
Expand All @@ -37,7 +39,7 @@ mise run stop # Stop dev containers and remove volumes

**Routing**: Next.js App Router (`app/`). Key pages: `(default)/page.tsx` (landing), `(map)/map/page.tsx` (interactive map), `(default)/debug/page.tsx` (env debug).

**Styling**: Mantine UI 7.x component library + CSS Modules. PostCSS configured with `postcss-preset-mantine`. Custom theme defined in `theme.ts` with project colors (primary orange `rgb(233 79 43)`, secondary green `rgb(155 185 98)`).
**Styling**: Mantine UI 8.x component library + CSS Modules. PostCSS configured with `postcss-preset-mantine`. Custom theme defined in `theme.ts` with project colors (primary orange `rgb(233 79 43)`, secondary green `rgb(155 185 98)`).

**State Management**: React Context API — `FormContext` (form/map interaction state), `NavbarContext` (sidebar/drawer toggle), `ClientIdContext` (anonymous client fingerprinting).

Expand Down Expand Up @@ -144,7 +146,7 @@ const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { ... }

**Form Data**: Use `z.infer<typeof schema>` for React Hook Form + Zod type extraction

**Type Imports**: Always use separate `import type` statements for type-only imports. This is enforced by both `verbatimModuleSyntax` in tsconfig and `@typescript-eslint/consistent-type-imports` ESLint rule:
**Type Imports**: Always use separate `import type` statements for type-only imports. This is enforced by both `verbatimModuleSyntax` in tsconfig and Biome `style/useImportType` rule:
```typescript
// Correct — separate import type statement
import { useModals } from '@mantine/modals'
Expand All @@ -162,19 +164,24 @@ import { ContextModalProps, useModals } from '@mantine/modals'
- For complex types, use `unknown` and narrow with type guards, or define proper interfaces
- For truly dynamic data, use `Record<string, unknown>` instead of `any`
- Third-party library types should use their exported types or be properly typed
- Enforced by ESLint `@typescript-eslint/no-explicit-any` rule
- Enforced by Biome `suspicious/noExplicitAny` rule

**When Adding New Code**:
1. Run `npx tsc --noEmit` to verify types before committing
2. Run `npm run lint` to check ESLint rules
2. Run `npm run check` to check Biome lint and formatting
3. NEVER use explicit `any` - see "Explicit `any` is Forbidden" section above
4. Import shared types from `@/types` using `import type` syntax
5. Add return types to exported functions and React components

## Linting
## Linting & Formatting

**ESLint 9** with flat config (`eslint.config.mjs`). Key rules:
- `@typescript-eslint/no-explicit-any: error` — no `any` types
- `@typescript-eslint/consistent-type-imports: error` — enforce `import type` on separate lines
- `@typescript-eslint/no-unused-vars: warn` — unused variables (prefix with `_` to ignore)
- `react-hooks/exhaustive-deps` — complete dependency arrays in hooks
**Biome 2.x** (`biome.json`) handles both linting and formatting. CSS files are excluded (PostCSS variables not supported by Biome).

Key lint rules:
- `suspicious/noExplicitAny: error` — no `any` types
- `style/useImportType: error` — enforce `import type` on separate lines
- `correctness/noUnusedVariables: warn` — unused variables (prefix with `_` to ignore)
- `correctness/noUnusedImports: error` — remove unused imports
- `correctness/useExhaustiveDependencies: warn` — complete dependency arrays in hooks

Formatter config: 4-space indent, double quotes, no semicolons, 120 char line width.
2 changes: 1 addition & 1 deletion frontend/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
BACKEND_URL=http://127.0.0.1:8000/api
BACKEND_URL=http://127.0.0.1:8080
NEXT_PUBLIC_MAPLIBRE_STYLE=https://127.0.0.1:8000/style.json
NEXT_PUBLIC_MAPBOX_STYLE=mapbox://styles/mapbox/standard
NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN=XXX
40 changes: 0 additions & 40 deletions frontend/README.md

This file was deleted.

60 changes: 60 additions & 0 deletions frontend/biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.14/schema.json",
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 4,
"lineWidth": 120
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"jsxQuoteStyle": "double",
"semicolons": "asNeeded"
}
},
"css": {
"linter": {
"enabled": false
},
"formatter": {
"enabled": false
}
},
"linter": {
"enabled": true,
"rules": {
"suspicious": {
"noExplicitAny": "error",
"noArrayIndexKey": "off",
"noShadowRestrictedNames": "off"
},
"security": {
"noDangerouslySetInnerHtml": "off"
},
"style": {
"useImportType": "error",
"noNonNullAssertion": "off"
},
"correctness": {
"noUnusedVariables": "warn",
"noUnusedImports": "error",
"useExhaustiveDependencies": "warn"
},
"a11y": {
"noSvgWithoutTitle": "off",
"useGenericFontNames": "off"
}
}
},
"assist": {
"actions": {
"source": {
"organizeImports": "on"
}
}
},
"files": {
"includes": ["src/**", "!**/*.css"]
}
}
36 changes: 0 additions & 36 deletions frontend/eslint.config.mjs

This file was deleted.

Loading