Commit Graph

7 Commits

Author SHA1 Message Date
a84742a01f chore(eslint): activate boundary sub-zones (TECH-WS3-BOUNDARIES-SUBZONES)
Adds the WS-3 §4.2 sub-zone classification to the apps/app
boundaries matrix:

- components-{shared,portal,organizer} alongside the legacy
  components type. components/{auth,settings} are folded into
  components-shared as the legacy cross-context home for MFA dialogs
  + PasswordRequirements (used by both organizer reset-password and
  portal wachtwoord-instellen / profiel).
- composables-forms (src/composables/forms/**) — pure form-runtime
  helpers reusable from organizer Form Builder later.
- stores-portal (src/stores/portal/**) — keeps the portal auth +
  portal store walled off from the organizer auth surface.
- pages-{register,portal,platform,organizer} alongside the legacy
  pages type — register pages cannot reach into stores or
  components-portal/-organizer; portal pages cannot reach
  components-organizer; organizer + platform pages cannot reach
  stores-portal or components-portal.

Cross-context edges are forbidden (organizer ↛ portal,
shared ↛ portal/organizer). Two pragmatic exceptions are documented
inline:
  - components-shared accepts the legacy auth/ + settings/ paths
    until PR-B2 cleanup re-homes them under shared/{auth,settings}/.
  - pages-register may read stores-portal because success.vue
    optionally enriches with the portal user when authenticated.
    PR-B2 may move success.vue into pages-portal so this drops.

Lint: 0 errors / 0 new warnings (only the pre-existing
boundaries v5→v6 deprecation warnings, which apply to all 19 rules
now). Tests: 23 / 162 pass. Typecheck clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 19:29:32 +02:00
bdbd5b0335 chore(cleanup): delete dead Vuexy views/ directory
src/views/ contained a single Vuexy-template file
(views/pages/authentication/AuthProvider.vue) with zero importers
in the repo. Vendored dead code from the original Vuexy template;
the §4.2 post-consolidation target layout drops views/ entirely.

Removed:
- apps/app/src/views/ (recursive)
- 'src/views/**' line from boundaries/ignore in .eslintrc.cjs

Closes TECH-DELETE-DEAD-VIEWS.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-04 21:39:31 +02:00
de71d31a2b chore(tooling): enable eslint-plugin-boundaries in apps/app
Adds 'boundaries' to plugins, the layered-architecture matrix to
rules, and the boundaries/elements + boundaries/ignore + boundaries/
include settings per the WS-3 1c audit (Phase A:
dev-docs/WS-3-SESSION-1C-AUDIT.md). Phase B sign-off (Bert):

- Q1=B — `lib → stores` is DISALLOWED in the matrix; lib/axios.ts is
  refactored in the next commit.
- Q2 — src/views/** added to boundaries/ignore (dead Vuexy file;
  TECH-DELETE-DEAD-VIEWS backlog item lands with the docs commit).
- Q3 — `navigation` allowed to import `types`, `utils` (forward
  headroom).
- Q4 — sub-zone enforcement deferred to TECH-WS3-BOUNDARIES-SUBZONES
  (lands when WS-3 PR-B brings the §4.2 components/{organizer,portal,
  shared} + pages/{(auth),portal,…} structure).

Forward-flag carried into the inline comment: when src/plugins/1.router/
migrates to a top-level src/router/ in a later WS-3 PR, add a
{ type: 'router', pattern: 'src/router/**' } element and a
{ from: 'router', allow: ['types','utils','lib','plugins','stores'] }
rule. Doc-side flag also lands in the ARCH-CONSOLIDATION 1c entry.

Boundaries plugin v6 emits a deprecation warning that the
'element-types' selector format is legacy (v5 syntax); the rule
still works on v5-compatible config and migrating to v6 object-
selector syntax is out of scope per the prompt's "only the two
.eslintrc.cjs changes listed are permitted" constraint. Filing a
TECH-BOUNDARIES-V6-SELECTOR-MIGRATION backlog item (in the docs
commit) so the migration happens deliberately.

Lint count after this commit: 4 errors, all in lib/axios.ts (lines
3, 4, 61, 72 — the 2 static + 2 dynamic store imports). The plugin
treats both static AND dynamic `await import('@/stores/...')` as
boundary edges; this is a deliberate intermediate state. The next
commit (refactor) resolves all 4 to land at lint = 0.

Tests + typecheck verified green (boundary errors are lint-only).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-30 23:16:57 +02:00
2fc2a569e7 fix(app): allow comments at SFC <script> block start
WS-3 session 1b-iii Task 2.

In Vue SFCs, the lines-around-comment rule conflicted with
vue/block-tag-newline at the <script setup>/comment boundary:
- lines-around-comment wants a blank line BEFORE the comment.
- vue/block-tag-newline wants exactly 1 line break after <script>.

Both can't be satisfied simultaneously when a script-block opens with
a leading comment. The session 1b-ii experiment (adding then reverting
blank lines) confirmed empirically these are mutually exclusive.

Resolution: per-*.vue override on lines-around-comment with
beforeBlockComment: false and beforeLineComment: false. Vue SFC
script blocks may now open with a leading comment without requiring
a preceding blank line. The base rule's allowBlockStart: true does
not help here because the <script> tag is not a JS block-start as
far as the AST sees it. All other rule options preserved
(allowBlockStart, allowClassStart, allowObjectStart, allowArrayStart,
ignorePattern: !SECTION).

Resolves the 3 items in PortalLayout.vue, PublicLayout.vue,
AppKpiCard.vue. Base rule remains in force for *.ts files.

Lint baseline: 6 → 3.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:46:21 +02:00
f4e0de0e4e fix(app): set indent SwitchCase to 1
WS-3 session 1b-iii Task 1.

Codebase consistently uses SwitchCase: 1 (cases indented 2 spaces from
switch keyword), but the eslint rule was running with default
SwitchCase: 0 (cases at the same column as switch). This produced 24
unfixable indent items in useTimeSlotDropdown.ts (and 0 in other
files because they didn't have switch statements with this pattern).

Resolution: pass { SwitchCase: 1 } to the indent rule's options so
its expectation matches the codebase reality. The autofix would
reformat the codebase to match the default if SwitchCase: 0 were
correct, but our codebase deliberately uses 1 — this is the
zero-compromise path, no codebase rewrite needed.

Lint baseline: 32 → 6 (Task 1 alone).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:44:35 +02:00
d407cd17de fix(app): resolve Bucket B (type safety) lint items
WS-3 session 1b-ii Task 3 (audit Bucket B — 34 items: 21 absorbed
via ignorePatterns + 14 real fixes; the count of 21 is the actual
non-Tier-3 lint-count drop from the .eslintrc edit, slightly above
the audit's predicted 20 because additional vendored-Vuexy items
beyond the 23 no-explicit-any landed in those paths too).

Config:
- .eslintrc.cjs: add src/@core/** and src/@layouts/** to ignorePatterns.
  Vendored Vuexy code, precedent: src/plugins/iconify/*.js. The
  CLAUDE.md no-any rule remains in force for our own code under src/.

Real type-safety fixes:
- B.1 ref<any> in our code (3 occurrences):
  * blank.vue / default.vue: AppLoadingIndicator template ref now
    typed as InstanceType<typeof AppLoadingIndicator> | null. Picks
    up the defineExpose'd fallbackHandle / resolveHandle methods.
  * NavSearchBar.vue:109: useApi<any>(...) → useApi<SearchResults[]>(...)
    matching the existing searchResult ref type.
- B.2 ShiftDetailPanel.vue: moved the Cancel-dialog ref declarations
  (isCancelDialogOpen, cancellingAssignment) from line 305-307 to
  line 248 — directly above the onCancel handler that uses them.
  Resolves all 7 no-use-before-define items in one move. Same-file,
  no logic change.
- B.3 useImpersonationStore.ts:119: renamed inner 'stored' to
  'storedSnapshot' to resolve shadowing of the outer 'stored' on
  line 18.
- B.4 useFormSchemas.ts:97-99: renamed local mutationFn parameter
  'confirmed_name' to camelCase 'confirmedName'. Wire-format key
  stays snake_case via destructure-alias:
    params: confirmedName ? { confirmed_name: confirmedName } : undefined
  No callers found in apps/app/src — safe rename.

Tests + typecheck verified green.

Lint baseline: 97 → 62.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 14:11:05 +02:00
192353f4bc feat(form-builder): admin UI completion — server filters, KPIs, resource expansion (WS-6 sessie 3c)
Closes the four production gaps that emerged from sessie 3b's admin UI.
What we ship here is final: no further rework planned before production.

Backend
- IndexFailuresRequest validates state/search/failed_at_from/failed_at_to/
  listener_class. orgIndex + platformIndex apply them via a single
  applyIndexFilters() helper. Search runs case-insensitive `LIKE` on
  exception_message; SQL wildcards in user input are escaped.
- New /kpis aggregate endpoint per scope (orgKpis, platformKpis) returns
  open / resolved_30d / dismissed_30d / total_submissions in O(1) COUNTs.
  Replaces sessie 3b's client-side bucketing of an oversized list.
- Resource expansion: organisation_name, form_schema_label,
  resolved_by_user_name, dismissed_by_user_name, exception_trace,
  retry_history[]. Eager-loading via indexEagerLoads()/detailEagerLoads()
  prevents N+1 (verified by query-count assertion in test).
- New 2026_04_28_181000 migration adds exception_trace (longtext nullable)
  to form_submission_action_failures. ApplyBindingsOnFormSubmit listener
  now captures $e->getTraceAsString() at failure time.
- New FormSubmissionActionFailureRetryAttemptResource exposes per-attempt
  data (timestamp, actor name, outcome, exception details) inside
  retry_history[]. Index payloads omit the field via whenLoaded() to keep
  list responses lean.

Frontend (apps/app)
- Types updated to mirror the expanded resource shape and the new KPI
  endpoint contract. FormFailuresKpis is now { open, resolved_30d,
  dismissed_30d, total_submissions } (server-aggregate).
- useFormFailures composable forwards all 5 server filters via
  buildIndexParams() (strips empty/whitespace). useFormFailuresKpis hits
  the dedicated /kpis endpoint per scope.
- FormFailuresTable replaces client-side bucketing with server-side
  filtering, adds listener_class + date-range filter inputs, and renames
  the 4th KPI tile to "Submissions" (was "Totaal").
- FormFailureDetail renders organisation_name + form_schema_label in the
  header, surfaces an expandable stack-trace card, names the resolved/
  dismissed actor in the timeline, and replaces the "v1 placeholder"
  retry-history card with a full per-attempt timeline.

ESLint config gap (apps/app)
- New .eslintrc.cjs adapted from the Vuexy reference, minus Vuexy-internal
  rules. `pnpm lint` now runs successfully (was previously broken — the
  package.json script referenced a missing config). The 80 baseline
  violations across the codebase are pre-existing and out of scope for
  this session.

Tests + gates
- 24 new backend tests across filter, kpis, and resource-shape suites.
  Backend: 1462 → 1486 passing, 0 → 0 failing. Larastan clean. Rector
  dry-run unchanged at 354 (pre-Task-1 baseline from f18b55b).
- 3 new vitest tests in apps/app (filter wiring, KPI endpoint, KPI tile
  values from /kpis). Vitest: 38 → 41 passing. tsc clean. Portal
  unchanged (113 vitest, tsc clean).
- 5 backfill rollback tests bumped --step counts +1 for the new migration.
- Ws6FoundationMigrationTest down/up chain now includes exception_trace
  before the parent table is restored.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 00:14:20 +02:00