mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-15 05:29:28 +00:00
fix(webui): shared SearchInput/useSearchQuery, correct expand rationale
Address review of the user/group search filter (feat/user-search):
- Correct the "group name match does not auto-expand" rationale in
UsageView.vue and lib/search-expand.ts. The prior comment claimed
Overview has no provider-name search — false: OverviewView's
providerMatches checks each model's full ROUTABLE id
(`${provider}/${model}`, lib/format.ts's routableModelId), so a
provider-name query does match and auto-expand there. The actual
reason Usage withholds auto-expand from a name-only match: a group's
name is already legible on its collapsed trigger row, so expanding
every name match too would dump full member rosters for no reason;
a member-only match has no such shortcut and still needs the reveal.
Behavior is unchanged — only the (now accurate) justification.
- Extract components/SearchInput.vue and composables/useSearchQuery.ts.
OverviewView's model search, UsageView's user/group search, and
ChartsView's scope-picker search were three ~22-line copies of the
same icon+Input+clear-button markup, plus three copies of the
query/normalized/hasQuery/clear quartet. All three views now consume
the shared component/composable. SearchInput's clear button is a real
shadcn Button (variant="ghost", size="icon-xs" — 24x24px) instead of a
hand-rolled <button>, fixing a WCAG 2.2 SC 2.5.8 hit-target/
focus-visible gap once instead of three times.
- ChartsView's search input: w-48 (clipped the 22-char placeholder) to
w-56, matching the sibling SelectTrigger's width.
- UsageView.vue's visibleMembers now calls the exported, tested
matchingMembersOfGroup helper instead of re-implementing the same
filter inline with userMatches.
- Reworded the "shared with ChartsView.vue" claims on membersOfGroup
(usage-search.ts) and in UsageView.vue's visibleMembers doc comment:
ChartsView never calls membersOfGroup directly, only indirectly via
groupMatches -> matchingMembersOfGroup.
- UsageView's groups/users empty-state messages now follow Overview's
aliasEmptyMessage three-way pattern ("none configured" vs "no
groups/users match ..." vs "none"), distinguishing nothing-configured
from nothing-matches instead of collapsing both to the same text.
Gate unchanged: npm run build + npm test + vue-tsc, make admin-ui
(separate regen commit), go test ./... -count=1, gofmt/vet, make
yaegi-check.
This commit is contained in:
@@ -34,10 +34,12 @@ export interface ExpandState {
|
||||
* manuallyExpanded plus every id in matchingIds that is not in
|
||||
* manuallyCollapsed (auto-expand every match, unless the user explicitly
|
||||
* closed that one). The caller decides what "matching" means for its own
|
||||
* view — OverviewView.vue passes every provider with a matching model;
|
||||
* UsageView.vue passes every group that matches ONLY via a member, not via
|
||||
* its own name (see that view's own doc comment for why a name match
|
||||
* alone does not force-expand there).
|
||||
* view, and this module has no opinion on it either way — OverviewView.vue
|
||||
* passes every provider that itself matched at all (every match there
|
||||
* implies at least one matching model, so every match auto-expands);
|
||||
* UsageView.vue instead passes only the subset of its own matches that
|
||||
* matched via a member rather than via the group's own name (see that
|
||||
* view's own doc comment for why it draws that narrower distinction).
|
||||
*/
|
||||
export function computeExpandedItems(state: ExpandState, hasQuery: boolean, matchingIds: string[]): Set<string> {
|
||||
if (!hasQuery) return new Set(state.manuallyExpanded)
|
||||
|
||||
@@ -28,9 +28,12 @@ export function groupNameMatches(group: Pick<AdminUsageEntryView, 'id'>, query:
|
||||
* `groupName === group.id` — a user's row already carries its own group
|
||||
* name (admin.go's adminUsageEntryView.GroupName, set in buildAdminUsage),
|
||||
* so this is a plain client-side join against the already-polled
|
||||
* GET /admin/api/usage response, no extra fetch. Shared so UsageView.vue
|
||||
* and ChartsView.vue never re-derive this join differently (it replaces
|
||||
* UsageView.vue's own former local membersOf helper).
|
||||
* GET /admin/api/usage response, no extra fetch. UsageView.vue calls this
|
||||
* directly (replacing its own former local membersOf helper);
|
||||
* matchingMembersOfGroup below builds on it too, which is how
|
||||
* ChartsView.vue's groupMatches call ends up depending on this same join
|
||||
* — indirectly, through matchingMembersOfGroup — without calling
|
||||
* membersOfGroup itself.
|
||||
*/
|
||||
export function membersOfGroup(group: Pick<AdminUsageEntryView, 'id'>, users: AdminUsageEntryView[]): AdminUsageEntryView[] {
|
||||
return users.filter((u) => u.groupName === group.id)
|
||||
|
||||
Reference in New Issue
Block a user