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
This commit is contained in:
Jakub Zych
2026-10-05 00:10:48 +02:00
parent 1c99de5013
commit a65c670574
50 changed files with 1441 additions and 66 deletions

View File

@@ -51,7 +51,7 @@ const path = {
}
const controllerId = controllerIdFromParams(path)
const listPath = controllerPath(controllerId) ?? '/'
const mode: FormMode = route.name === 'create' ? 'create' : 'update'
const mode: Exclude<FormMode, 'preview'> = route.name === 'create' ? 'create' : 'update'
const recordId = mode === 'update' ? Number(route.params.id) : null
const schema = ref<FormView | null>(null)
@@ -152,6 +152,11 @@ const subtitle = computed(() =>
mode === 'update' ? message(schema.value?.messages.update, undefined, { name: recordName.value }) : '',
)
// An update form of a record that has a preview screen is entered from it,
// so back and Cancel return there (UI-SPEC S3); a delete still goes to the list.
const backToPreview = computed(() => mode === 'update' && !!schema.value?.preview)
const backPath = computed(() => (backToPreview.value ? `${listPath}/${recordId}/preview` : listPath))
const dirty = computed(
() =>
!loading.value &&
@@ -366,7 +371,7 @@ async function onLeave(): Promise<void> {
return
}
}
await go(listPath)
await go(backPath.value)
}
onBeforeRouteLeave(guard)
@@ -395,7 +400,7 @@ void load()
<Button
variant="outline"
data-action="back"
:aria-label="t('backend::lang.form.return_to_list')"
:aria-label="t(backToPreview ? 'backend::lang.form.return_to_preview' : 'backend::lang.form.return_to_list')"
class="size-10! px-0!"
@click="onLeave"
>