feat(webui): shadcn-vue accordion + sortable DataTable, full-width panel

Component-first admin-panel rework (operator directive), replacing
hand-rolled patterns with the actual shadcn-vue library components
rather than working around them.

- Providers (OverviewView) and Groups (UsageView) now render as a real
  shadcn-vue Accordion: trigger is the summary line, content holds the
  detail (base URL, last refresh/error, model chips for providers;
  full stat grid + member users for groups) — the earlier hand-rolled
  second-<TableRow> toggle is retired. lib/provider-expand.ts's tested
  two-set expand-state module is unchanged; only the template now
  adapts it to Accordion's type="multiple" v-model via a writable
  computed instead of driving a plain v-if.
- Usage view: expanding a group shows its member users' own usage rows
  — a client-side join on AdminUsageEntryView.groupName, which admin.go
  already sets per user (verified via admin_test.go's existing
  TestAdminUsage_MathAgainstSeededCounters; no backend change needed).
- New DataTable.vue: the shadcn-vue DataTable pattern
  (@tanstack/vue-table, pinned to ^8.21.3 — the version whose API
  matches the documented pattern; npm's default "latest" resolves to
  v9, a breaking rewrite with a different API) factored into one
  reusable, generic component so every real table (users usage,
  aliases, a group's nested member list) gets clickable sortable
  headers with asc/desc indicators for free. usage-columns.ts holds
  the shared ColumnDef set; UsageTable.vue now delegates to DataTable.
  Groups themselves cannot be a literal <table> (Accordion wraps
  content in a <div>, illegal inside <tbody>), so their own
  sortability is driven by the same columns headlessly, surfaced as a
  small header-button toolbar above the Accordion.
- Aliases table: alias id gets the ModelChip copy treatment (the alias
  name is the routable string); target stays plain text.
- App.vue: dropped the max-w-6xl container — the panel now uses the
  full viewport width with responsive gutters (px-4 sm:px-6 lg:px-8).
  Table.vue already wraps every table in its own overflow-x-auto
  container, so a wide table still scrolls itself, never the page.

shadcn-vue add accordion re-added @lucide/vue and required swapping
its Chevron icons for FontAwesome (webui/README.md's own documented
trap); components.json's iconLibrary/font keys stayed absent
(verified main.css byte-identical after the add).

Browser-verified against the real integration stack (real /admin over
the Yaegi plugin, strict CSP incl. style-src 'self'): provider/group
accordion expand, search auto-expand through the new Accordion
adapter, sort toggles on aliases/users/groups-toolbar/nested-member
tables, alias copy-to-clipboard, full-width at 1920px. Zero console
errors (one unrelated MetaMask extension log).
This commit is contained in:
2026-08-21 11:12:59 +01:00
parent 92e9ad0c1e
commit 60d66ca147
16 changed files with 666 additions and 248 deletions
+33
View File
@@ -11,6 +11,7 @@
"@fortawesome/fontawesome-svg-core": "^7.3.1",
"@fortawesome/free-solid-svg-icons": "^7.3.1",
"@fortawesome/vue-fontawesome": "^3.3.3",
"@tanstack/vue-table": "^8.21.3",
"@vueuse/core": "^14.4.0",
"chart.js": "^4.5.1",
"class-variance-authority": "^0.7.1",
@@ -1753,6 +1754,19 @@
"vite": "^5.2.0 || ^6 || ^7 || ^8"
}
},
"node_modules/@tanstack/table-core": {
"version": "8.21.3",
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz",
"integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
}
},
"node_modules/@tanstack/virtual-core": {
"version": "3.17.8",
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.17.8.tgz",
@@ -1763,6 +1777,25 @@
"url": "https://github.com/sponsors/tannerlinsley"
}
},
"node_modules/@tanstack/vue-table": {
"version": "8.21.3",
"resolved": "https://registry.npmjs.org/@tanstack/vue-table/-/vue-table-8.21.3.tgz",
"integrity": "sha512-rusRyd77c5tDPloPskctMyPLFEQUeBzxdQ+2Eow4F7gDPlPOB1UnnhzfpdvqZ8ZyX2rRNGmqNnQWm87OI2OQPw==",
"license": "MIT",
"dependencies": {
"@tanstack/table-core": "8.21.3"
},
"engines": {
"node": ">=12"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
"vue": ">=3.2"
}
},
"node_modules/@tanstack/vue-virtual": {
"version": "3.13.36",
"resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.36.tgz",