chore(f3.5): AppShell mockup parity — sidebar, topbar, plugin fixes #26
Reference in New Issue
Block a user
Delete Branch "chore/f3.5-appshell-mockup-parity"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
F3.5 sub-package: brings the AppShell layout (sidebar + topbar) in line with the agreed mockup, and fixes several PrimeVue/Iconify integration bugs that surfaced during the parallel-mode window.
Rebased onto current
main(which now includes the Storybook setup from #25).Commits (8, oldest → newest)
8f3a404a4089a14bf8fddc0e3df55b4d29f3fdf2b1443be4f218ac6e71585e1bFiles changed (6, +388 / −67)
apps/app/src/layouts/components/AppShell.vue— sidebar width, topbar restructure, responsive wrappersapps/app/src/layouts/components/SidebarHeader.vue(new) — org-switcher cardapps/app/src/layouts/components/SidebarUserCard.vue(new) — user-info card at sidebar bottomapps/app/src/main.ts— installer import tweak, explicit component importsapps/app/src/plugins/iconify.ts(new) — runtime Tabler icon-set bootstrapapps/app/src/plugins/primevue/index.ts— named-export installer to stop double-registrationTest plan
pnpm devfromapps/app/— confirm SPA boots without console errors and sidebar/topbar render per mockuppnpm test+pnpm typecheck— greenpnpm storybook— AppShell-adjacent stories (if any) still renderSection headings ("Beheer" / organisation name, "Platform") were already uppercase + muted but read as bold paragraph dividers more than as quiet group markers. Tighten letter-spacing, drop weight from semibold to medium, lighten the color one step (surface-500 → surface-400), and shrink text to 11px so the headings recede and let the nav items themselves carry the visual weight. Spacing nudged from mt-4/mb-2/px-2 → mt-6/mb-1/px-3: more breathing room above each group, less below (the items already have py-2 on top), and the heading left-edge now lines up with the icons of the nav items beneath it (both at px-3). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>unplugin-vue-components' Components({ dirs }) in vite.config.ts only scans src/components, src/@core/components, and src/views/demos. The sub-components introduced in B1/B3 live under src/layouts/components/, which is NOT in the auto-import scan path. Without an explicit script import, Vue renders <SidebarHeader> and <SidebarUserCard> as unknown HTML elements (no DOM output, no errors), which is why the topbar and sidebar-bottom cards looked empty in browser inspection. Adding the two imports inline with the existing Icon import keeps the component graph explicit. The post-edit eslint --fix hook preserves the imports because the template usages (already present from B1 and B3) make vue-eslint-parser see them as used. The original B1/B3 commits had the imports stripped by the hook because the imports were added in a separate Edit *before* the template usages — eslint --fix correctly removed them as unused at that moment, and the next Edit added the template usage but not the import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>main.ts explicitly calls installPrimeVue(app) AFTER registerPlugins(app) per the comment in main.ts ("so PrimeVue lives outside the Vuexy @core machine"). The intent was a single registration site outside the auto-discovery loop. Bug: registerPlugins (src/@core/utils/plugins.ts) globs plugins/*/index.{ts,js} eagerly and invokes the `default` export of each match. plugins/primevue/index.ts was exporting installPrimeVue as the default, so registerPlugins also picked it up and called it. End result: PrimeVue and its three services (Toast, Confirmation, Dialog) were each registered twice on every app boot. Visible symptoms: duplicate Toast emissions on a single Toast.add() call, and ConfirmationService callbacks firing twice for one user confirmation. Fix: convert `export default function installPrimeVue` to a NAMED export, and update main.ts's import to `{ installPrimeVue }`. The registerPlugins glob still picks up the module path but the `pluginImportModule.default?.(app)` invocation becomes a no-op via optional chaining (no default export to call). main.ts remains the single registration site. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Tailwind's lg:hidden loses to PrimeVue's .p-button { display: inline-flex } due to equal specificity but later cascade order. Resulted in the mobile hamburger remaining visible on desktop, allowing the Drawer to open over the already-visible permanent sidebar. Fix: wrap mobile-only cluster (hamburger + title) in a plain <div lg:hidden> so the wrapper owns the visibility toggle. The wrapper is not a PrimeVue component, so no specificity competition. The Drawer itself had the same anti-pattern (class="lg:hidden") and is worse, because PrimeVue Drawer teleports to body — a wrapping div on the parent does not isolate the teleported overlay, and a class on the Drawer root loses to .p-drawer { display: flex } when visible. Converted to v-if="!isLg" driven by useMediaQuery('(min-width: 1024px)'). Vue simply does not render the component on lg+, so no display rule competes. Audited all 5 layouts for the same anti-pattern: - AppShell.vue — fixed (Button + Drawer described above) - default.vue / OrganizerLayout.vue / PortalLayout.vue — delegate to AppShell; no PrimeVue elements with responsive classes - blank.vue — plain <div>, no PrimeVue - PublicLayout.vue — plain <main>, no PrimeVue useMediaQuery is auto-imported via unplugin-auto-import's @vueuse/core entry in vite.config.ts; explicit imports get stripped by the post-edit ESLint --fix hook as redundant. F3-introduced bug (commit43915501); surfaced during F3.5 testing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>