You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The JSON-driven manifest renderer (PR #89) dispatches pages[].type to a Cn*Page component:
type
dispatches to
"index"
CnIndexPage
"detail"
CnDetailPage
"dashboard"
CnDashboardPage
"custom"
component looked up in the registry
For index / detail / dashboard, the renderer forwards page.config as props (v-bind="config"). But CnIndexPage / CnDetailPage expect data props (objects, schema, pagination, loading, searchTerm, activeFilters, …) — not just configuration. The data has to be loaded by useListView(type, { objectStore })somewhere. Today that "somewhere" is a wrapper Vue component the consuming app authors:
That works (decidesk pilot — ConductionNL/decidesk#138 — uses this pattern), but it defeats the simplicity of type: "index": every standard list view still needs a hand-written wrapper.
Proposal
Have CnPageRenderer auto-wire the data composable for type: "index" / "detail" when the manifest config carries an OpenRegister object type. New manifest shape:
Optionally accepts config.title, config.columns, etc. as direct props.
Same idea for type: "detail" with useDetailView(objectType, { id: $route.params.id }).
The type: "custom" registry escape hatch stays for views that need bespoke logic (e.g. inline edit forms, multi-source data merging).
Why this matters
Eliminates the per-view boilerplate decidesk currently has (and procest, pipelinq, opencatalogi, mydash will all hit the same gap).
Makes the manifest truly declarative for standard CRUD pages — no Vue file per route.
Aligns with ADR-022 ("apps consume OpenRegister abstractions"): apps stop owning the index/detail wiring; the library does it once.
Future app builder (the dynamic backend that overrides the manifest) becomes much more useful: admins can create entire CRUD pages by editing JSON, no rebuild required.
Out of scope
Apps with non-canonical object stores. The decidesk pilot revealed that some apps still ship their own local objectStore (since removed in decidesk#138 per ADR-022). Auto-wire targets the canonical store; non-canonical stores would need to provide the same contract or stay on type: "custom".
Context
The JSON-driven manifest renderer (PR #89) dispatches
pages[].typeto aCn*Pagecomponent:type"index"CnIndexPage"detail"CnDetailPage"dashboard"CnDashboardPage"custom"For
index/detail/dashboard, the renderer forwardspage.configas props (v-bind="config"). ButCnIndexPage/CnDetailPageexpect data props (objects,schema,pagination,loading,searchTerm,activeFilters, …) — not just configuration. The data has to be loaded byuseListView(type, { objectStore })somewhere. Today that "somewhere" is a wrapper Vue component the consuming app authors:To route this through the manifest, the consuming app has to register the wrapper in
customComponentsand declare the page astype: "custom":{ "id": "Decisions", "type": "custom", "component": "DecisionsView" }That works (decidesk pilot — ConductionNL/decidesk#138 — uses this pattern), but it defeats the simplicity of
type: "index": every standard list view still needs a hand-written wrapper.Proposal
Have
CnPageRendererauto-wire the data composable fortype: "index"/"detail"when the manifest config carries an OpenRegister object type. New manifest shape:{ "id": "Decisions", "route": "/decisions", "type": "index", "title": "decidesk.decisions.title", "config": { "objectType": "decision", "columns": ["title", "outcome", "decisionDate", "isPublished"] }, "slots": { "create-dialog": "DecisionCreateDialog" } }When
config.objectTypeis present,CnPageRenderer(fortype: "index"):useListView(config.objectType, { objectStore })once on mount, using the canonicaluseObjectStore(or an injected/prop-supplied custom store).CnIndexPageas props/listeners.slots[]overrides as before (#create-dialog,#row-actions, etc. — already supported by PR feat: implement JSON manifest renderer #89's slot map).config.title,config.columns, etc. as direct props.Same idea for
type: "detail"withuseDetailView(objectType, { id: $route.params.id }).The
type: "custom"registry escape hatch stays for views that need bespoke logic (e.g. inline edit forms, multi-source data merging).Why this matters
Out of scope
objectStore(since removed in decidesk#138 per ADR-022). Auto-wire targets the canonical store; non-canonical stores would need to provide the same contract or stay ontype: "custom".slotsoverride map (PR feat: implement JSON manifest renderer #89).Related
type: "custom"registry as the current workaround.