feat(12.2-04): add the fileupload field with deferred uploads on the form session key

- sessionKey.ts: one 32-byte base64url key per form mount, sent only in headers
- api/files.ts: FileRoutes over the record and child file routes, XHR upload with progress, 401 refresh and retry
- FileuploadField and FileCaptionModal per UI-SPEC section 3: dropzone, image grid, rows, per-item states, client pre-checks, reorder, protected previews
- FormView provides FORM_SESSION, counts pending changes as dirty and sends X-Session-Key on create and update
- fileupload lang keys in en and pl, admin-spa docs note, deferred smoke test, rebuilt dist
This commit is contained in:
Jakub Zych
2026-10-02 19:22:22 +02:00
parent 1ebfe691a2
commit ea33296799
22 changed files with 2338 additions and 15 deletions

View File

@@ -0,0 +1,68 @@
{
"data": {
"name": "Gadget",
"fields": [
{
"name": "name",
"label": "Name",
"type": "text",
"required": true
},
{
"name": "photos",
"label": "Photos",
"type": "fileupload",
"mode": "image",
"multiple": true,
"maxFiles": 3,
"fileTypes": [
"jpg",
"png"
],
"maxFilesize": 2,
"thumbOptions": {
"mode": "crop"
},
"useCaption": true
}
],
"messages": {
"create": {
"other": "New gadget"
},
"update": {
"other": "Editing gadget"
},
"saved": {
"other": "Gadget „:name” saved"
},
"deleteConfirm": {
"other": "Delete this gadget?"
},
"deleted": {
"other": "Gadget deleted"
}
},
"meta": {
"locale": "en"
},
"assets": {
"scripts": [],
"styles": []
},
"redirects": {
"create": {
"redirect": "acme/demo/gadgets/update/:id",
"redirectClose": "acme/demo/gadgets"
},
"update": {
"redirect": "acme/demo/gadgets",
"redirectClose": "acme/demo/gadgets"
},
"default": "acme/demo/gadgets"
}
},
"meta": {
"locale": "en"
}
}

View File

@@ -10,6 +10,7 @@ import settingsJson from './settings.json'
import extensionFormSchemaJson from './extension.form-schema.json'
import extensionListSchemaJson from './extension.list-schema.json'
import extensionPartialJson from './extension.partial.json'
import deferredFormSchemaJson from './deferred.form-schema.json'
import formSchemaJson from './widgets.form-schema.json'
import listJson from './widgets.list.json'
import listSchemaJson from './widgets.list-schema.json'
@@ -30,6 +31,8 @@ export const settingsFixture: {
values: S['cabana.Envelope-cabana_SettingsResult']
} = settingsJson
export const formSchemaFixture: S['cabana.Envelope-cabana_FormView'] = formSchemaJson
/** A gadget form with deferred-binding fields: fileupload (Phase 12.2). */
export const deferredFormSchemaFixture: S['cabana.Envelope-cabana_FormView'] = deferredFormSchemaJson
/** A form with a plugin widget and its controller script (Phase 10.1). */
export const extensionFormSchemaFixture: S['cabana.Envelope-cabana_FormView'] = extensionFormSchemaJson
/** A list with a header partial and a controller stylesheet (Phase 10.1). */