18 KiB
phase, plan, subsystem, tags, requires, provides, affects, actuals, plan_head_before, plan_head_after, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
| phase | plan | subsystem | tags | requires | provides | affects | actuals | plan_head_before | plan_head_after | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | coverage | duration | completed | status | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 12.2-admin-form-fields-date-file-upload-relation-editing-with-def | 04 | admin-spa |
|
|
|
|
|
cb653711f0 |
69a37456f6 |
|
|
|
|
|
|
about 2h across two executors (Task 1 by the first, Tasks 2-4 by the continuation, 17:24Z to 17:57Z) | 2026-10-02 | complete |
Phase 12.2 Plan 04: Admin SPA for dates, uploads and related records Summary
The admin SPA now edits date, datetime and time values with a Reka date picker, uploads files with progress, reorder and captions, and creates, edits, deletes, links and pivot-edits related records in modals. On a record that is not saved yet, uploads and relation work are held against the form's session key and committed by its first save.
Performance
- Duration: about 2 hours over two executors. Task 1 ran first, then the continuation ran Tasks 2 to 4 from 17:24Z to 17:57Z.
- Completed: 2026-10-02
- Tasks: 4 (Task 2 was the dependency decision)
- Files modified: 31 source, test, lang and docs files, plus the rebuilt
modules/boardwalk/dist
Accomplishments
- Session keys (D-02). FormView makes one 43-character base64url key per mount and sends it as
X-Session-Keyon the save. A relation child modal makes its own key and sends it asX-Child-Session-Key. No key ever goes in a URL. - FileuploadField (D-03, D-08 to D-10). It covers the dropzone, the image tile grid, attachOne rows and file rows. Each upload shows its own state (queued, uploading with progress, failed, done). The SPA pre-checks type, size and maxFiles before sending. Removals wait for the save. Protected thumbnails and downloads use object URLs that are revoked on unmount. Reorder works by pointer and by keyboard, with one debounced request. Captions are edited in FileCaptionModal.
- DatepickerField (D-18 to D-21). It is built on Reka DatePicker and TimeField and orders segments by the admin locale.
- A datetime is shown in the browser time zone and emitted as UTC. With
ignoreTimezonethe wall clock is kept unchanged. - A
dateis emitted as YYYY-MM-DD and atimeas HH:MM:SS. - The calendar popover maps
firstDay,minDate/maxDateandyearRange. - The field also has a clear button, a read-only text box in
displayFormat, and an invalid state for partial or out-of-range input. - It uses no native date input and no global Date.
- A datetime is shown in the browser time zone and emitted as UTC. With
- List cells.
dateandtimecolumns show the stored string, with seconds dropped for time. - Relation manager (D-11, D-12, D-14, D-17).
- The toolbar shows create, link, delete and unlink in the declared order, with exactly one primary button. Delete asks for confirmation, and the confirm button shows a busy state while the request runs.
- Clicking a row opens the child modal in update mode. Without update it opens the pivot modal, and without a pivot form it opens the view form read-only. A belongsToMany with a pivot form and update gets a trailing pivot button.
- RelationChildModal (720px) has tabs, an error banner, focus on the first invalid field, skeleton loading, a load-failure alert, and the 404 "gone" flow. Closing it with unsaved changes asks first. Uploads and dates inside it use the child's own key.
- RelationPivotModal edits link details.
- With a pivot form, the picker links one record at a time. It shows a Next step with the pivot form and posts
{ids:[id], pivot}.
- Create-screen deferral (D-03). Deferrable relation managers render on create with the pending note, call every route with owner id 0 and the form key, and mark the form dirty.
- Language keys. The datepicker, fileupload and relation keys are in English and Polish.
TestPhase10SPAKeysResolvepasses.
Task Commits
- Task 1 (tracer): fileupload on a new record -
ea33296(feat; the earlier executor) - Task 2: approve @internationalized/date@3.12.4 - decision checkpoint. The user answered approve. The install is committed with Task 3.
- Task 3: datepicker, dateFormat, date/time cells, datepicker lang -
a0c1827(feat) - Task 4: relation child, pivot and create-screen deferral modals -
69a3745(feat)
The plan ledger shows 5 commits from cb65371 to HEAD. Two of them are concurrent phase 13 planning commits made by another session on master: 1ebfe69 and d66812c.
Files Created/Modified
admin/src/app/sessionKey.ts,admin/src/api/files.ts,admin/src/components/form/formContext.ts: the session key, the file route adapters and the form sessionadmin/src/app/dateFormat.ts: parse, emit and format of date values, plus week start and boundsadmin/src/components/form/fields/{FileuploadField,FileCaptionModal,DatepickerField}.vue: the new controlsadmin/src/components/relation/{RelationManager,RelationPickerModal,RelationChildModal,RelationPivotModal}.vue: relation editingadmin/src/components/list/{CellValue,DataTable}.vue: date and time cells, openable rows and the trailing celladmin/src/components/ui/{confirm.ts,ConfirmDialog.vue}: a busy confirm action (additive)admin/src/components/form/registry.ts:fileuploadanddatepickerregistered, RelationManager resolved lazilyadmin/src/views/FormView.vue: the session key, pending changes indirty, and deferrable relation managers kept on createmodules/phrasebook/backend/lang/{en,pl}/lang.yaml,docs/backend/admin-spa.md,modules/boardwalk/dist
Decisions Made
See key-decisions in the frontmatter. Two of them came from the user. They approved the exact @internationalized/date pin. They also decided that existing belongsToMany managers stay deferrable on the create screen unless the field says context: update.
Deviations from Plan
Auto-fixed Issues
1. [Rule 3 - Blocking] The registry test used fileupload as its unsupported-type example
- Found during: Task 1
- Fix: It now uses
codeeditor. - Commit:
ea33296
2. [Rule 2 - Critical] A lost session during an upload was not reported
- Found during: Task 1
- Fix:
admin/src/api/client.tsgainsreportUnauthorized(). If the upload XMLHttpRequest still gets 401 after its one refresh, it reports the lost session through the typed client's handler. - Commit:
ea33296
3. [Rule 3 - Blocking] An import cycle in FileCaptionModal
- Found during: Task 1
- Fix: FileCaptionModal has its own small parser for 422 details. Importing
formState.fieldErrorswould have closed the cycle registry, field, formState, registry. - Commit:
ea33296
4. [Fixture] Typed deferred fixture
- Found during: Task 1
- Fix:
admin/tests/fixtures/typed.tsgainsdeferredFormSchemaFixture, which type-checks the fixture against the generated FormView. - Commit:
ea33296
5. [No change needed] formState.ts
- Found during: Task 1
- Note: The plan listed formState.ts, but it needed no change.
fileuploadis registered as valueless, andeditablePayloadalready leaves valueless types out.
6. [Rule 3 - Blocking] RelationManager was registered as UnsupportedField
- Found during: Task 4
- Issue: The child modal renders FormGrid and uses formState. That puts RelationManager on an import cycle with the registry. When RelationManager loaded first, the registry's map was built while RelationManager was still undefined, so
rendererFor('relation-manager')returned UnsupportedField. - Fix: The registry now looks RelationManager up lazily, at render time.
- Commit:
69a3745
7. [Rule 2 - Missing] DataTable could not open rows or hold a trailing cell
- Found during: Task 4
- Issue: UI-SPEC row click needs a first-cell button and a 56px trailing pivot cell. DataTable had neither.
- Fix: DataTable gains the additive props
openableandtrailing, anopenemit and atrailingslot. The list views are unchanged. - Commit:
69a3745
8. [Rule 2 - Missing] The delete confirm could not show progress
- Found during: Task 4
- Issue: UI-SPEC says the delete confirm button shows a busy state while the request runs. The shared confirm closed as soon as it was clicked.
- Fix:
useConfirm().ask(request, action)now keeps the dialog open and busy until the action settles. ConfirmDialog gains abusyprop. Existing callers are unchanged. - Commit:
69a3745
9. [Rule 2 - Missing] The picker did not send the session key
- Found during: Task 4
- Issue: On owner id 0 the candidate and link calls need
X-Session-Key. - Fix: RelationPickerModal gains a
sessionKeyprop. - Commit:
69a3745
10. [Tests] New relation tests were added
- Found during: Task 4
- Note: The RelationManager and RelationPickerModal test files gain tests for the new toolbar, row click, delete and pivot step. No existing expectation changed, and all existing tests pass unchanged.
- Commit:
69a3745
Issues Encountered
- Task 1's commit
ea33296also contains.planning/phases/13-.../13-VALIDATION.md. The concurrent phase 13 session had staged that file in the shared index. The commit was left as is (master is shared; no history rewrite). The file belongs to phase 13. - Running
npx --prefix admin vitestfrom the repo root made a straynode_modules/.vitecache at the root. It was removed and never committed.
Known Limitations
- In a preview child modal (view form) the form fieldset is disabled. Download buttons for protected files are therefore disabled in preview. Public files keep working links.
- DatepickerField detects partly filled segments on keyup, focusout and value changes, by reading Reka's
data-placeholdermarkers.
Known Stubs
None.
Human Checks (end of phase)
- Task 3: Check datepicker keyboard entry, the calendar keyboard flow (Enter, arrow keys, PageUp/PageDown, Esc returning focus to the trigger), that disabled days cannot be picked, the zone segment, and the visuals against Direction C.
- Task 4: Run the create-screen flow: children with an uploaded image, a link with pivot details, attachMany drag reorder, then Save. Then edit a child, edit pivot details and delete a child on the saved record. Upload progress and the modals should match UI-SPEC.
Threat Flags
None. Every new request uses the routes and headers in the plan's threat model. The session keys travel only in headers, there are no raw-HTML sinks, and the only new hex value is the inherited #e0b020.
Next Phase Readiness
Plan 05 can write unit tests and backstops for:
dateFormat.ts- DatepickerField: Esc focus and disabled days
- FileuploadField: protected thumbnails, keyboard reorder
- RelationChildModal: tabs, 422 focus and the dirty close
- RelationPivotModal
- the busy confirm
- the lazy registry lookup
Self-Check: PASSED
- Created files exist: dateFormat.ts, DatepickerField.vue, RelationChildModal.vue, RelationPivotModal.vue, sessionKey.ts, files.ts, FileuploadField.vue, FileCaptionModal.vue.
- Commits
ea33296,a0c1827and69a3745are on master. - These checks pass:
npm --prefix admin run typecheck, the vitest suite (54 files, 695 tests),TestPhase10SPAKeysResolve,scripts/check-admin-dist.sh,go test ./modules/boardwalk,go vet ./...,go test -short ./...andTestDocsTree.