Commit Graph

56 Commits

Author SHA1 Message Date
Jakub Zych
c3834cb42c feat(18.1-01): task 4 flat reorder in the admin SPA
- ReorderView reads flat from the reorder data; ReorderBranch takes a flat
  prop and shows only move up and move down
- ArrowRight and ArrowLeft do nothing, an inside drop lands after the row
  (tree.ts flatZone, flatMove) and flatten sends parent_id null
- vitest flat cases; docs describe the flat page; rebuilt dist
2026-10-14 16:05:10 +02:00
Jakub Zych
cf63fb3152 feat(18.1-01): task 2 taglist string mode, customTags and all save sites
- string mode: split on the separator (comma or whitespace), normalised,
  stored joined by a comma or one space; null stores an empty string
- customTags false refuses a tag outside options (taglist.not_allowed)
- taglist checked and hydrated on relation forms and settings forms;
  settings bind taglist columns; required checked on the tags
- admin: TaglistField string mode and closed option lists, preview renders
  the tags read-only as a comma-separated list; rebuilt dist
- docs: Taglist section covers both modes, separator and customTags
2026-10-14 15:52:52 +02:00
Jakub Zych
349bad5596 feat(18.1-01): task 1 taglist field in array mode end to end
- cabana: core type taglist (mode string|array, separator, customTags,
  options as a list of tags), column type gate, save-time normalisation
  (trim, drop empties, dedupe) and taglist.invalid 422, record hydration
- FormField gains separator and customTags (omitempty); OpenAPI and
  schema.d.ts regenerated
- admin: TaglistField chip input with suggestions, registry entry, vitest
- acme.catalog fixture with TestTaglistFieldCompile and TestTaglistFieldSmoke
- docs: forms.md Taglist section, cabana README; rebuilt dist
2026-10-14 15:47:30 +02:00
Jakub Zych
1e841b4281 feat(18-01): write-only settings secrets, richeditor and tabs on settings forms (task 3)
- type: password on a settings form binds a lagoon.Encrypted column as a
  write-only secret (any other column fails boot naming the field): the
  GET omits it from data and reports stored, env or empty per field in
  meta.secrets; on PUT an absent key or "" keeps the value, null clears
  it, a non-string is a 422; the answer never carries a value
- cabana.SecretFallbacks (field to config key) gives the env state;
  cabana.SettingsMeta, SecretStored/SecretEnv/SecretEmpty exported
- settings forms accept richeditor: the Put sanitizes through seaglass
  like a controller save and returns meta.notices; repeater and
  dependsOn stay refused (settingsFormOnly next to controllerFormOnly)
- backend::lang secret.* phrases in en and pl
- admin SPA: PasswordField secret status line (stored with Remove stored
  value, env, empty, pending clear with Undo), SettingsFormView tabs with
  error-tab activation and the secrets/notices meta, settings payload
  that never sends an untouched secret; regenerated OpenAPI and TS
  types; rebuilt dist
- vault fixture, TestSettingsSecretCompile, Docker TestSettingsSecretSmoke
  and TestSettingsRichEditorSmoke, conform settings fixture with a
  password field; settings.md sections, forms.md notes, cabana README
2026-10-13 11:36:12 +02:00
Jakub Zych
0c8605123e feat(18-01): money list column, danger record actions, preview Edit rule and table kit (task 2)
- columns.yaml type: money with currency or currencyFrom (exactly one),
  never searchable, not sortable by default; every list row carries the
  currencyFrom column's value under its own key; the schema serves
  currency and currencyFrom on the column
- pact.AdminRecordAction.Danger, served as danger on cabana.RecordAction
  (absent when false); RecordActions.vue passes it to the confirm dialog
- CellValue money kind through formatMoney with the row's code, the em
  dash when empty and the amount alone without a code; DataTable aligns
  money headers and cells to the right
- PreviewView renders Edit only when a registered value field allows
  update (F6)
- main.css .summer-table* and .summer-stat__note kit classes (F7) over
  --c-* variables; cabana README kit rows
- fixture record actions void (danger) and resend; TestMoneyListCompile,
  TestMoneyListSmoke and TestRecordActionDangerSmoke; regenerated OpenAPI
  and TS types; rebuilt dist; lists-and-filters and admin-controllers docs
2026-10-13 11:22:36 +02:00
Jakub Zych
20385e53ee feat(18-01): add the cabana money field (task 1)
- type: money on fields.yaml with currency or currencyFrom, scale (2..4,
  default 4) and max; boot fails naming the field on two sources, none,
  a bad scale or max, a column that is not a string or a Scanner/Valuer,
  or a currencyFrom that is not a string column
- a save answers 422 money.invalid, money.too_precise or money.too_large
  on the field and fills the accepted decimal string unchanged; the same
  check runs on controller, settings and relation child saves
- backend::lang money.* phrases in en and pl
- admin SPA: money.ts (parseMoneyInput, trimMoneyDisplay, formatMoney over
  Intl with the string itself), MoneyField.vue with the currency suffix,
  blur validation and the read-only box, preview through the control,
  the field scope on PreviewView; regenerated OpenAPI and TS types,
  rebuilt modules/boardwalk/dist
- acme.payments fixture with TestMoneyFieldCompile and the Docker
  TestMoneyFieldSmoke; forms.md Money section and cabana README
2026-10-13 11:15:05 +02:00
c1d767f0f8 fix(admin): sanitize the richeditor read-only display with DOMPurify (T-10-16)
- displayHtml uses a dedicated DOMPurify 3.4.16 instance: EDITOR_TAGS, href
  only on <a> through isAllowedLink, DROPPED elements go with their content
- string in, string out: parsed into DOMPurify's inert document, nothing
  adopted into the live document before binding
- fails closed (empty string) unless isSupported and a fixed canary hold
- Vitest browser mode in Playwright Chromium (npm run test:browser) covers
  the clobbering vectors, off-origin form, iframe, meta refresh, links and
  the inert parse; happy-dom keeps engine-independent assertions
2026-10-12 20:35:57 +02:00
872fd6535f feat(17-02): tree reorder page and singleton form in the admin
- ReorderView: indented tree with drag handle, move up/down, outdent and
  indent (buttons and arrow keys on the handle), pointer drag before, after
  or inside a row and never into its own subtree; one POST with the whole
  node set 400 ms after the last move, flushed on leave; a failed save
  restores the saved tree, a 409 reloads; loading, empty, load-failure and
  403 states; partition selector
- ListView: outline Reorder button before New record, passing the filtered
  partition
- SettingsIndexView opens a singleton controller's record through
  GET …/singleton; FormView in singleton mode has no Delete and returns to
  the settings index
- reorder.* and messages.list.reorder phrase keys (en, pl), smoke spec, docs
2026-10-12 16:18:31 +02:00
d3bf5d8f86 feat(17-02): repeater field and dependent dropdown states
- RepeaterField: rows with drag handle, index, titleFrom title, error chip,
  move up/down (buttons, ArrowUp/ArrowDown on the handle, native drag and
  drop), expand/collapse, remove (asks only for a row holding a value),
  add with nested defaults, maxItems counter, empty state, collapse all;
  every change edits form state only
- 422 row paths (content.3.answer) open the row, mark it and focus the
  nested control; the banner no longer repeats row messages
- DropdownField dependsOn: waiting, loading, not used, empty, loaded with
  depth indent, failed with retry; cached per sibling value, last request
  wins, a stale value is cleared after a sibling change and announced
- FormView provides form feedback (errors and notices by row path, the
  polite live region, its confirm dialog) and the options cache; FormGrid
  provides the sibling scope for repeater rows
- repeater.* and form.dependent_* phrase keys (en, pl), smoke specs, docs
2026-10-12 16:08:23 +02:00
cb2987c146 feat(17-02): richeditor field on TipTap with the untouched rule
- RichEditorField: Reka toolbar, link popover with the http/https/mailto/tel
  and relative URL policy, Edit HTML view, lossy note, skeleton while the
  lazily imported TipTap chunk loads, HTML view fallback when it fails
- untouched values are submitted byte-identical; after an edit the value is
  the editor HTML (empty paragraph -> ""); a value from outside (the save's
  sanitized answer) reloads the editor
- read-only renders through a client display allowlist, never as stored
- TipTap 3.31.4 (core, pm, starter-kit) pinned exact after the package check
- richeditor.* phrase keys (en, pl), smoke specs, forms docs
2026-10-12 15:56:59 +02:00
73074ee257 fix(admin): keep plugin rail and section panel sticky so Settings stays at the viewport bottom 2026-10-11 14:34:24 +02:00
cb4b0a9c75 fix(16.3): disable Create token until a scope is selected
Client-side canSubmit now matches the 422 rule so Submit stays disabled
with an empty scope set (WR-02).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-11 13:53:32 +02:00
1c930b6ff7 feat(16.3-02): list states, show-revoked toggle, and revoke via ConfirmDialog
Add skeleton/empty/error coverage, revoked-row handling, ConfirmDialog
overflow wrap for long names, and rebuild boardwalk dist.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-11 02:04:54 +02:00
5a6a190e7f feat(16.3-02): tracer — API tokens card on My account with show-once create
Wire ApiTokensPanel after the profile form, add the two-step create dialog,
phrase keys, smoke tracer, and rebuilt boardwalk dist.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-11 02:02:44 +02:00
85fe541b03 feat(admin): View site link next to the user menu
Opens the admin origin's root in a new tab, like the WinterCMS backend.
2026-10-10 14:01:44 +02:00
4adb48a76e feat(16.2-01): translatable plugin fields, element uploads and save notices in the admin
- MLPluginField wraps PluginField in the MLField locale switcher; the
  element gets the active content locale and that locale's string
- isMLField treats a translatable plugin field as a locale map in the form
  state, FormView adopt and the relation child modal
- summer-upload posts the file with the admin session to the field upload
  route and answers summer-upload-result {id, url} or {id, error}
- noticeLines shows meta.notices under each field as a warning status line
  (text only) and in the saved toast; the stored value is reloaded
- docs/backend/field-types.md documents the full element contract;
  modules/boardwalk/dist rebuilt
2026-10-10 11:25:26 +02:00
83fa2f7b2c feat(16.2-01): host plugin field type elements in the admin SPA
- loadFieldTypes fetches GET /field-types after sign-in, loads each script
  once and adds the styles as global links activateStyles never disables
- PluginField creates the type's element, sets the value property and the
  contract attributes, takes summer-change as the form value and never
  writes back a value the element just emitted
- rendererForField and isRegisteredField route any field with an element
  to PluginField in forms, previews and the save payload
- rebuilt modules/boardwalk/dist
2026-10-10 11:10:41 +02:00
64d21c8caf feat(cabana): add backend user avatars to My account and the top bar
- cabana.BackendUser is an attach.Owner (MorphName Backend\Models\User,
  cabana.BackendUserMorphType) with one public attachOne avatar
  (cabana.BackendUserAvatarField), as WinterCMS stores it
- POST /account/avatar stores one file_data image (jpg, jpeg, png, gif or
  webp, image guard, at most cabana.AvatarMaxBytes = 2 MiB, under a
  server-generated name) and replaces the previous avatar: old rows are
  deleted in the transaction, their blobs after the commit
- DELETE /account/avatar removes the avatar and is idempotent
- GET /auth/me and the account routes return avatar {url, thumb_url}
  (250x250 crop); cabana.BackendUserAvatar lets plugins read it
- swag annotations, regenerated admin.json and schema.d.ts; route
  inventories, CSRF walk (29) and OpenAPI conformance learn both routes
- SPA: avatar upload and remove on My account, avatar image in the user
  menu trigger and header; backend::lang.myaccount avatar strings
- rebuilt boardwalk dist; cabana README, users-and-permissions and
  admin-spa docs
2026-10-09 17:45:43 +02:00
143eca5a1f feat(cabana): add the My account page with subtitle and password change
- lagoon: 202610090001_backend_users_metadata adds WinterCMS's nullable
  jsonable backend_users.metadata column (TEXT); rollback drops only it
- cabana.BackendUser.Metadata and Subtitle read metadata.subtitle leniently
  (NULL, blank, null, [], invalid JSON and non-strings read as empty)
- GET /auth/me returns subtitle; PUT /account edits the signed-in admin's
  own names, email and subtitle, and the password with current_password;
  any other key is a 422 and nothing is written
- email: required, valid, 6-255 chars, unique against other admins' email
  and login ignoring case (soft-deleted included); a unique-index race is 422
- a password change sets tokens_valid_after, revokes the presented token and
  gives a cookie session a fresh cookie
- swag annotation, regenerated admin.json and schema.d.ts; route inventories,
  CSRF walk (27) and OpenAPI conformance learn the route
- SPA: My account item in the user menu opens /account (AccountView), which
  saves through PUT /account and updates the top bar via setCurrentUser
- backend::lang.myaccount strings (en, pl), rebuilt boardwalk dist
- cabana and lagoon READMEs, users-and-permissions and admin-spa docs
2026-10-09 17:38:04 +02:00
Jakub Zych
a714bb4629 fix(admin): name records by translatable (mltext) fields
The record heading, breadcrumb crumb and document title took the first
plain text field only, so records named by an mltext field fell back to
the generic update message. A shared recordDisplayName helper now takes
the first text or ML field and reads its default locale, then the first
non-empty one.
2026-10-07 19:05:47 +02:00
Jakub Zych
0b09acdb0c feat(admin): open controller-link settings entries from the settings index
A settings entry with a controller opens that controller's route from
its card (settingsPath); singleton cards still open /settings/<code>.
On a linked controller with no navigation entry of its own the rail
marks Settings current, the breadcrumbs read Settings > entry label
(plus the record title on record and create routes) and the page title
uses the entry label. The settings docs describe the admin behaviour;
the embedded admin shell is rebuilt.
2026-10-07 18:24:54 +02:00
Jakub Zych
b040ee3a74 fix(admin): size the switch toggle in pixels at the 14px root
The admin sets the html font size to 14px, so the rem utilities w-11 and
size-5 shrank the track to 38.5px and the knob to 17.5px against the
fixed 26px height and 18px travel. Use w-[44px] and size-[20px] so the
knob sits in a symmetric 3px inset at both ends. Rebuild the embedded
admin shell.
2026-10-07 18:15:41 +02:00
Jakub Zych
9b3e630b6b fix(admin): fit long plugin labels in rail 2026-10-07 10:39:22 +02:00
Jakub Zych
840af3e922 fix: give long plugin labels more room 2026-10-07 00:07:49 +02:00
Jakub Zych
a913eec50f feat(admin): render mltextarea fields per locale
- MLField.vue is the one locale-switching wrapper; MLTextField,
  MLMarkdownField and the new MLTextareaField are shells around it
- isMLFieldType replaces the inline ML type-name checks in formState,
  FormView and RelationChildModal
- register mltextarea as a group-labelled control; rebuild dist
2026-10-06 21:36:04 +02:00
Jakub Zych
b5d850662c feat(admin): follow presets on mltext fields per locale
- formState: changedLocales and presetUpdates hold the preset-follow rules
  for text and mltext pairs
- FormView applies presetUpdates on create, tracks per-locale hand edits of
  ML fields and follows the active ML locale
- rebuilt modules/boardwalk/dist
2026-10-06 21:18:09 +02:00
Jakub Zych
a0116dfbb9 fix(admin): render markdown preview from server-sanitized HTML
- MarkdownField posts the source to POST /markdown/preview when Preview
  opens and again 300 ms after a change while open; stale answers dropped
- the pane binds only data.html of a 2xx answer; a refusal is a text notice
- mlmarkdown previews the active locale and follows a locale switch
- .summer-markdown style kit restores headings, lists, code and tables
- vitest coverage, forms.md and rebuilt modules/boardwalk/dist
2026-10-06 20:57:51 +02:00
Jakub Zych
2f03128a58 fix(admin): drop ML copy-from locale chrome (quick-261006-s0v)
Copy overwrote the active locale with the other locale's often-empty string, so the editor went blank. Keep a single synchronized locale picker.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-06 20:12:47 +02:00
Jakub Zych
424fe9ccc0 feat(14.2.1-05): cover mlmarkdown, child adopt, and regen admin artifacts
MLMarkdownField and relation-child forms share the enabled-locale
inject and adopt merge; OpenAPI, types, docs, and boardwalk dist match.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-06 15:41:46 +02:00
Jakub Zych
0f8d13738d feat(14.2.1-05): hydrate ML create/GET/save through TranslatedExact
Form schema meta lists enabled locales, Show/save expand mltext maps
without D-11 fallback, and the SPA seeds and adopt-merges every locale.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-06 15:37:00 +02:00
Jakub Zych
a04116d41f feat(14.2.1-03): add markdown and multilingual cabana fields
Lift locale maps before ProjectWritableFields so a Journal-shaped save can persist the default host scalar and non-default locales through TranslationWriter without dropping nested JSON.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-06 12:32:54 +02:00
Jakub Zych
c6f68e4639 feat(admin): widget field payload and summer-result data channel (quick-261006-eyj)
- WidgetField posts the summer-action event's detail.payload as payload only
  when the detail carries one; a payload-less post body is unchanged
- after a successful action the response data is set on the element as the
  data attribute (removed when the answer has none) and announced with a
  summer-result event carrying {data, fill, message}; failures dispatch nothing
- WIDGET_RESULT_EVENT exported from formContext; unit tests for both directions
- modules/boardwalk/dist rebuilt
2026-10-06 11:16:46 +02:00
Jakub Zych
df5cace852 feat(12.1-02): writable foreign keys, locked relation options, invisible columns
- FieldRelationContract.WritableForeignKey makes a belongsTo field over a
  protected foreign key writable; the protected key list is unchanged
- cabana.RelationLockProvider names related ids an administrator may not add
  or remove: options and labels carry locked, and a create or update that
  changes the locked subset is 403 before any row is written
- columns.yaml invisible keeps a column searchable and out of the rows
- a controller implementing pact.FilterOptions serves a scope filter's
  choices before the model
- SPA: locked chips and options in RelationField, DataTable skips invisible
  columns
- README, docs, OpenAPI document, TS types and dist updated
2026-10-05 10:58:38 +02:00
Jakub Zych
f50d9b8f10 feat(12.1-02): permissioneditor field in radio or checkbox mode
- type: permissioneditor with mode radio (1, -1) or checkbox (1); the
  controller serves the options per request through
  cabana.PermissionEditorProvider and reads and stores the values
- a save answers 422 for a non-object, an unknown code or a value outside the
  mode's set and 403 for a changed locked code; stored codes that are not
  offered are kept
- record responses carry the stored permissions as an object
- SPA: PermissionEditorField with sections by tab, locked rows and a read-only
  mode for the preview
- README, docs, OpenAPI document, TS types and dist updated
2026-10-05 10:44:50 +02:00
Jakub Zych
a1c6bb1ce6 feat(12.1-02): password and form-only fields, rules per operation and preset
- pact.FormVirtualFields lists form fields that are not model columns: never
  bound, filled or projected; their values reach the Form hooks through
  cabana.VirtualFieldsFromContext when the field's context allows the operation
- type: password is a masked field that must be listed as virtual
- pact.FormRules supplies the rule set per operation and replaces the model's
  Rules() for admin saves; a rule on a virtual field sees the submitted value
- preset on a text field follows another text field on the create form
- SPA: PasswordField, preset handling in FormView, empty password left out of
  an update
- README, docs, OpenAPI document, TS types and dist updated
2026-10-05 10:35:08 +02:00
Jakub Zych
a65c670574 feat(12.1-02): read-only preview screen with a status hint and record actions
- config_form.yaml preview block (optional headerPartial), reported in the form schema as preview
- fields with context: preview show only on the preview screen and are never written
- form messages preview and edit; recordActions without a preview block stops boot
- SPA route {id}/preview, PreviewView and PreviewField, record actions in the footer
- mapWinterUrl maps preview/:id; the update form returns to the preview
- summer-callout partial style classes for status hints
- README, docs, OpenAPI document, TS types and the embedded build updated
2026-10-05 00:10:48 +02:00
Jakub Zych
71073bc8a2 feat(12.1-01): cabana.ForbiddenError answers a refused write with 403
- hooks and bulk, record, toolbar and widget actions may return it
- 403 forbidden with the localized message and field details; the write's
  transaction is rolled back; other errors stay the opaque 500
- form shows a refused save as a persistent banner and keeps the values;
  a refused delete is a toast
- smoke tests, OpenAPI notes, dist, README, docs
2026-10-04 23:53:34 +02:00
Jakub Zych
61d5fc72ad feat(12.1-01): list row states from one controller call per page
- pact.ListRowStates with the fixed RowState set deleted, negative, disabled
- list response meta.row_states keyed by row id; unknown values dropped
- list messages rowStateDeleted, rowStateNegative, rowStateDisabled
- update writes through the scope the load used, so a soft-deleted record
  a controller includes stays soft-deleted
- DataTable row state badges and text styles
- roster fixture, smoke tests, OpenAPI, TS types, dist, READMEs, docs
2026-10-04 23:45:44 +02:00
Jakub Zych
e0ccced76a feat(12.1-01): declared record actions with an applicability rule
- pact.HasAdminRecordActions with AdminRecordAction (Applies, Run)
- config_form.yaml recordActions, compiled fail-loud
- show response meta.actions lists the permitted actions that apply
- POST .../{controller}/{id}/actions/{action}: record loaded and locked
  through the form scope; 404 out of scope, 409 when it does not apply
- RecordActions.vue with confirm and request flow (mounted by plan 02)
- roster fixture, smoke tests, OpenAPI, TS types, READMEs, docs
2026-10-04 23:37:30 +02:00
Jakub Zych
a879d6388c feat(12.1-01): declared bulk actions on admin lists
- pact.HasAdminBulkActions with AdminBulkAction, its input and result
- config_list.yaml bulkActions, compiled fail-loud, needs showCheckboxes
- POST .../{controller}/bulk/{action}: ids resolved and locked through the
  list scope in one transaction; partial selection is 409
- list schema offers declared actions per principal, with confirm text
- admin SPA bulk actions menu with confirm, busy state and failure toasts
- acme.roster fixture, tracer test, OpenAPI, TS types, dist, READMEs, docs
2026-10-04 23:28:30 +02:00
Jakub Zych
55314e41f8 fix(admin): datetime popover clock, unsaved confirm, inferred list dates
BM UAT on v0.1.1 showed a date-only calendar for datetime fields, a stuck discard dialog, and raw ISO timestamps when columns.yaml omitted type. The picker now edits time in the popover, confirm sits above the calendar, and omitted time.Time / Date / TimeOfDay columns compile as datetime / date / time.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-03 05:58:24 +02:00
Jakub Zych
516f9c9025 fix(12.2): close code-review blockers on uploads, JSON caps, and pivot fill
Keep form save behind in-flight uploads, make retries idempotent via X-Upload-Id, cap remaining JSON bodies, and surface pending pivot type errors instead of zeroing them.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-02 23:10:48 +02:00
Jakub Zych
5b06b20ac3 fix(12.2-05): mark a partly filled time-mode datepicker invalid
The partial-segment check read only DateField segments, so a time-mode
field (Reka TimeField, data-reka-time-field-segment) with some segments
typed was never flagged. Read both segment attributes. DatepickerField
tests cover both UI-SPEC backstops (Escape focus and disabled days, the
fixed-zone datetime round trip) and the partly filled time case.
2026-10-02 20:37:38 +02:00
Jakub Zych
69a37456f6 feat(12.2-04): edit related records in modals and defer relation work on new records
- RelationManager renders create/link/delete/unlink in declared order with
  one primary, opens the child modal (update or view form) or the pivot
  modal on row click, and deletes selected children behind a busy confirm
- RelationChildModal creates and edits children with its own session key
  (X-Child-Session-Key) so uploads and dates work inside it
- RelationPivotModal edits link details; the picker links one record with
  its pivot values when the relation has a pivot form
- deferrable managers render on the create screen with owner id 0, the
  form's X-Session-Key and the pending note, and mark the form dirty
- the registry resolves RelationManager lazily (child forms close an
  import cycle); DataTable gains openable rows and a trailing cell
- relation lang keys in en and pl; rebuilt boardwalk dist
2026-10-02 19:56:10 +02:00
Jakub Zych
a0c182745e feat(12.2-04): add the datepicker field and stored date and time list cells
- pin @internationalized/date 3.12.4 as a direct admin dependency (approved)
- dateFormat.ts parses and emits date, datetime (local display, UTC emit,
  ignoreTimezone wall clock) and time values without the global Date
- DatepickerField on Reka DatePicker and TimeField with locale segments,
  calendar popover, clear button, min/max and yearRange bounds
- list cells of type date and time render the stored string
- datepicker lang keys in en and pl; rebuilt boardwalk dist
2026-10-02 19:45:43 +02:00
Jakub Zych
ea33296799 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
2026-10-02 19:22:22 +02:00
Jakub Zych
a5e7dac10f feat(10.1-02): run registered toolbar actions and scope plugin CSS per controller
- ListToolbar renders server-filtered actions after delete as outline buttons, busy during their POST
- ListView posts {} to toolbar/{action}, toasts, reloads the list and refetches the header partial
- Lists load controller assets too, so other controllers' stylesheet links are disabled on every open
- Vite dev server proxies {prefix}/assets to summer serve; dist rebuilt
2026-09-29 02:13:26 +02:00
Jakub Zych
9df9fae930 feat(10.1-02): render header and form partials through an allowlisted node renderer
- partialNodes rebuilds the server node tree with h() under the server's tag, attribute and URL lists
- PartialHost owns the skeleton, empty and failure states and keeps nodes visible on refetch
- type: partial is a valueless group-labelled field rendered on create and update
- ListView shows headerPartial above the list card and refetches it after bulk delete
- summer-partial and summer-stats style kit in main.css, documented in the cabana README; dist rebuilt
2026-09-29 02:10:18 +02:00
Jakub Zych
107d820109 feat(10.1-02): mount plugin widget elements and run their actions from the form
- pluginAssets loads controller scripts and stylesheets from {base}/assets/ only, once per URL
- WidgetField mounts the custom element with attributes only and posts summer-action through the typed client
- Only declared fill keys returned by the server are patched; the form turns dirty and nothing saves
- widget is a registered valueless type rendered on create and update, labelled as a group
- backend::lang.extension strings in en and pl; embedded dist rebuilt
2026-09-29 02:04:34 +02:00
Jakub Zych
1c2a66df45 test(10-05): bring every SPA module, composable and component under Vitest
- 41 unit and component suites under admin/tests/{app,state,shell,list,form,relation,views,ui}
  covering states and a11y roles; every src module is imported by a test
- typed fixture helper assigns each JSON fixture to its generated OpenAPI type
- fix: iconFor ignores inherited object members such as "constructor"
- fix: field controls import ./control instead of the registry (import cycle
  left a renderer unregistered depending on module load order)
- fix: dropdown shows the placeholder for an unknown stored value next to an emptyOption
- fix: list announces a failed schema load even when the rows arrive after it
- tailwind no longer scans admin/tests; boardwalk/dist rebuilt
2026-09-27 17:53:57 +02:00