Add @media (max-width: 600px) rule to form.css: - Stack form row labels above inputs (1fr grid, single column) - Ensure 44×44px minimum touch targets on checkboxes, radios, selects, textareas, text inputs, and .btn/.btn--sm - Stack thesis-add-header and recap-dl grids to single column - Stack form footer buttons vertically with full width - Unstick sticky formats fieldset on mobile - Tighten fieldset margins for narrow viewports
9.6 KiB
TODO
HTMX v2 Migration
Reference: docs/autosave-system.md → "HTMX v2 Migration Plan" section.
contenus-edit.php(pages): Addhx-*attrs, addovertype:changedispatch in OverTypeonChangecontenus-edit.php(form_help): Addhx-*attrs, addovertype:changedispatch in OverTypeonChangeapropos-groups-form.php(contacts): Addhx-*attrs onlycontenus-edit.php(sidebar_links): Addhx-*attrs only- Add
handleAutosaveResponse()shared handler +htmx:beforeRequestloading state - Delete
autosave.js - Fix backend
$isAjaxdetection: also recognizeHX-Requestheader (page.php, apropos.php, form-help.php) - Form-help inline editors: add OverType toolbar + HTMX auto-save + remove save buttons
- Markdown cheatsheet modal: reusable dialog on all OverType editors
FilePond crash on TFE upload forms
- Analyze root cause →
docs/filepond-crash-analysis.md - Partial fixes (Content-Type headers, onerror cleanup, load object) — insufficient, crash still reproduces
- HTMX/destroy race hypothesis investigation →
docs/filepond-race-investigation.md(verdict: REFUTED) - Diagnostic probes + deep analysis: confirmed load-file-error dispatch path, traced via error.stack to fileValidateSizeFilter line 389
- ROOT CAUSE FIXED: fileValidateSizeFilter accessed
item.filenamebut FileValidateSize's LOAD_FILE filter passes the raw File/Blob (which has.name, not.filename). Changed toitem.filename || item.name. Also added null guard to getExt(). - Defensive: Wt and Fr crash guards in filepond.min.js prevent action.status.main crash
- process.onload: replaced throw with error-marker return (prevents FilePond crash when server returns HTML)
- Routing: partage index.php now routes /partage/actions/* directly to PHP files (was treating 'actions' as a slug and returning full HTML page)
- All crashes resolved — verified working on partage form
Form Accessibility & Resilience — Assessment Follow-up
Reference: Assessment against progressive-enhancement / WCAG-AA / "never lose data" / low-common-denominator guidelines.
1. WCAG AA: Add field-level aria-errormessage + aria-invalid to TFE form
Current state: Flash error divs have role="alert" but individual fields are never linked to their error via aria-errormessage. The autofocusFieldForError() mechanism focuses the field after a validation redirect but does not announce the error to screen readers. Help <small> text is not linked via aria-describedby.
To do:
- Extend
text-field.php,select-field.php,checkbox-list.phppartials to accept optional$errorFieldNamevariable - When
$errorFieldNamematches the field, addaria-errormessage="flash-error"andaria-invalid="true"on the input - On validation redirect, populate
$errorFieldNamefromApp::consumeAutofocus()so each failing field references the flash error container - Add
aria-describedbylinking each<small>hint to its input (always, not just on error) - Give flash-error div
id="flash-error"andtabindex="-1"for programmatic reference - Wire
App::flashAutofocus()into partage submit catch block (was missing) - Wire
$withAutofocusFnin admin add template (was defaulting to identity) - Apply
aria-invalid+aria-errormessageon synopsis textarea (not in text-field partial) - Apply
aria-describedbyon file inputs infichiers-fragment.php - Apply format checkboxes
aria-invalidsupport infichiers-fragment.php - Test with VoiceOver and NVDA on the full add/edit/partage form flows
2. No-JS file uploads silently fail (data loss)
Current state: form.php hardcodes <input type="hidden" name="filepond_mode" value="1">. Without JS, no queue_file[] hidden inputs are populated → server gets filepond_mode=1 with empty queue → all files silently dropped. The form is supposed to work without JS.
To do:
- Change the hidden input to
<input type="hidden" name="filepond_mode" value="0" disabled>by default; JS enables it and setsvalue="1"on DOMContentLoaded - Add server-side fallback in
ThesisCreateController::submit()andThesisEditController::save(): whenfilepond_mode=1but noqueue_filedata is present, fall through to the legacy$_FILESpath - Test end-to-end: submit the partage form with JS disabled, verify files arrive via
$_FILES
3. Autosave text fields on partage form
Current state: Only FilePond files are persisted to the server before submit. Text/select/checkbox fields live only in DOM and are lost on tab close / crash / network failure. beforeunload-guard.js warns but cannot recover data. The admin panel already has an HTMX-based autosave pattern (contenus-edit.php).
To do:
- Implement a per-session draft endpoint (
POST /partage/<slug>/draft) that saves form data to a session-backed store - On field blur/change, POST the field name+value via HTMX (debounced 500ms)
- On page load, hydrate all fields from the draft endpoint
- Draft is cleared on successful submission; expires after 24h with session
- Add a visible "Brouillon enregistré" indicator to reassure the student
4. FilePond temp file IDs lost on partage validation redirect
Current state: After a validation error, storePrimedFiles() shows file names so the user knows what to re-select. But the actual FilePond hex IDs (server-side temp files) are not preserved — the user must re-upload everything. For large files on slow connections, this is painful.
To do:
- After a validation error in the partage form, preserve FilePond temp file IDs in
$_SESSION['share_filepond_ids_' . $slug]by queue type - Use these IDs to build the
data-existing-filesJSON attribute on the re-rendered form, seeding the FilePond pools - Ensure the temp files' server-side lifetime covers at least one validation round-trip (extend
FilepondHandlerTTL if needed)
5. Two-phase commit: protect against crash during file moves
Current state: DB transaction commits, then files are moved from tmp to storage. If the process crashes between COMMIT and file move completion, the DB has a thesis record with no files (or partial files). This is a public-facing service — no acceptable failure rate.
To do:
- Add an
is_publishedorstatusflag to theses table (if not already present) - In the partage submit flow: INSERT thesis with
status='draft'inside the transaction → COMMIT → move files → UPDATEstatus='active' - On partage submission error recovery, roll back draft theses that are older than N minutes with no files
- Add a periodic cleanup job (
just cleanup-drafts) for orphaned drafts
6. Mobile-responsive form layout
Current state: Form field rows use grid-template-columns: 260px 1fr with no breakpoint. Below ~520px viewport width, labels wrap and inputs are cramped.
To do:
- Add
@media (max-width: 600px)rule inform.cssswitching togrid-template-columns: 1frwith labels stacked above inputs - Test on 320px wide viewport (PSP, low-end Android) — verified via responsive design tokens (min 360px clamp)
- Ensure touch targets are at least 44×44px (WCAG 2.5.5)
7. Split form.css: shared base vs admin-only styles
Current state: form.css is 36KB (1,500+ lines). The partage form loads all of it but uses only ~40% (base input/fieldset styles). Admin-specific styles (.admin-jury-*, .admin-backoffice-*, etc.) are dead weight for students.
To do:
- Split
form.cssintoform-base.css(~15KB: inputs, selects, textareas, fieldsets, legends, validation states, flash messages,aria-invalidstyling) andform-admin.css(~21KB: admin layout, jury fieldset, backoffice, tag pills, autocomplete) - Load
form-base.cssin the partage form; load both in admin forms - Update
head.phpto support$extraCssAdminfor admin-only stylesheets
8. Fix aria-required on <fieldset> (standards compliance)
Current state: checkbox-list.php and fichiers-fragment.php put required aria-required="true" on <fieldset> elements. The required attribute is not valid on <fieldset>. Screen readers may not interpret this correctly.
To do:
- Remove
requiredattribute from<fieldset>incheckbox-list.php— keeparia-required="true"only - Remove
requiredattribute from<fieldset>infichiers-fragment.php— keeparia-required="true"only - Add
role="group"on both<fieldset>elements for explicit ARIA semantics
9. Refactor partage form page wrapper to a template
Current state: renderShareLinkForm() in partage/index.php outputs an entire <html> document inline (~120 lines of HTML inside a PHP function). This duplicates head.php/header.php logic.
To do:
- Extract the partage form page chrome to
templates/partage/form-page.php - Use
App::render()or a dedicatedrenderPartageForm()helper - Share meta tags, favicon references, CSRF meta, and live-reload script from
head.php
10. Reduce form bootstrap duplication across 3 entry points
Current state: admin/add.php, admin/edit.php, and partage/index.php (inside renderShareLinkForm()) each repeat ~40–60 lines of identical setup: load ThesisCreateController, call loadFormData(), build $helpFn, load site settings, set up jury/default access variables, etc.
To do:
- Add a
ThesisFormSetuphelper class (or static method on the controllers) that returns a standardised array of form variables - Each entry point becomes:
$formVars = ThesisFormSetup::prepare($mode, $slug); extract($formVars); include '...form.php';