Commit Graph

6 Commits

Author SHA1 Message Date
Pontoporeia
2143869b1e Add admin form field partials and apply to add/edit forms
Four reusable PHP partials extracted to templates/partials/form/:

- text-field.php  — single-line input (text/number/url); wraps input+hint in div,
                    skips the inner wrapper when no hint is present. Supports $type,
                    $placeholder, $required, $attrs, $hint, $id overrides.
- select-field.php — <select> with leading empty option; matches $selected against
                    option id OR option name string (handles view-sourced data where
                    orientation/ap/finality come back as name strings, not FK ids).
- checkbox-list.php — checkbox group (languages, formats); renders .admin-checkbox-list
                    with typed-string comparison so int ids from DB match string values.
- file-field.php  — file input with accept/multiple/hint; appends [] to name when
                    $multiple is true.

Both add.php and edit.php rewritten to use the partials:
- ~15 repeated text-field divs collapsed to single-line include calls
- ~6 repeated select divs collapsed to single-line include calls
- 4 checkbox-list blocks collapsed to 2 calls each
- 3 file input blocks collapsed to single-line include calls
- Textarea fields (synopsis, context_note) kept inline — no partial for <textarea>
- Banner preview block in edit.php kept inline — conditional UI not generalised

Line count: add.php 251→93 (-158), edit.php 289→171 (-118)
2026-04-02 12:48:04 +02:00
Pontoporeia
c8a3cc0ff2 css: replace admin-form-row/admin-label/admin-input/select/textarea classes with semantic selectors
Remove five presentational classes from admin forms and replace with
structural CSS selectors scoped to .admin-form:

- .admin-form-row  → .admin-form > div:not(.admin-submit-wrap)
  Grid layout (260px label col + 1fr input col) applied directly to div
  children of the form; submit-wrap div excluded via :not().

- .admin-label     → .admin-form > div:not(.admin-submit-wrap) > label
  Scoped to the direct label child of each form row div; does not bleed
  into nested checkbox labels inside .admin-checkbox-list.

- .admin-input / .admin-select / .admin-textarea
  → .admin-form input:not([type=checkbox|radio|file|hidden|submit])
  → .admin-form select
  → .admin-form textarea
  Also extended to .admin-inline-form input/select (tags page) so the
  tags table inputs retain identical base styling and focus colour.

Templates updated: add.php, edit.php, login.php, account.php,
pages-edit.php, import.php, tags.php,
templates/partials/form/jury-fieldset.php — all class= attributes for
the five removed classes stripped.

import.php: added 'admin-form' class alongside 'admin-import-area' so
its single file-input row gets the grid row treatment; submit div was
already using admin-submit-wrap so it is correctly excluded.

No visual change — selectors target the same elements as before.
2026-04-02 12:42:49 +02:00
Pontoporeia
e9e012376d Replace .admin-alert BEM classes with semantic role/data-type attributes
- admin.css: replace .admin-alert / .admin-alert--error / .admin-alert--success
  selectors with [role="alert"][data-type="error"] and [role="status"][data-type="success"]
- All 10 admin templates updated: <div class="admin-alert admin-alert--{type}">
  becomes <p role="alert|status" data-type="error|success"> (or <div> for the
  import.php multi-item list that contains a <ul>)
- flash-messages.php partial updated to match
- WCAG benefit: role="alert" is an ARIA live region — errors are announced
  immediately by screen readers without focus movement (fixes WCAG 3.3.1, 4.1.2)
- role="status" (polite live region) used for success messages — announced
  without interrupting the user
- Removes two BEM modifier classes; CSS now targets element semantics directly
2026-04-02 12:35:23 +02:00
Pontoporeia
10b07393fe Extract jury-fieldset.php partial; deduplicate jury section from add.php and edit.php
The jury composition fieldset (président·e, promoteur·ice + external checkbox, dynamic
lecteur·ices list with JS add/remove) was copy-pasted verbatim between the two longest
admin forms.

- Created templates/partials/form/jury-fieldset.php
  - Consumes $juryPresident, $juryPromoteur, $juryPromoteurExt, $juryLecteurs[]
  - Handles both add-mode (falls back to old()/wasSelected() flash helpers) and
    edit-mode (pre-populates from DB-loaded variables)
  - $juryIdx initialised from max(count($juryLecteurs), 1) — correct for both modes
- add.php: 311 → 251 lines (-60); entire fieldset + <script> replaced with one require
- edit.php: 359 → 289 lines (-70); PHP variable extraction kept inline before require
2026-04-02 12:26:44 +02:00
Pontoporeia
7834d88873 Extract pagination into templates/partials/pagination.php
The pagination nav was duplicated between public/index.php and public/search.php
with structural differences: index.php used string concatenation for query params
and had first/last-page buttons (« »); search.php used http_build_query but had
only prev/next (‹ ›) and a flat <span> rather than a <ul>/<li> structure.

- Add templates/partials/pagination.php: accepts $page, $totalPages, $baseParams[]
  (any array of query params to preserve); builds URLs with http_build_query;
  renders a semantic <nav>/<ul>/<li> block with first/prev/info/next/last buttons,
  correct aria-disabled + tabindex on disabled links, and aria-label on each button.
  Returns immediately (no output) when $totalPages <= 1.

- Replace inline pagination block in index.php with:
    $baseParams = array_filter(['year' => $year]);
    include pagination.php

- Replace inline pagination block in search.php with:
    $baseParams = array_diff_key($_GET, ['page' => '']);
    include pagination.php
  This also upgrades search.php to the full first/last button set it was missing.

Both callers verified with php -l. No functional change to existing behaviour.
2026-04-02 12:20:31 +02:00
Pontoporeia
eb67e6d499 Add src/App.php foundation class and flash-messages partial
Create the central App helper that eliminates ~170 lines of duplicated
bootstrap/auth/CSRF preamble across 24 page and action handler files.

src/App.php provides:
- boot(): loads Database + ensures CSRF token (public pages)
- adminGuard(): requires AdminAuth login + boot (admin pages)
- verifyCsrf() / rotateCsrf(): centralised CSRF lifecycle
- flash() / consumeFlash(): unified flash messages with legacy key drain
  (error, success, admin_error, admin_success, edit_error, edit_success,
  form_error all consumed transparently for incremental migration)
- redirect(): flash + Location header + exit in one call
- render(): head → header → content → footer pipeline with auto admin
  footer selection

App.php is auto-loaded from config/bootstrap.php so all existing pages
get the class for free without any changes.

templates/partials/flash-messages.php uses App::consumeFlash() to replace
the 5+ copy-pasted flash blocks across admin templates.

All existing tests pass. No existing page files modified — this is a
non-breaking addition that enables incremental controller extraction.
2026-04-01 15:55:12 +02:00