diff --git a/webui/README.md b/webui/README.md index bdeaf3b..20b8fe7 100644 --- a/webui/README.md +++ b/webui/README.md @@ -71,11 +71,18 @@ npm run build # what `make admin-ui` runs, plus webui/generate.mjs (Overview/Usage, 5s poll), `history` (Charts, fetch-on-selection + 30s refresh). - `src/components/` — `AuthGate`, `OverviewView`, `UsageView`, - `ChartsView`/`UsageChart`, `UsageTable`; `ui/` holds the shadcn-vue - primitives this panel actually uses (Card, Table, Tabs, Button, Input, - Alert, Select, Badge) — nothing installed and unused. + `ChartsView`/`UsageChart`, `UsageTable`, `DataTable` (the shared + sortable-table wrapper — shadcn-vue's DataTable pattern, `@tanstack/ + vue-table`, generic over row type); `ui/` holds the shadcn-vue + primitives this panel actually uses (Accordion, Card, Table, Tabs, + Button, Input, Alert, Select, Badge) — nothing installed and unused. + Providers (Overview) and Groups (Usage) render as a real Accordion — + expanding a group shows its member users' own usage rows, a + client-side join on `AdminUsageEntryView.groupName`. - `src/lib/` — `api.ts` (the `/admin/api/*` fetch wrapper), `format.ts`, - `key-storage.ts`, `chart-setup.ts`. + `key-storage.ts`, `chart-setup.ts`, `provider-expand.ts` (search-aware + expand state, adapted to drive Accordion's v-model), `usage-columns.ts` + (shared `ColumnDef`s for every usage table). - `src/types/api.ts` — TypeScript mirrors of admin.go's JSON response shapes (`adminOverviewResponse`, `adminUsageResponse`, `usageHistoryResponse`). diff --git a/webui/package-lock.json b/webui/package-lock.json index c5faa42..b4322cb 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -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", diff --git a/webui/package.json b/webui/package.json index 96db079..11c489c 100644 --- a/webui/package.json +++ b/webui/package.json @@ -12,6 +12,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", diff --git a/webui/src/App.vue b/webui/src/App.vue index 6cf3666..bfd4e56 100644 --- a/webui/src/App.vue +++ b/webui/src/App.vue @@ -32,7 +32,7 @@ onMounted(() => {