feat(app-page): show pages, menu, schemas and flows on the app detail page - #439
Merged
Conversation
… 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.
Contributor
Quality Report — ConductionNL/buildiq @
|
| 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.
Contributor
Quality Report — ConductionNL/buildiq @
|
| 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.
Contributor
Quality Report — ConductionNL/buildiq @
|
| 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.
Contributor
Quality Report — ConductionNL/buildiq @
|
| 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.
Contributor
Quality Report — ConductionNL/buildiq @
|
| 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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,MenuWidgetandSchemasWidgetare implemented, carryREQ-OBADO-009spec tags, and have passing unit tests — andgrep -rlfinds 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
FlowsWidgetlists the OpenRegister flows bound to the app.ApplicationDetailActions.setFlowspatches{ flows }), not part of the version manifest, soactiveFlowsreads the record.flowState()reports an absentenabledas "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 shipped —SchemaDesignerListat/builder/:slug/schemas. The emit now routes there.Renamed it
add-schema→addSchemaforvue/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
registerSlugForAppreturnsopenbuild-{appSlug}while its comment claimedbuildiq-{appSlug}. The code is right. The app id moved tobuildiqand the CONTROL register moved with it (MigrateRegisterSlug, whose map isopenbuild => buildiqand 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 namedopenbuild-*. 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:
eslintvitestphpcscheck-manifestNot 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.