Skip to content

feat(app-page): show pages, menu, schemas and flows on the app detail page - #439

Merged
rubenvdlinde merged 6 commits into
developmentfrom
feat/app-page-full-builder
Aug 24, 2026
Merged

feat(app-page): show pages, menu, schemas and flows on the app detail page#439
rubenvdlinde merged 6 commits into
developmentfrom
feat/app-page-full-builder

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

An app is navigation, pages, data and business logic. The app detail page showed none of those as lists, so building an app meant opening the app and using the in-page action menu. This puts the four structure tables on the page itself.

Three of the four were already built and mounted nowhere

PagesWidget, MenuWidget and SchemasWidget are implemented, carry REQ-OBADO-009 spec tags, and have passing unit tests — and grep -rl finds no importer for any of them.

The dashboard's own docblock claims a "Structural grid — Register / Schemas / Groups / Pages / Menu", while its template mounted only three widgets. They were built, spec'd, tested, and never rendered for a single user. That is the orphaned-capability shape gate 57 exists for, one layer up from services.

So this mounts them, fed by computed props the dashboard already had (activePages / activeMenu / activeSchemas).

The missing fourth

FlowsWidget lists the OpenRegister flows bound to the app.

  • Flows are a field on the Application record (ApplicationDetailActions.setFlows patches { flows }), not part of the version manifest, so activeFlows reads the record.
  • Rows deep-link into OpenRegister, which owns flows (ADR-022). The widget lists and hands off rather than wrapping OR's editor.
  • flowState() reports an absent enabled as "Unknown", not "Disabled". An unset field is not a value, and defaulting it to the innocent-looking answer is how a missing state quietly becomes a wrong one.

A dead affordance, now wired

SchemasWidget's add button emitted an event nobody listened to, logging "schema-create dialog not yet registered — deferred to schema-designer spec". That designer shippedSchemaDesignerList at /builder/:slug/schemas. The emit now routes there.

Renamed it add-schemaaddSchema for vue/v-on-event-hyphenation, which is safe precisely because nothing consumed it, and declared two emits that were fired but never declared.

A docblock that contradicted its own code

registerSlugForApp returns openbuild-{appSlug} while its comment claimed buildiq-{appSlug}. The code is right. The app id moved to buildiq and the CONTROL register moved with it (MigrateRegisterSlug, whose map is openbuild => buildiq and covers that one row), but per-app register slugs are frozen once objects live under them — every per-app register on a live instance is named openbuild-*. Comment corrected, with the reasoning recorded so the next reader does not "fix" the code to match a wrong comment.

Verification

Browser-verified against a real instance, not inferred from unit tests:

Pages table 2 rows (id / route / type / title)
Menu table 2 rows (label / route / order / section)
Schemas table 1 row + add button
Flows table empty state
eslint clean on changed files
vitest 1378 / 1378
phpcs clean
check-manifest PASS

Not in this PR

The four buttons (settings modal, setup wizard, walkthrough, support & donations) and trimming the in-page orange menu. Splitting them keeps this reviewable — this half is the load-bearing one and stands on its own.

… page

An app is navigation, pages, data and business logic. The app detail page
showed none of those as lists, so building an app meant opening the app and
using the in-page action menu. This puts the four structure tables on the
page itself.

Three of the four already existed and were mounted NOWHERE. PagesWidget,
MenuWidget and SchemasWidget are implemented, carry REQ-OBADO-009 spec tags,
and have passing unit tests, but `grep -rl` finds no importer. The
dashboard's own docblock claims a "Structural grid — Register / Schemas /
Groups / Pages / Menu", while its template mounted only three widgets. They
were built, spec'd, tested, and never rendered for a single user.

So this mounts them, fed by computed props the dashboard already had
(activePages / activeMenu / activeSchemas), and adds the missing fourth:

- FlowsWidget lists the OpenRegister flows bound to the app. Flows are a
  field on the Application record (ApplicationDetailActions.setFlows patches
  `{ flows }`), not part of the version manifest, so activeFlows reads the
  record. Rows deep-link into OpenRegister, which owns flows (ADR-022); the
  widget lists and hands off rather than wrapping OR's editor.
- flowState() reports an absent `enabled` as "Unknown" rather than
  "Disabled". An unset field is not a value, and defaulting it to the
  innocent-looking answer is how a missing state becomes a wrong one.

SchemasWidget's add button emitted an event nobody listened to, logging
"schema-create dialog not yet registered — deferred to schema-designer
spec". That designer shipped: SchemaDesignerList lives at
/builder/:slug/schemas. The emit now routes there.

Renamed that emit `add-schema` -> `addSchema` to satisfy
vue/v-on-event-hyphenation, which is safe precisely because nothing consumed
it, and declared two emits that were fired but never declared.

Also corrects a docblock that contradicted its own code: registerSlugForApp
returns `openbuild-{appSlug}` while its comment claimed `buildiq-{appSlug}`.
The code is right. The app id moved to buildiq and the CONTROL register
moved with it, but per-app register slugs are frozen once objects live under
them; every per-app register on a live instance is named `openbuild-*`.

Verified in a browser against a real instance, not just unit tests: created
an app and confirmed all four tables render with real rows (Pages 2, Menu 2,
Schemas 1, Flows empty-state). eslint clean, 1378/1378 vitest, phpcs clean,
check-manifest PASS.
Renaming SchemasWidget's emit from `add-schema` to `addSchema` fixed the
underlying vue/custom-event-name-casing warning, which left its entry in
eslint-suppressions.json describing a problem that no longer occurs. ESLint 9
treats an unmatched suppression as a failure, so Lint Check exited 2 with
"0 errors, 142 warnings" — the count was unchanged from development, and the
exit code came entirely from the stale entry.

`npx eslint src --prune-suppressions` removes exactly that one entry. eslint
now exits 0.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/buildiq @ af96f5f

Check PHP Vue Security License Tests
lint
phpcs ⏭️
phpmd ⏭️
psalm ⏭️
phpstan ⏭️
phpmetrics
eslint
stylelint
build
check-nc-floor
format
check-l10n-js
check-schema-l10n
composer ⏭️
npm ✅ 642/642
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-24 08:04 UTC

Download the full PDF report from the workflow artifacts.

These edit the app's chrome — its settings, its first-run wizard, its guided
tour — rather than any one page, so they belong on the app page rather than
in the in-page orange edit menu, which is being narrowed to page-local
actions.

Setup wizard needed no new editor. The Walkthrough Designer already hosts
BOTH editors as tabs, so the work was making the tab reachable: `mode` now
initialises from `?mode=setup`, and the button deep-links to it. Without that
the button would land the user on the walkthrough tab and leave them to find
the setup one, which is the kind of "technically it is there" that makes a
feature feel missing. Optional chaining on `$route` because the designer is
mounted in unit tests without a router.

Not included: Edit support & donation. Its modal (CnEditSupportModal) exists
in @conduction/nextcloud-vue but is not exported from the package index, so
it cannot be imported, and `manifest.support` was rejected by the v2 schema's
`additionalProperties: false`. Both are fixed in
ConductionNL/nextcloud-vue#748; the button lands here once that reaches a
published release. Shipping three working buttons now beats shipping four
where one is wired to nothing.

Browser-verified against a live instance: both buttons render on the app
page, Walkthrough routes to /builder/pet-store/walkthrough, and Setup wizard
routes to ?mode=setup and opens ON the Setup steps tab.

eslint exit 0, 27 applicationDetail tests, 14 WalkthroughDesigner tests,
11 WalkthroughDesignerHost tests all pass.
…docs

The tour walked a user to the Store and had them clone a template, then
stopped. Cloning is not building, so a new user finished the guided tour
without having made a schema, a page, or anything of their own.

It now builds the canonical pet store, which is the sample domain the academy
tutorials already use, so the tour and the docs teach the same thing:

  welcome -> Apps -> create Pet Store -> add a Pet schema -> add an index
  page over Pet -> open the app and add a pet -> done

Each step keeps a real `advanceOn`, anchored to routes that exist
(VirtualApps, SchemaDesigner, PageDesigner, BuilderHost) rather than to
invented ones, and the build steps carry `allowManualNext` so a user who does
it slightly differently is never trapped.

The final step now closes on a call to action that opens the documentation,
per the fleet rule that a walkthrough's last step points somewhere. It targets
the Documentation nav item, which already exists in the menu, so the CTA lands
on a real destination rather than a URL invented for the copy.

Voice-checked against the shared writing skill: no em-dashes, every sentence
under 16 words, every task starts with a verb, no praise, and no step whose
body restates its own task. check-manifest PASSES.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/buildiq @ ef57ef2

Check PHP Vue Security License Tests
lint ⏭️
phpcs ⏭️
phpmd ⏭️
psalm ⏭️
phpstan ⏭️
phpmetrics ⏭️
eslint ⏭️
stylelint ⏭️
build ⏭️
composer ⏭️ ⏭️
npm ⏭️ ⏭️
app:check-code ⏭️
info.xml ⏭️
REUSE ⏭️
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-24 08:23 UTC

Download the full PDF report from the workflow artifacts.

Two CI gates caught real gaps in the FlowsWidget commit.

`test:l10n` found "No flows bound to this app yet." used in the component but
absent from l10n/en.json, so the string would have shipped untranslatable.
Added via the repo's own extractor, plus the Dutch translation — the gate only
checks en.json, but Dutch is required fleet-wide, and a key present only in
English is a string Dutch users read in English.

`format` found FlowsWidget.vue not prettier-clean. Fixed with prettier --write;
the only change is wrapping one call's arguments.

The extractor also picked up "This application has no flows yet.", a
pre-existing missing key from another component that was already failing this
gate.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/buildiq @ 9ef1e52

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
test-l10n
check-gitignore
check-nc-floor
format
check-l10n-js
check-schema-l10n
composer ✅ 106/106
npm ✅ 642/642
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-24 09:31 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/buildiq @ c591db1

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
test-l10n
check-gitignore
check-nc-floor
format
check-l10n-js
check-schema-l10n
composer ✅ 106/106
npm ✅ 642/642
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-24 10:46 UTC

Download the full PDF report from the workflow artifacts.

…oads

The previous commit added "No flows bound to this app yet." to l10n/en.json
and l10n/nl.json, and `test:l10n` went green — that check reads the JSON.

Nextcloud does not serve the JSON. It serves l10n/*.js, which is generated
from it, and those still lacked the key. The string would have rendered
untranslated in every language while two l10n gates reported success.

`check:l10n-js` is the gate that can see the difference, and it is the one
that failed. Regenerated via the repo's own `l10n:build`; en.js and nl.js now
carry the key.
@rubenvdlinde
rubenvdlinde merged commit cde3372 into development Aug 24, 2026
83 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/buildiq @ 2340598

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
test-l10n
check-gitignore
check-nc-floor
format
check-l10n-js
check-schema-l10n
composer ✅ 106/106
npm ✅ 642/642
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman
Playwright
Hydra gates

Quality workflow — 2026-08-24 17:03 UTC

Download the full PDF report from the workflow artifacts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant