Skip to content

Members — feature audits ​

admin SPA + 6 widgets · French. 31 features audited. Totals across this project: Blocker 11 · High 69 · Medium 132 · Low 57.

See the feature queue for the inventory and the project-level findings for architectural root-causes. Severity counts are parsed from each draft's finding headers.

Every feature below links to its full audit.

Auth callback (admin SPA) ​

Blocker 1 · High 2 · Medium 3 · Low 2

The callback view itself is three lines of code, and almost every defect in this feature lives in the two things it delegates to: App.vue's unconditional session.initialize() and the router's busy-wait guard. The most important finding is a genuine **in…

B-Active dashboard ​

Blocker 0 · High 1 · Medium 1 · Low 2

A read-only staff dashboard of stat tiles, progress rings and bar charts for one program-year. It is mostly clean — no forms, no destructive actions, currency is formatted correctly (admin useCurrency.ts uses maximumFractionDigits: 2) and the Directus-a…

B-Active memberships ​

Blocker 0 · High 0 · Medium 3 · Low 2

The B-Active memberships view (bactive-adhesions) is a thin wrapper over the shared TableData + FilterDrawer components plus a "Comparaison" sub-view, so most of what it does — sorting, filtering, error-as-empty rendering — inherits the project-level …

B-Active reminders (relances) ​

Blocker 0 · High 2 · Medium 2 · Low 3

bactive-relances is a ~60-line read-only view (views/BActive/Relances.vue) that renders one shared TableData of B-Active members who need a reminder. The worklist is computed client-side as pmo.members minus bactive.members, and the single most im…

BCC memberships ​

Blocker 0 · High 1 · Medium 5 · Low 0

The bcc-adhesions route is a two-tab view: a members list built on the shared TableData (Adhésions tab) and a year-over-year diff (CompareMemberships). The list surface inherits the project-level TableData defects and adds no new ones; the **Compara…

BCC reminders (relances) ​

Blocker 0 · High 1 · Medium 2 · Low 1

views/BCC/Relances.vue is a thin, read-only TableData wrapper: it lists PMO members who have no BCC membership for the selected year (Relances.vue:21) so staff know who to chase. Despite the queue's forms/multi-select-input and `user-feedback/notifi…

Coherence checks ​

Blocker 0 · High 0 · Medium 3 · Low 3

The Coherence view (admin/coherence, "Cohérence des données") is a self-contained card list — it does not use the shared TableData.vue, so the project-level TableData cluster (mouse-only rows, dead isError branch, FilterDrawer focus) does not appl…

Cotisations (dues) ​

Blocker 1 · High 4 · Medium 7 · Low 4

The Cotisations table itself is competently built — capability-gated, sensible column meta feeding the FilterDrawer, a real confirmation modal for the surplus transfer, and correct 2-decimal money rendering through the admin's own useCurrency. The serious…

Data explorer ​

Blocker 0 · High 1 · Medium 5 · Low 2

The data explorer is one of the better-built admin surfaces: filter/operator/value controls are real <button>/<select> elements (not the mouse-only div@click rows of TableData), the row-edit dialog is server-guarded and audited, and the results head…

Demandes (requests) ​

Blocker 2 · High 4 · Medium 9 · Low 1

The unified Demandes queue (PR #75, c20219b) is the only place staff can act on member-submitted membership change requests, and two of its controls do not work: every selection made in the filter panel returns an empty table (the three filter columns…

Donation progress (widget) ​

Blocker 1 · High 2 · Medium 4 · Low 1

DonsProgress is a single-component embeddable widget: it logs the member in, fetches { totalDons, donsObjective } and draws one progress bar. The happy path is fine, but the state machine collapses everything that is not a successful non-zero result int…

Error states ​

Blocker 0 · High 0 · Medium 1 · Low 2

The admin SPA's only dedicated error surface is the forbidden route (Forbidden.vue), a three-line component that renders a single French BccMessage and nothing else. It states the problem but offers no next step, no recovery action and no heading — wh…

Events ​

Blocker 0 · High 2 · Medium 7 · Low 1

The Events admin surface (list, detail with Informations / Programme / Inscriptions tabs, create modal, CSV/XLSX import) works for the happy path but has two genuine data-integrity problems: the shared date picker serialises the wrong calendar day for eveni…

Facturation (invoicing) ​

Blocker 0 · High 4 · Medium 9 · Low 2

Facturation is the most consequential admin surface audited so far: it creates and emails real Pennylane invoices and credit notes, in bulk, to families and to over-18 members. The single-row flows are genuinely well built — preview first, an explicit confi…

Familles ​

Blocker 0 · High 1 · Medium 4 · Low 1

Familles is a two-tab admin view (Familles.vue) built on the shared TableData + ModalFamille + SelectMembers + ModalBase stack. It works for the happy path, but the create/edit modal is not screen-reader-labelled, closes optimistically before the …

Home ​

Blocker 0 · High 0 · Medium 0 · Low 2

The home route renders Home.vue: a static French welcome hero (<h1> + one-line description) beside a "Quoi de neuf" panel that injects the latest bundled release note via v-html. It is not a statistics dashboard — the data-display/dashboard/`s…

Imports ​

Blocker 1 · High 2 · Medium 4 · Low 2

The Imports feature is not an import wizard: release 1.8.0 removed the Excel import assistant and replaced it with the Pennylane sync on the « À catégoriser » page, leaving imports as a read-only history of past runs. So the checks this audit was brie…

Member-emulation panel ​

Blocker 1 · High 3 · Medium 6 · Low 3

The emulation console is a staff tool that mints a real member session and renders the six member-facing widgets fully live — event registration, SumUp checkout, signed waiver PDFs, membership and cotisation changes are all reachable from the preview pa…

My camps & objectives (widget) ​

Blocker 0 · High 3 · Medium 6 · Low 1

The camps-objectives widget renders three quite different experiences (own objective, family configuration, family progress) off a single mount point, and picks between them from data shape alone — never from who is looking or whether the data actually load…

My décharges (parent liability waivers for minors) ​

Blocker 0 · High 3 · Medium 8 · Low 2

The waiver flow is well-built on its happy path: the clause the parent reads on screen (WaiverModal.vue:104-107) is word-for-word the clause archived in the signed PDF (decharge-pdf.service.ts:246-249), the server re-verifies family scope on every call,…

My events, checkout & payment ​

Blocker 2 · High 9 · Medium 4 · Low 1

This is the only flow in the programme that takes real money, and it is the least defended. The server side is solid — assertSumupCheckoutPaid really does verify with SumUp before confirming, so there is no payment-spoofing hole — but the client above it …

My memberships (B-Active) ​

Blocker 0 · High 4 · Medium 2 · Low 0

B-Active is the BCC widget plus money: the same accordion, the same shared Condition / SelectField / Toast components (so it inherits the BCC draft's findings one-for-one — see Inherited below), with a club selection, a cotisation total and a six-ch…

My memberships (BCC) ​

Blocker 1 · High 5 · Medium 3 · Low 1

The BCC membership widget is a consent-capture form, and its two weakest points are exactly the two that matter for consent: the terms a member is agreeing to can be unreachable (the cotisation PDF link only resolves for 2025 and 2026, and the checkbox that…

Objectives & group comparison ​

Blocker 0 · High 1 · Medium 3 · Low 2

The objectifs list is a thin TableData wrapper and is mostly clean (its shared-table defects are the project-level TableData.vue cluster, not re-filed here). The custom bactive-groupes comparison screen is where the real defects sit: it deletes shar…

PMO ​

Blocker 0 · High 0 · Medium 3 · Low 2

PMO is a thin wrapper over the shared TableData (a member worklist with search, column filters via FilterDrawer, and a row → ModalMember), plus an admin-only PMOSync panel that triggers a server sync. It therefore inherits the whole already-confirme…

Release notes ​

Blocker 0 · High 0 · Medium 1 · Low 3

A read-only version-history page: useReleaseNotes eagerly globs bundled release-notes/*.md, renders each through marked into v-html, and a sticky sidebar scroll-spies the active version. It works and its IntersectionObserver is correctly torn down…

Settings ​

Blocker 0 · High 1 · Medium 4 · Low 2

The Settings view (admin-settings, views/Admin/Settings.vue) is a two-card config form (sub-organisation + organisation) with no client-side validation and no error surface on load. The most serious issue is a data-loss path: a failed initial fetch sile…

Sponsors (Admin SPA) ​

Blocker 0 · High 1 · Medium 6 · Low 2

The Sponsors feature (annuaire sponsors-list, campaign board sponsors-campaigns, sponsor sponsors-detail, plus five modals and sponsors.store.ts) is functionally complete and, unlike the widgets, gets currency right — useCurrency.ts and every spon…

Superadmin ​

Blocker 0 · High 3 · Medium 4 · Low 2

The Superadmin view is a hand-rolled, self-contained CRUD surface (no TableData.vue) for cross-org management: create/rename/deactivate organisations and sub-organisations, toggle feature modules, and grant the Administrateur role. It is functional and re…

Transactions (admin SPA) ​

Blocker 1 · High 8 · Medium 9 · Low 4

The two transaction views are the admin SPA's ledger surface, and the write paths are where they fail. Splitting a transaction on the main list silently destroys money: the new row's primary key is sha1(Date-Montant-Libellé) with no uniqueness salt, so th…

Users & roles ​

Blocker 0 · High 1 · Medium 4 · Low 1

Two hand-rolled tables (Users.vue users tab + RoleAccessMatrix.vue roles tab) outside the shared TableData.vue, so the project-level TableData findings do not apply and these views must be judged on their own. The core interaction — assigning a role…