Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion FILEMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ Quick-reference for "when you change X, also update Y" and "where does X live?"
| **`hooks.server.ts`** | All server responses (security headers, locale) |
| **`svelte.config.js`** (CSP) | Test that scripts/styles/images still load; Turnstile needs `script-src` + `frame-src` for `challenges.cloudflare.com` |
| **`app.html`** | FOUC prevention, nonce attribute, theme init |
| **`UserManagementCard.svelte`** | Thin shell β€” delegates to `RoleManagement.svelte` and `AccountActions.svelte` |
| **Component barrel `index.ts`** | All imports from that feature folder |
| **i18n keys** (rename/remove in `en.json`) | Same key in `cs.json`, all `m.{key}()` usages |
| **i18n keys** (add) | Add to both `en.json` and `cs.json` |
Expand All @@ -122,6 +123,8 @@ Quick-reference for "when you change X, also update Y" and "where does X live?"
| **`.npmrc`** (pnpm settings) | `Dockerfile`, `Dockerfile.local` (both COPY it), CI `--frozen-lockfile` behavior |
| **`package.json`** (scripts) | CI/CD references, CLAUDE.md pre-commit checks |
| **`src/test-setup.ts`** | All test files (provides global `$app/*` mocks; changes here affect every test) |
| **`src/test-utils.ts`** (shared test utilities) | All route-level test files that import `MOCK_USER`, `createMockLoadEvent`, `createMockCookies` |
| **`$lib/utils/jobs.ts`** (job formatting) | `JobTable.svelte`, `JobInfoCard.svelte`, `JobExecutionHistory.svelte` |
| **`vite.config.ts`** (`test` block) | vitest test runner config (include patterns, environment, setupFiles) |

### Cross-Stack Changes
Expand Down Expand Up @@ -190,7 +193,7 @@ src/frontend/src/
lib/components/ui/{component}/ (shadcn β€” generated)
lib/state/ {feature}.svelte.ts
lib/types/ index.ts (type aliases)
lib/utils/ ui.ts (cn()), permissions.ts, audit.ts, platform.ts, roles.ts
lib/utils/ ui.ts (cn()), permissions.ts, audit.ts, platform.ts, roles.ts, jobs.ts
messages/ en.json, cs.json
routes/(app)/ {feature}/+page.svelte, +page.server.ts
routes/(public)/ login/+page.svelte
Expand Down
17 changes: 15 additions & 2 deletions src/frontend/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ src/
β”‚ β”‚ β”œβ”€β”€ layout/ # Header, Sidebar, SidebarNav, UserNav, ThemeToggle, LanguageSelector, ShortcutsHelp
β”‚ β”‚ β”œβ”€β”€ profile/ # ProfileForm, ProfileHeader, AvatarDialog, AccountDetails, InfoItem
β”‚ β”‚ β”œβ”€β”€ settings/ # ChangePasswordForm, DeleteAccountDialog, ActivityLog
β”‚ β”‚ β”œβ”€β”€ admin/ # UserTable, RoleCardGrid, UserManagementCard, AuditTrailCard, ...
β”‚ β”‚ β”œβ”€β”€ admin/ # UserTable, UserDetailCards, UserManagementCard, RoleManagement, AccountActions, RoleCardGrid, RoleDetailsCard, RolePermissionsSection, RoleDeleteSection, JobTable, JobInfoCard, JobActionsCard, JobExecutionHistory, AuditTrailCard, ...
β”‚ β”‚ └── common/ # StatusIndicator, WorkInProgress
β”‚ β”œβ”€β”€ config/ # i18n.ts (client-safe), server.ts (server-only β€” never export from barrel)
β”‚ β”œβ”€β”€ state/ # .svelte.ts files only (cooldown, health, shake, theme, sidebar, shortcuts)
β”‚ β”œβ”€β”€ types/index.ts # Type aliases from API schemas
β”‚ └── utils/ # ui.ts (cn()), permissions.ts, audit.ts, platform.ts, roles.ts
β”‚ └── utils/ # ui.ts (cn()), permissions.ts, audit.ts, platform.ts, roles.ts, jobs.ts
β”œβ”€β”€ routes/
β”‚ β”œβ”€β”€ (app)/ # Authenticated (redirect guard)
β”‚ β”‚ └── admin/ # Permission-guarded per page
Expand Down Expand Up @@ -188,6 +188,10 @@ import { hasPermission, hasAnyPermission, Permissions } from '$lib/utils';
let canManage = $derived(hasPermission(data.user, Permissions.Users.Manage));
```

### Graceful Degradation for Secondary Fetches

When a page loads multiple API resources in parallel: primary entity failure throws (hard error), but secondary data failures (roles list, permissions list) return empty arrays with a `*LoadFailed` flag. Components must consume the flag and show an `Alert` warning so users understand why functionality is unavailable. See `users/[id]/+page.server.ts` for the pattern.

## i18n

Keys: `{domain}_{feature}_{element}` (e.g., `auth_login_title`, `profile_personalInfo_firstName`).
Expand Down Expand Up @@ -298,6 +302,10 @@ vi.mock('$app/state', () => ({
- **Per-file override:** add `// @vitest-environment jsdom` at the top of files that need DOM (component tests). This avoids making every test pay jsdom startup cost.
- **When to add `@testing-library/svelte`:** install it when writing the first Svelte component test, not before. It provides `render()`, `fireEvent()`, and DOM queries for `.svelte` files.

### Shared Test Utilities (`src/test-utils.ts`)

For route-level (server load function) tests, import `MOCK_USER`, `createMockLoadEvent`, and `createMockCookies` from `src/test-utils.ts` instead of duplicating mock setup.

### Conventions

- **Co-locate tests with source:** `foo.ts` β†’ `foo.test.ts` in the same directory
Expand All @@ -315,6 +323,11 @@ pnpm run test:watch # watch mode
pnpm run test -- -t "name" # filter by test name
```

## TypeScript Strictness

- **`noUncheckedIndexedAccess: true`** β€” array/object index access returns `T | undefined`. Guard with `if`, optional chaining, or nullish coalescing before using indexed values.
- **`@typescript-eslint/no-explicit-any: 'error'`** β€” `any` is a lint error. Use `unknown`, generics, or proper interfaces.

## Don'ts

- `export let` β€” use `$props()`
Expand Down