feat: frontend restyle — minimal nav, live dashboard, vault breadcrumbs, folder tree and vault-aware UI - #448
Merged
Merged
Conversation
Stage 1 of the original-look restyle. The Vault menu entry becomes \"All secrets\" with a key icon (KeyVariant registered in src/icons.js — unregistered icon names render no icon at all), and the Certificates and Emergency access entries move from the footer to the settings foldout. Documentation, Features & roadmap, My activity and Password health deliberately stay in the footer. Also removes the duplicated \"Personal settings\" opener: CnAppNav auto-prepends its own entry at the top of the settings foldout while the manifest already declares the UserSettings entry (action: user-settings), so the same dialog had two openers. nav.includePersonalSettings:false keeps the explicit manifest entry as the only one — the e2e specs assert it, and the upcoming manifest-driven nav override renders it directly. The manifest schema rejects _note on nav and menu entries, hence this explanation lives here. Placement assertions in page-surfaces.spec.ts follow the relabel and the two moved entries; the local restyle plan file is gitignored.
…resh data
Completes the Stage-2 dashboard restyle:
- recent-activity-text (the ADR-049 placeholder) is replaced by a live
feed off GET /api/v1/audit/me: When (relative time via
format:date-time), Action and Item columns, newest-first. The endpoint
is asked for 6 entries while the table shows 5, so the View-all footer
appears exactly when a sixth entry exists, linking to My activity.
- The pending-apps count banner grew into the real approval queue: an
object-table of the actual pending applications (name, registered by,
requested), capped at 3 with a View-all footer to the applications
page. Its def-level visibleWhen collapses the whole card - grid row
included - for non-admins and empty queues, decided before the grid's
first paint so nothing pops in and reflows the page.
- KPI color identity: total-secrets primary, folders success,
rotation-due error, shared-with-you deliberately theme-default. The
*-text token variants, NOT the bare tokens - on current server builds
the bare --color-error/--color-success are pale background tints
(verified live: #FFE7E7 / #D8F3DA) while the *-text variants are the
strong readable colors on both server generations.
- The quick-action tiles get real icons and working links. They declared
iconType:class with MDI component names, which renders nothing (class
expects a Nextcloud core CSS class) - now inline SVG paths filled with
the tile's text color. And linkType:app resolves as
generateUrl('/apps/' + value), so their full-path values produced
/apps//apps/keepiq/... and 404'd; the router is hash-based, so the
values are now keepiq#/secrets and keepiq#/applications.
- The dashboard is current on every visit: its endpoint-bound widgets
fetch through the library's shared 5-minute cache, so creating a
secret and returning showed pre-creation totals and a feed without the
event. A router hook drops that cache on every navigation that lands
on the dashboard; within-page dedupe keeps working.
- L10n: the feed and queue strings land in all 37 locales (Item and
No recent activity with their real per-locale translations, the queue
title/empty text and View all with nl/de/fr/es/it translations), the
dashboard header strings that silently fell back to English (Vault
overview, its description, Rotation due) are translated, and the
Dutch folder pickers now say "Hoofdmap" for the vault root instead of
the literal "Kluiswortel".
…alias Adds the CnBreadcrumbs stub to the vitest alias for @conduction/nextcloud-vue (one span per crumb, aria-current="page" on the last, nothing for an empty trail — mirroring the real component's contract). The declared ^2.3.0 range already covers the library release carrying CnBreadcrumbs, so the dependency update is a lockfile refresh at merge time.
…olbar and rich empty state The vault page gets a visible title (folder name, or "Secrets" at the root), a CnBreadcrumbs folder trail below it (home crumb + parentId walk with cycle/depth guards; current folder unlinked; nothing at the root), and a rich NcEmptyContent empty state. The toolbar becomes a declarative toolbarItems() list where per-item placement is the only knob: Refresh is the sole visible secondary button (New secret stays CnIndexPage's add), and one "More actions" overflow carries New vault/New folder (level-appropriate label), Ask for a credential, Import, Team sharing, the former "My data" entries, and the type filter as a radio group — all data-testids and disabled conditions unchanged. CnIndexPage's refresh action and the visible button both drive the existing fetch. e2e specs that clicked toolbar buttons directly (secret-import, export-gdpr) now open the overflow first. Six new strings ride the l10n pipeline (en + nl).
The parity ratchet requires every English source key in all required locales. The six vault-restyle strings (Refresh, No secrets found, Add your first secret using the button above, More actions, New vault, Filter by type) gain translations in the 35 remaining locale catalogs, the .js catalogs are regenerated, and the ratchet is tightened to record the progress.
The secret list shows the current folder's direct subfolders as rows above the secrets, file-manager style (root = the top-level vaults): folder icon plus name, click navigates into the folder, where the Stage-5 breadcrumbs walk back up. Rows come from a pure helper (subfolderRows) with ids prefixed "folder:" so they cannot collide with secret ids; folders sort first as one name-sorted group, the inline search filters them client-side (everything visible in the list is searchable), they render on page 1 only, and they carry no bulk checkbox. The move dialogs already reached any depth through their flat folder list; their options now carry full "A / B / C" path labels so same-named nested folders are distinguishable. New vitest specs cover the row assembly and path labels; a new e2e test navigates root -> vault -> nested folder purely via rows and breadcrumbs.
The Stage-7 rail introduces five strings (Vaults, All vaults, Admin settings, Opens in a new tab, Keepiq navigation). They land in the English source and all 36 required locales, the .js catalogs are regenerated, and the parity ratchet is tightened to record the progress.
…acing the in-page folder pane KeepiqAppNav fills CnAppRoot's #menu slot: it renders manifest.menu by section/order exactly like CnAppNav (captions, :to route items so the href$ e2e selectors keep working, the user-settings action via the cnOpenUserSettings inject, external hrefs) and reuses CnAppNav's cn-app-nav__* class names and cn-nav-* testids as a documented e2e-parity contract. Below the main entries, a component-owned Vaults caption and an "All vaults" item host NavFolderTree — a recursive NcAppNavigationItem tree over the folder store, display-capped at depth 5 with a "…" node standing in for hidden levels (single hidden child: navigates into it; several: opens the parent page whose subfolder rows list them) and deepest-visible-ancestor highlighting. The caption lives in the component, not the manifest: buildManifest drops route-less menu entries, so a manifest caption never reaches the nav (library follow-up noted in the plan). The isAdmin-gated Admin-settings link-out (new tab + open-in-new marker) is replicated so the custom rail does not lose it. SecretList drops the in-page folder pane and its two-pane layout; the create-folder e2e goes through the More-actions overflow and asserts the new tree; the CnFolderSidebar vitest stub is removed.
The level-aware create dialog introduces two strings (Vault name, Create vault). They land in the English source and all 36 required locales, the .js catalogs are regenerated, and the parity ratchet is tightened.
…ype-icon map Review polish on the restyle: root-level entries ARE the vaults, so they carry the safe glyph everywhere — the nav tree's top level, the vault rows on the root list, the New-vault overflow entry and the create dialog's submit. The redundant "All vaults" host item leaves the rail (the Vaults caption already names the group and the extra link double-highlighted next to All secrets); the tree now renders directly under the caption. The create dialog's wording follows the SELECTED parent (New vault / Vault name / Create vault at the root, folder wording inside a vault) and its parent picker gains the path labels. The page title and the toolbar (Refresh + More actions) share one row aligned with the content via CnIndexPage's #header slot. The secret-type icon map now covers every seeded type: totp gets a clock, passkey a fingerprint, card a credit card and identity an ID card. The create-folder e2e accepts the level-aware submit label.
Contributor
Quality Report — ConductionNL/keepiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ❌ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 111/111 | |||
| npm | ✅ | ✅ 550/550 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-26 14:59 UTC
Download the full PDF report from the workflow artifacts.
Contributor
Quality Report — ConductionNL/keepiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ❌ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ❌ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 111/111 | |||
| npm | ✅ | ✅ 550/550 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-26 22:47 UTC
Download the full PDF report from the workflow artifacts.
The Lint Check and Code Quality (eslint + format) jobs failed on the branch files: hyphenated template attributes where the config requires camelCase, unsorted imports, a useless v-bind, missing blank lines between multi-line props, and prettier formatting drift in seven files. All fixes are auto-applied style changes; no behavior changes.
Contributor
Quality Report — ConductionNL/keepiq @
|
| 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-27 08:32 UTC
Download the full PDF report from the workflow artifacts.
Contributor
Quality Report — ConductionNL/keepiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 111/111 | |||
| npm | ✅ | ✅ 550/550 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ❌ | ||||
| Hydra gates | ❌ |
Quality workflow — 2026-08-27 09:50 UTC
Download the full PDF report from the workflow artifacts.
…findings The frontend restyle (PR #448) left four Playwright specs and three hydra gates red. The UI is correct; the specs and traceability tags were stale. Playwright: - NcActionButton renders data-testid on its <li role="presentation"> wrapper while the click handler sits on the inner button, so the specs' native li.click() on 'open-create-folder' / 'import-secrets' fired nothing and the dialogs never opened. New clickOverflowAction() helper opens "More actions" and clicks the inner button. - The breadcrumb-navigation test reloaded the page to pick up API-created folders, but a reload wipes the in-memory CryptoKey and lands on the lock gate. It now reloads, unlocks again and reopens the vault. - The create-folder test asserted '.secret-list-view__sidebar', a pane the restyle removed; it now asserts the new subfolder row. - compromise-recovery pressed Escape once against STACKED dialogs (recovery on top of user-settings) — the recorded CI flake. It now presses Escape once per remaining dialog. - The one waitForLoadState('networkidle') became domcontentloaded (ADR-074 rule 4, hydra gate-58). Hydra gates: - gate-46: five @SPEC tags pointed at dashboard/spec.md#app-navigation- renders, which never existed. The requirement + scenario now live in menu-architecture/spec.md (the menu's own spec) and the tags — plus navigation.spec.ts's @e2e tag — point there. - gate-16: the 17 restyle-touched methods in KeepiqAppNav.vue, SecretList.vue and favicon.js carry @SPEC tags against their real anchors (folder-management, list-and-pagination, secret-types, create-a-folder-and-move-a-secret, settings-foldout). Verified against a CI-seeded instance: the full suite passes and all applicable hydra gates pass at full coverage.
Contributor
Quality Report — ConductionNL/keepiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 111/111 | |||
| npm | ✅ | ✅ 550/550 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| PHPUnit | ✅ | ||||
| Newman | ✅ | ||||
| Playwright | ✅ | ||||
| Hydra gates | ✅ |
Quality workflow — 2026-08-27 13:19 UTC
Download the full PDF report from the workflow artifacts.
bbrands02
approved these changes
Aug 27, 2026
rubenvdlinde
added a commit
that referenced
this pull request
Aug 27, 2026
#448 (frontend-improvements) added `Registered by` and `Requested` to the manifest. The manifest is data the renderer walks rather than source the l10n extractor scans, so neither reached the catalogue and both rendered English for a Dutch user. Registered by -> Geregistreerd door Requested -> Aangevraagd This is the drift the fleet check was added to catch, arriving the ordinary way: not a historical backlog, just the next PR that adds a manifest string. It took a couple of hours to appear, which is roughly the rate to expect. Worth noting `check:l10n-js` did NOT catch it and could not: nl.json and nl.js were in sync at 617 keys each, both simply missing the strings. That check guards source-versus-artifact drift, not manifest-versus-catalogue coverage. Different failure, different detector — humaniq's validate-l10n-parity.js is the one that asserts every manifest string has a key, and this app does not run it. Then `l10n:build`, because nl.json alone ships nothing. Verified: 0 manifest strings left without Dutch, keys DROPPED = 0 against the pre-change catalogues, check:l10n-js PASS, check-l10n-parity PASS.
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.
The full frontend restyle (stages 1–7 of the restyle plan), commit by commit:
secondary entries consolidate into the settings foldout; the main nav
keeps only the primary destinations.
version truth from
appinfo/info.xml, injected as initial state.feat(dashboard): live activity feed, approval queue, KPI colors and
fresh data — the dashboard shows a real recent-activity feed (newest
first, relative times, "View all" link), the pending-applications queue,
and constant KPI color identities.
new library component's contract; the dependency itself is the lockfile
refresh in this PR.
toolbar and rich empty state — visible page title (folder name, or
"Secrets" at the root), a CnBreadcrumbs folder trail (home crumb +
parentIdwalk with cycle/depth guards, current folder unlinked), arich empty state, and a declarative
toolbarItems()toolbar: Refreshvisible, everything else (create, credential request, Import, Team
sharing, the former "My data" menu, the type filter as radios) in one
"More actions" overflow — all testids and disabled conditions unchanged.
rows) — the current folder's direct subfolders list above the secrets,
file-manager style; folders sort first as a group, the inline search
matches them, and the move dialogs gain full "A / B / C" path labels.
replacing the in-page folder pane —
KeepiqAppNavfills CnAppRoot's#menuslot, rendering the manifest menu with CnAppNav's exact classnames and testids as a documented e2e-parity contract, plus the Vaults
caption and
NavFolderTree(display-capped at depth 5 with a "…"stand-in; deepest-visible-ancestor highlighting). The isAdmin-gated
Admin-settings link-out (new tab + marker) is replicated so the custom
rail keeps it. The in-page folder sidebar is gone.
type-icon map — root-level entries are vaults and carry the safe glyph
everywhere (nav tree, root list rows, the New-vault entry, the create
dialog); the create dialog's wording follows the selected parent
("New vault"/"Vault name"/"Create vault" at the root); the page title
and toolbar share one row; and every seeded secret type has a fitting
icon (card → credit card, identity → ID card, passkey → fingerprint,
totp → clock).
vault dialog) to English plus all 36 required locales, regenerate the
.jscatalogs, and tighten the parity ratchet each time.The e2e specs that drove toolbar buttons or the sidebar directly
(secret-import, export-gdpr, folder-sharing) now go through the "More
actions" overflow and the new nav tree; a new e2e navigates root → vault →
nested folder purely via subfolder rows and breadcrumbs.
Review note:
buildManifestdrops route-less menu entries, so the Vaultscaption is rendered by
KeepiqAppNavitself rather than declared in themanifest — flagged in the plan as a library follow-up (the filter should
spare
type: "caption").