feat(10-03): open, edit and save a record with toast and 422 feedback
- The SPA loads the backend::lang bundle before /auth/me, sets the document language from meta.locale and renders plural messages with Intl.PluralRules; interpolate mirrors phrasebook for :name/:Name/:NAME - Create and record routes; mapWinterUrl maps recordUrl and redirects onto the controller's list, create and record routes only - List rows open their record; FormView loads the form schema and the record, shows context-allowed fields in the span grid, saves values keyed by field name and toasts the resolved saved message - A 422 puts each message under its field (aria-invalid, aria-describedby), shows the plural banner, focuses the first invalid field in schema order and clears a field's error on change - The D-05 registry maps text, textarea, number and dropdown; any other type renders the unsupported-field box with the type in DM Mono - The admin OpenAPI document declares the write request bodies (AdminRecord, AdminIDsRequest) and the list filter query as a deepObject, so the typed client can send them - New backend::lang form.load_failed key; boardwalk/dist rebuilt
This commit is contained in:
39
admin/src/app/winterUrl.ts
Normal file
39
admin/src/app/winterUrl.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
// Winter-shaped URLs (config_list recordUrl, config_form redirects) mapped
|
||||
// onto the SPA's D-10 routes. The strings come from plugin YAML, so they are
|
||||
// never used verbatim: only the current controller's list, create and record
|
||||
// routes can come out (research Gap 8, T-10-20).
|
||||
//
|
||||
// <vendor>/<plugin>/<controller> -> list
|
||||
// <vendor>/<plugin>/<controller>/create -> create
|
||||
// <vendor>/<plugin>/<controller>/update/:id -> record (:id substituted)
|
||||
// anything else, or another controller -> list
|
||||
import { controllerPath, parseControllerId } from './controllerRoutes'
|
||||
|
||||
const DIGITS = /^\d+$/
|
||||
|
||||
export function mapWinterUrl(url: string | null | undefined, controllerId: string, id?: string | number | null): string {
|
||||
const params = parseControllerId(controllerId)
|
||||
const base = controllerPath(controllerId)
|
||||
if (!params || !base) {
|
||||
return '/'
|
||||
}
|
||||
const raw = (url ?? '').trim().split(/[?#]/, 1)[0] ?? ''
|
||||
const segments = raw.split('/').filter((segment) => segment !== '')
|
||||
const owner = [params.vendor, params.plugin, params.controller]
|
||||
const ownsUrl = owner.every((segment, index) => segments[index]?.toLowerCase() === segment.toLowerCase())
|
||||
if (!ownsUrl) {
|
||||
return base
|
||||
}
|
||||
const rest = segments.slice(owner.length)
|
||||
if (rest.length === 0) {
|
||||
return base
|
||||
}
|
||||
if (rest.length === 1 && rest[0] === 'create') {
|
||||
return `${base}/create`
|
||||
}
|
||||
if (rest.length === 2 && rest[0] === 'update') {
|
||||
const target = rest[1] === ':id' ? String(id ?? '') : (rest[1] ?? '')
|
||||
return DIGITS.test(target) ? `${base}/${target}` : base
|
||||
}
|
||||
return base
|
||||
}
|
||||
Reference in New Issue
Block a user