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
This commit is contained in:
@@ -255,6 +255,11 @@ func (s *service) mount(r pact.Router) {
|
||||
constrainController(g)
|
||||
g.Post("/{vendor}/{plugin}/{controller}/bulk-delete", requireAjax(s.bulkDelete))
|
||||
constrainController(g)
|
||||
// Declared bulk actions (D-09): ids are resolved through the list
|
||||
// scope before plugin code runs.
|
||||
g.Post("/{vendor}/{plugin}/{controller}/bulk/{action}", requireAjax(s.bulkAction))
|
||||
constrainController(g)
|
||||
g.Where("action", "[A-Za-z_][A-Za-z0-9_]*")
|
||||
// Runtime extension actions (Phase 10.1): cabana owns these routes, so
|
||||
// CSRF, auth and record scoping never depend on plugin code.
|
||||
g.Post("/{vendor}/{plugin}/{controller}/widgets/{field}", requireAjax(s.widgetAction))
|
||||
@@ -719,6 +724,20 @@ func (s *service) listSchema(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
view.ToolbarActions = allowed
|
||||
// The built-in delete stays; a declared bulk action is offered only
|
||||
// to an admin who may run it. A new slice: the cached schema is
|
||||
// never mutated.
|
||||
bulk := make([]BulkAction, 0, len(view.BulkActions))
|
||||
for _, entry := range view.BulkActions {
|
||||
if builtinToolbarActions[entry.Name] {
|
||||
bulk = append(bulk, entry)
|
||||
continue
|
||||
}
|
||||
if registered, ok := cc.BulkActions[entry.Name]; ok && Allows(principal, registered.Permissions) {
|
||||
bulk = append(bulk, entry)
|
||||
}
|
||||
}
|
||||
view.BulkActions = bulk
|
||||
view.Assets = s.controllerAssets(cc)
|
||||
meta := map[string]any{}
|
||||
if view.Meta != nil {
|
||||
|
||||
Reference in New Issue
Block a user