From 3a73fcffeeb60fbffd98f4c4cf6e0c859f3fd64a Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Thu, 27 Aug 2026 16:13:54 +0200 Subject: [PATCH 1/8] feat(walkthrough): show where flows are edited, without asking anyone to build one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This app ships a Flows page and its getting-started tour never mentions it, so the automation surface is reachable only by someone who already knows it is there. Measured across the fleet: 19 apps declare a walkthrough, 12 ship a flows page, and exactly one tour mentioned flows at all. The stop is deliberately view-only. `allowManualNext` with a `route-match` advance and `optional: true` means it points at the surface and lets the user walk past it — nothing here gates the tour on having built a flow, which is the difference between showing someone where something lives and making them do it before they may continue. `manifest.version` gets a minor bump because that is what `sinceVersion` is compared against: a returning user whose recorded seen-version equals the old manifest version would otherwise never be shown the new step. --- src/manifest.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/manifest.json b/src/manifest.json index c72c5a55..9a56a6a2 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/ConductionNL/nextcloud-vue/main/src/schemas/app-manifest-v2.schema.json", - "version": "1.0.0", + "version": "1.1.0", "dependencies": [ "openregister" ], @@ -18,6 +18,7 @@ { "id": "welcome", "sinceVersion": "0.2.11", "placement": "center", "title": "Welcome to Stackiq", "body": "Let's take a quick spin through your software catalogue. We'll register an organisation together so you can see how the pieces fit, and you'll add the record yourself.", "target": { "kind": "page", "ref": "Dashboard" }, "advanceOn": { "type": "manual" } }, { "id": "go-organisaties", "sinceVersion": "0.2.11", "placement": "right", "body": "Organisations are the heart of your catalogue: they own the contracts, modules and compliance records you track. Open Organisations from the menu to get started.", "task": "Click Organisations in the menu", "target": { "kind": "nav-item", "ref": "Organisaties" }, "advanceOn": { "type": "route-match", "route": "Organisaties" } }, { "id": "create-organisatie", "sinceVersion": "0.2.11", "placement": "bottom", "allowManualNext": true, "body": "Add your first organisation. Give it a name and save. Contracts, modules and compliance records attach to it afterwards.", "task": "Click New and save an organisation", "target": { "kind": "element", "ref": "index-add" }, "advanceOn": { "type": "object-created", "register": "stackiq", "schema": "organization", "capture": { "organisatieId": ":id" } } }, + { "id": "see-flows", "sinceVersion": "1.1.0", "placement": "right", "optional": true, "allowManualNext": true, "title": "Where the automation lives", "body": "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.", "task": "Open Flows in the menu", "target": {"kind": "nav-item", "ref": "FlowsMenu"}, "advanceOn": {"type": "route-match", "route": "Flows"} }, { "id": "done", "sinceVersion": "0.2.11", "placement": "center", "title": "Your catalogue has its first organisation", "body": "The Dashboard tracks the catalogue as it grows. The documentation covers the rest.", "task": "Open the documentation to keep going", "target": { "kind": "nav-item", "ref": "Documentation" }, "advanceOn": { "type": "manual" } } ] } From 9e440bf97d55f292c45d849eff6058fb08c6b19a Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Thu, 27 Aug 2026 16:24:04 +0200 Subject: [PATCH 2/8] chore(l10n): translate the three strings the Flows walkthrough stop adds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every string the tour puts on screen is user-facing. Dutch is written rather than machine-produced; "Flows" stays "Flows", which is what the existing catalogue already does for the term. Worth noting for whoever picks this up: this app's `tests/l10n/check-l10n.js` does NOT scan `src/manifest.json`, so it reported OK for these keys before they existed in any catalogue. The gate passed because it did not look, not because the strings were covered — every menu label, page title, setup step and tour line in the manifest is currently outside its scope. Only dossiq's copy of the checker reads the manifest. --- l10n/en.json | 5 ++++- l10n/nl.json | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/l10n/en.json b/l10n/en.json index 6fc1b54b..08b97c91 100644 --- a/l10n/en.json +++ b/l10n/en.json @@ -693,6 +693,9 @@ "Add a new contactpersoon to organisation: {name}": "Add a new contactpersoon to organisation: {name}", "Add contactpersoon": "Add contactpersoon", "Failed to add contactpersoon: {error}": "Failed to add contactpersoon: {error}", - "Invalid contactpersoon data structure": "Invalid contactpersoon data structure" + "Invalid contactpersoon data structure": "Invalid contactpersoon data structure", + "Where the automation lives": "Where the automation lives", + "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.": "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.", + "Open Flows in the menu": "Open Flows in the menu" } } diff --git a/l10n/nl.json b/l10n/nl.json index 11cc74a7..dc7f3a84 100644 --- a/l10n/nl.json +++ b/l10n/nl.json @@ -749,6 +749,9 @@ "Release notes & artefacts": "Releasenotes en artefacten", "Exposure": "Blootstelling", "Audit trail": "Audittrail", - "Flow": "Flow" + "Flow": "Flow", + "Where the automation lives": "Waar de automatisering zit", + "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.": "Flows zijn wat er gebeurt zonder dat iemand klikt: een herinnering voordat een termijn verstrijkt, een bevestiging bij indiening. Hier lees en bewerk je ze — je hoeft nu niets te bouwen.", + "Open Flows in the menu": "Open Flows in het menu" } } From 0bdbe96855f4cf1589fd269764ded636a89e72ea Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Thu, 27 Aug 2026 16:55:39 +0200 Subject: [PATCH 3/8] chore(l10n): rebuild the browser catalogues so the new strings actually ship MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `l10n/*.json` is the source; `l10n/*.js` is what the browser loads. Adding the three tour strings to the JSON left the built catalogues stale, so the strings existed in the repo and reached no user — the exact shape of the 2026-08-24 finding where nine apps shipped 8,137 translations no browser ever received. `npm run check:l10n-js` catches it (`Stale browser catalogue: l10n/en.js, l10n/nl.js`), which is why the gate exists. This is `npm run l10n:build` and nothing else. --- l10n/en.js | 5 ++++- l10n/nl.js | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/l10n/en.js b/l10n/en.js index 38c3ea72..8c625e12 100644 --- a/l10n/en.js +++ b/l10n/en.js @@ -694,7 +694,10 @@ OC.L10N.register( "Add a new contactpersoon to organisation: {name}": "Add a new contactpersoon to organisation: {name}", "Add contactpersoon": "Add contactpersoon", "Failed to add contactpersoon: {error}": "Failed to add contactpersoon: {error}", - "Invalid contactpersoon data structure": "Invalid contactpersoon data structure" + "Invalid contactpersoon data structure": "Invalid contactpersoon data structure", + "Where the automation lives": "Where the automation lives", + "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.": "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.", + "Open Flows in the menu": "Open Flows in the menu" }, "nplurals=2; plural=(n != 1);" ) diff --git a/l10n/nl.js b/l10n/nl.js index 5f63eaa8..b1288497 100644 --- a/l10n/nl.js +++ b/l10n/nl.js @@ -750,7 +750,10 @@ OC.L10N.register( "Release notes & artefacts": "Releasenotes en artefacten", "Exposure": "Blootstelling", "Audit trail": "Audittrail", - "Flow": "Flow" + "Flow": "Flow", + "Where the automation lives": "Waar de automatisering zit", + "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.": "Flows zijn wat er gebeurt zonder dat iemand klikt: een herinnering voordat een termijn verstrijkt, een bevestiging bij indiening. Hier lees en bewerk je ze — je hoeft nu niets te bouwen.", + "Open Flows in the menu": "Open Flows in het menu" }, "nplurals=2; plural=(n != 1);" ) From b5afe26592c80ef07c42a3a1217ac5d38dc6dbf7 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Thu, 27 Aug 2026 17:13:41 +0200 Subject: [PATCH 4/8] fix(walkthrough): target the flows entry by ROUTE, which is what resolves CnWalkthrough.resolveTarget() looks a nav-item target up as `[data-cn-route=""]`, and CnAppNav sets that attribute from `item.route`. The step was authored with the MENU id (FlowsMenu), which matches nothing, so it fell back to a centred anchorless coachmark instead of pointing at the entry. Easy to miss because every tour step in this fleet that works today targets an entry whose menu id happens to EQUAL its route (Cases, MyWork). FlowsMenu -> Flows is the first place they differ. Verified against the live DOM: [data-cn-route="Cases"] resolves, [data-cn-route="FlowsMenu"] does not. --- src/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifest.json b/src/manifest.json index 9a56a6a2..52d357d7 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -18,7 +18,7 @@ { "id": "welcome", "sinceVersion": "0.2.11", "placement": "center", "title": "Welcome to Stackiq", "body": "Let's take a quick spin through your software catalogue. We'll register an organisation together so you can see how the pieces fit, and you'll add the record yourself.", "target": { "kind": "page", "ref": "Dashboard" }, "advanceOn": { "type": "manual" } }, { "id": "go-organisaties", "sinceVersion": "0.2.11", "placement": "right", "body": "Organisations are the heart of your catalogue: they own the contracts, modules and compliance records you track. Open Organisations from the menu to get started.", "task": "Click Organisations in the menu", "target": { "kind": "nav-item", "ref": "Organisaties" }, "advanceOn": { "type": "route-match", "route": "Organisaties" } }, { "id": "create-organisatie", "sinceVersion": "0.2.11", "placement": "bottom", "allowManualNext": true, "body": "Add your first organisation. Give it a name and save. Contracts, modules and compliance records attach to it afterwards.", "task": "Click New and save an organisation", "target": { "kind": "element", "ref": "index-add" }, "advanceOn": { "type": "object-created", "register": "stackiq", "schema": "organization", "capture": { "organisatieId": ":id" } } }, - { "id": "see-flows", "sinceVersion": "1.1.0", "placement": "right", "optional": true, "allowManualNext": true, "title": "Where the automation lives", "body": "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.", "task": "Open Flows in the menu", "target": {"kind": "nav-item", "ref": "FlowsMenu"}, "advanceOn": {"type": "route-match", "route": "Flows"} }, + { "id": "see-flows", "sinceVersion": "1.1.0", "placement": "right", "optional": true, "allowManualNext": true, "title": "Where the automation lives", "body": "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.", "task": "Open Flows in the menu", "target": {"kind": "nav-item", "ref": "Flows"}, "advanceOn": {"type": "route-match", "route": "Flows"} }, { "id": "done", "sinceVersion": "0.2.11", "placement": "center", "title": "Your catalogue has its first organisation", "body": "The Dashboard tracks the catalogue as it grows. The documentation covers the rest.", "task": "Open the documentation to keep going", "target": { "kind": "nav-item", "ref": "Documentation" }, "advanceOn": { "type": "manual" } } ] } From cb3c356209874ebc56a562f435924cface7a8d82 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Thu, 27 Aug 2026 19:59:03 +0200 Subject: [PATCH 5/8] fix(copy): no em-dash in the tour copy, per voice.md section 8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gate-96 (manifest-copy-style) caught it: "Em-dashes and double-dashes are AI tells. Replace with a period, a comma, or a colon." The Flows stop's body ended "...read and edit them — nothing to build now."; it now ends with a full stop and a short sentence, which is what the rule asks for and reads no worse. The English string is the l10n KEY, so the catalogues are re-keyed in the same change and the browser .js rebuilt. Leaving the key behind would have made the string untranslated in every locale while the catalogue still claimed to cover it. The Dutch value drops its em-dash too, for the same reason the English one does. Verified per app with the script that app's own CI runs (test:l10n or check:l10n), plus check:l10n-js, plus schema validation of the manifest. --- l10n/en.js | 2 +- l10n/en.json | 2 +- l10n/nl.js | 2 +- l10n/nl.json | 2 +- src/manifest.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/l10n/en.js b/l10n/en.js index 8c625e12..e1da9d6e 100644 --- a/l10n/en.js +++ b/l10n/en.js @@ -696,7 +696,7 @@ OC.L10N.register( "Failed to add contactpersoon: {error}": "Failed to add contactpersoon: {error}", "Invalid contactpersoon data structure": "Invalid contactpersoon data structure", "Where the automation lives": "Where the automation lives", - "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.": "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.", + "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them. Nothing to build now.": "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them. Nothing to build now.", "Open Flows in the menu": "Open Flows in the menu" }, "nplurals=2; plural=(n != 1);" diff --git a/l10n/en.json b/l10n/en.json index 08b97c91..6d054cdb 100644 --- a/l10n/en.json +++ b/l10n/en.json @@ -695,7 +695,7 @@ "Failed to add contactpersoon: {error}": "Failed to add contactpersoon: {error}", "Invalid contactpersoon data structure": "Invalid contactpersoon data structure", "Where the automation lives": "Where the automation lives", - "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.": "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.", + "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them. Nothing to build now.": "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them. Nothing to build now.", "Open Flows in the menu": "Open Flows in the menu" } } diff --git a/l10n/nl.js b/l10n/nl.js index b1288497..3960d0e6 100644 --- a/l10n/nl.js +++ b/l10n/nl.js @@ -752,7 +752,7 @@ OC.L10N.register( "Audit trail": "Audittrail", "Flow": "Flow", "Where the automation lives": "Waar de automatisering zit", - "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.": "Flows zijn wat er gebeurt zonder dat iemand klikt: een herinnering voordat een termijn verstrijkt, een bevestiging bij indiening. Hier lees en bewerk je ze — je hoeft nu niets te bouwen.", + "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them. Nothing to build now.": "Flows zijn wat er gebeurt zonder dat iemand klikt: een herinnering voordat een termijn verstrijkt, een bevestiging bij indiening. Hier lees en bewerk je ze. Je hoeft nu niets te bouwen.", "Open Flows in the menu": "Open Flows in het menu" }, "nplurals=2; plural=(n != 1);" diff --git a/l10n/nl.json b/l10n/nl.json index dc7f3a84..e508ec4e 100644 --- a/l10n/nl.json +++ b/l10n/nl.json @@ -751,7 +751,7 @@ "Audit trail": "Audittrail", "Flow": "Flow", "Where the automation lives": "Waar de automatisering zit", - "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.": "Flows zijn wat er gebeurt zonder dat iemand klikt: een herinnering voordat een termijn verstrijkt, een bevestiging bij indiening. Hier lees en bewerk je ze — je hoeft nu niets te bouwen.", + "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them. Nothing to build now.": "Flows zijn wat er gebeurt zonder dat iemand klikt: een herinnering voordat een termijn verstrijkt, een bevestiging bij indiening. Hier lees en bewerk je ze. Je hoeft nu niets te bouwen.", "Open Flows in the menu": "Open Flows in het menu" } } diff --git a/src/manifest.json b/src/manifest.json index 52d357d7..24f8c075 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -18,7 +18,7 @@ { "id": "welcome", "sinceVersion": "0.2.11", "placement": "center", "title": "Welcome to Stackiq", "body": "Let's take a quick spin through your software catalogue. We'll register an organisation together so you can see how the pieces fit, and you'll add the record yourself.", "target": { "kind": "page", "ref": "Dashboard" }, "advanceOn": { "type": "manual" } }, { "id": "go-organisaties", "sinceVersion": "0.2.11", "placement": "right", "body": "Organisations are the heart of your catalogue: they own the contracts, modules and compliance records you track. Open Organisations from the menu to get started.", "task": "Click Organisations in the menu", "target": { "kind": "nav-item", "ref": "Organisaties" }, "advanceOn": { "type": "route-match", "route": "Organisaties" } }, { "id": "create-organisatie", "sinceVersion": "0.2.11", "placement": "bottom", "allowManualNext": true, "body": "Add your first organisation. Give it a name and save. Contracts, modules and compliance records attach to it afterwards.", "task": "Click New and save an organisation", "target": { "kind": "element", "ref": "index-add" }, "advanceOn": { "type": "object-created", "register": "stackiq", "schema": "organization", "capture": { "organisatieId": ":id" } } }, - { "id": "see-flows", "sinceVersion": "1.1.0", "placement": "right", "optional": true, "allowManualNext": true, "title": "Where the automation lives", "body": "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them — nothing to build now.", "task": "Open Flows in the menu", "target": {"kind": "nav-item", "ref": "Flows"}, "advanceOn": {"type": "route-match", "route": "Flows"} }, + { "id": "see-flows", "sinceVersion": "1.1.0", "placement": "right", "optional": true, "allowManualNext": true, "title": "Where the automation lives", "body": "Flows are what happens without anyone clicking: a reminder before a deadline passes, a confirmation sent on submission. This is where you read and edit them. Nothing to build now.", "task": "Open Flows in the menu", "target": {"kind": "nav-item", "ref": "Flows"}, "advanceOn": {"type": "route-match", "route": "Flows"} }, { "id": "done", "sinceVersion": "0.2.11", "placement": "center", "title": "Your catalogue has its first organisation", "body": "The Dashboard tracks the catalogue as it grows. The documentation covers the rest.", "task": "Open the documentation to keep going", "target": { "kind": "nav-item", "ref": "Documentation" }, "advanceOn": { "type": "manual" } } ] } From dbba8078efb218d07af6aad81f390e83cd72f826 Mon Sep 17 00:00:00 2001 From: Conduction Release Bot Date: Fri, 28 Aug 2026 07:57:15 +0200 Subject: [PATCH 6/8] build(deps): take @conduction/nextcloud-vue 2.21.0 so the Flows stop anchors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `see-flows` stop added by this PR targets a nav item in the SETTINGS section. CnAppNav emitted `data-cn-route` on its main, child and footer loops but not the settings one, so the stop resolved nothing — and CnWalkthrough.armStep() SKIPS an optional step whose target is absent, with no console error and nothing on screen: const el = this.resolveTarget(this.step) if (!el) { if (this.step.optional) { this.wt.skip(); return } } `optional: true` is exactly what keeps the stop from forcing anyone to build a flow, so the friendly authoring choice is also the one that fails silently. Without this bump the step ships and reaches nobody. The caret range does not decide this: `npm ci` installs from package-lock.json, and that was pinned at 2.19.0, which predates the fix (nextcloud-vue#811). 2.21.0 was verified by unpacking the published tarball — data-cn-route appears 4 times, one inside the `v-for="item in settingsItems"` template. --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2d0febe2..dc153d1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@babel/core": "^7.29.0", "@codemirror/lang-json": "^6.0.0", - "@conduction/nextcloud-vue": "^2.19.0", + "@conduction/nextcloud-vue": "^2.21.0", "@nextcloud/auth": "^2.6.0", "@nextcloud/axios": "^2.5.0", "@nextcloud/capabilities": "^1.2.1", @@ -2192,9 +2192,9 @@ } }, "node_modules/@conduction/nextcloud-vue": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/@conduction/nextcloud-vue/-/nextcloud-vue-2.19.0.tgz", - "integrity": "sha512-gSLl4RZ7hy1e2TBuzYZLiTLBMggRYgfuxFMva59JBhb1EP93GgvEd/HpQcg+zoBL9FJPgJw9ir2OGf9Tore7og==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@conduction/nextcloud-vue/-/nextcloud-vue-2.21.0.tgz", + "integrity": "sha512-5HjI4X8k2IYxUeBdHa2OK/MnLhOFf4HxkF5dUGl42dWmYPDaPHjvawDMZcUcyP6wsP+rk0QzmPov4FGu/4Rn7Q==", "license": "EUPL-1.2", "dependencies": { "@ckpack/vue-color": "^1.6.0", @@ -2256,7 +2256,7 @@ "dompurify": "^3.0.0", "eslint": "^8.56.0 || ^9.0.0 || ^10.0.0", "eslint-plugin-vue": "^9.21.0 || ^10.0.0", - "gridstack": "^12.0.0", + "gridstack": "^12.0.0 || ^13.0.0", "marked": "^12.0.0", "pinia": "^2.0.0 || ^3.0.0", "vue": "^3.5.0", diff --git a/package.json b/package.json index b46f5e14..ace9cccc 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "dependencies": { "@babel/core": "^7.29.0", "@codemirror/lang-json": "^6.0.0", - "@conduction/nextcloud-vue": "^2.19.0", + "@conduction/nextcloud-vue": "^2.21.0", "@nextcloud/auth": "^2.6.0", "@nextcloud/axios": "^2.5.0", "@nextcloud/capabilities": "^1.2.1", From 7c4cdf415e11af5a02f47163d4a7ee8f56d635f9 Mon Sep 17 00:00:00 2001 From: Conduction Release Bot Date: Fri, 28 Aug 2026 09:19:32 +0200 Subject: [PATCH 7/8] test(e2e): Edit opens the detail page, so the dialog is one click further MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @conduction/nextcloud-vue 2.21.0 makes the index row's Edit action navigate to the record's detail page instead of opening a modal over the list. That is the intended fleet rule: a record with its own detail page is edited there, where its nested collections are reachable, rather than through a dialog that shows only the schema's flat scalars. These two tests asserted the old shape and so failed with "element(s) not found" on `getByRole('dialog')` — the dialog was never going to appear, because the click now routes. The edit form still exists; it is reached from the detail page's Edit button (`cn-detail-page-edit`, gated on canEditRecord). Everything the tests actually check — the title field, the absence of a scheduledDate format alert, the save round-trip — is unchanged. --- tests/e2e/workflows/crud-persistence.spec.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/e2e/workflows/crud-persistence.spec.ts b/tests/e2e/workflows/crud-persistence.spec.ts index 7587c34e..3e62260b 100644 --- a/tests/e2e/workflows/crud-persistence.spec.ts +++ b/tests/e2e/workflows/crud-persistence.spec.ts @@ -204,6 +204,11 @@ test.describe('Contactpersoon CRUD-persistence', () => { await openRowActions(page, contactsUid) await clickAction(page, 'Edit') + // Edit on an index row NAVIGATES to the detail page now rather than + // opening a modal over the list (@conduction/nextcloud-vue 2.21.0): + // a record with its own detail page is edited there. The form is one + // click further on. + await page.getByTestId('cn-detail-page-edit').click() const editDialog = page.locator('[role="dialog"], .modal-container').first() await editDialog.waitFor({ state: 'visible', timeout: 15000 }) @@ -231,6 +236,11 @@ test.describe('Contactpersoon CRUD-persistence', () => { await openRowActions(page, contactsUid) await clickAction(page, 'Edit') + // Edit on an index row NAVIGATES to the detail page now rather than + // opening a modal over the list (@conduction/nextcloud-vue 2.21.0): + // a record with its own detail page is edited there. The form is one + // click further on. + await page.getByTestId('cn-detail-page-edit').click() const editDialog = page.locator('[role="dialog"], .modal-container').first() await editDialog.waitFor({ state: 'visible', timeout: 15000 }) // The edit form is pre-filled with the existing UID — proves the row From 321629b8db2b1f567826763cbf43e3b487fe18e1 Mon Sep 17 00:00:00 2001 From: Conduction Release Bot Date: Fri, 28 Aug 2026 10:34:09 +0200 Subject: [PATCH 8/8] test(e2e): return to the index before asserting the edit landed in the list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the detail-page edit route. The first of these two tests passed once the extra click was added; this one still failed, for a different reason. It edits the record and then asserts the new job title is rendered as a row in the Table view. That assertion assumed the edit happened in a modal OVER the list, so the list was still on screen when it ran. It is not: the row's Edit action routes to the record's detail page, the save happens there, and the page never goes back on its own. Without the return trip the assertion runs against the detail page and fails as "row not found" — which reads like the save not persisting rather than the test standing on the wrong page. --- tests/e2e/workflows/crud-persistence.spec.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/e2e/workflows/crud-persistence.spec.ts b/tests/e2e/workflows/crud-persistence.spec.ts index 3e62260b..01e1ee07 100644 --- a/tests/e2e/workflows/crud-persistence.spec.ts +++ b/tests/e2e/workflows/crud-persistence.spec.ts @@ -260,6 +260,14 @@ test.describe('Contactpersoon CRUD-persistence', () => { await page.waitForTimeout(2500) await dismissSupportDialog(page) + // Back to the index: the edit happened on the record's DETAIL page, + // because the row's Edit action routes there now instead of opening a + // modal over the list. Without this the list assertion below would run + // against the detail page and fail as 'row not found', which reads like + // the save not persisting rather than the test being on the wrong page. + await gotoAppRoute(page, '/contactpersonen') + await dismissSupportDialog(page) + // The edited job title is now rendered in the list (Table view) — // PERSISTED. `functie` is one of the page's declared columns. await showTable(page)