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
This commit is contained in:
Jakub Zych
2026-10-04 23:37:30 +02:00
parent a879d6388c
commit e0ccced76a
34 changed files with 1350 additions and 24 deletions

View File

@@ -54,6 +54,24 @@ type formConfigDocument struct {
Create *formRedirects `yaml:"create"`
Update *formRedirects `yaml:"update"`
Messages *formMessageKeys `yaml:"messages"`
RecordActions recordActionList `yaml:"recordActions"`
}
// recordActionList is the declarative recordActions list (D-10): the names of
// record actions the controller registers through pact.HasAdminRecordActions,
// in display order. Decode has no controller, so membership is resolved in
// compileExtension.
type recordActionList struct {
items []string
}
func (l *recordActionList) UnmarshalYAML(node ast.Node) error {
items, err := actionNameList(node, "recordActions", "recordActions must be a list of record action names the controller registers")
if err != nil {
return err
}
l.items = items
return nil
}
type formRedirects struct {
@@ -118,6 +136,9 @@ func CompileForm(pluginID string, ctl pact.AdminController, fsys fs.FS) (*FormSc
Fields: fields,
redirects: FormRedirects{Default: doc.DefaultRedirect},
fieldsPath: fieldsPath,
configPath: cfgPath,
recordActions: doc.RecordActions.items,
}
if doc.Messages != nil {
schema.messageKeys = *doc.Messages