From 9de22047521162a29942831c7a7d9f789c521d4c Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Fri, 21 Aug 2026 12:14:59 +0100 Subject: [PATCH] chore(webui): drop dead clear() from useSearchQuery, add spec, doc composables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useSearchQuery.ts: remove clear() — zero callers, since SearchInput's clear button already clears by emitting update:modelValue('') through v-model, no separate imperative clear needed. - Add composables/useSearchQuery.spec.ts (3 cases: normalized trims, normalized lowercases, hasQuery false for whitespace-only query). - README.md Structure section: document src/composables/ (useThemeColors, useSearchQuery) and mention the new shared SearchInput component alongside the existing components/lib entries. --- webui/README.md | 17 ++++++++----- webui/src/composables/useSearchQuery.spec.ts | 23 +++++++++++++++++ webui/src/composables/useSearchQuery.ts | 26 +++++++++++--------- 3 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 webui/src/composables/useSearchQuery.spec.ts diff --git a/webui/README.md b/webui/README.md index 6f234ae..49526e4 100644 --- a/webui/README.md +++ b/webui/README.md @@ -73,12 +73,17 @@ npm run build # what `make admin-ui` runs, plus webui/generate.mjs - `src/components/` — `AuthGate`, `OverviewView`, `UsageView`, `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`. + vue-table`, generic over row type), `SearchInput` (the shared search-box + — icon + shadcn `Input` + shadcn `Button` clear button — every search + filter in the panel renders); `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/composables/` — `useThemeColors` (Chart.js axis/grid/legend colors + off the current design tokens), `useSearchQuery` (the shared + `query`/`normalized`/`hasQuery` state every `SearchInput` pairs with). - `src/lib/` — `api.ts` (the `/admin/api/*` fetch wrapper), `format.ts`, `key-storage.ts`, `chart-setup.ts`, `search-expand.ts` (search-aware expand state, adapted to drive Accordion's v-model — shared by diff --git a/webui/src/composables/useSearchQuery.spec.ts b/webui/src/composables/useSearchQuery.spec.ts new file mode 100644 index 0000000..b4deed1 --- /dev/null +++ b/webui/src/composables/useSearchQuery.spec.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from 'vitest' + +import { useSearchQuery } from './useSearchQuery' + +describe('useSearchQuery', () => { + it('normalized trims leading/trailing whitespace', () => { + const { query, normalized } = useSearchQuery() + query.value = ' alice ' + expect(normalized.value).toBe('alice') + }) + + it('normalized lowercases the query', () => { + const { query, normalized } = useSearchQuery() + query.value = 'AlIcE' + expect(normalized.value).toBe('alice') + }) + + it('hasQuery is false for a whitespace-only query', () => { + const { query, hasQuery } = useSearchQuery() + query.value = ' ' + expect(hasQuery.value).toBe(false) + }) +}) diff --git a/webui/src/composables/useSearchQuery.ts b/webui/src/composables/useSearchQuery.ts index 26fbd07..7df1dcf 100644 --- a/webui/src/composables/useSearchQuery.ts +++ b/webui/src/composables/useSearchQuery.ts @@ -1,24 +1,26 @@ import { computed, ref } from 'vue' /** - * useSearchQuery is the one raw-query → {normalized, hasQuery, clear} - * adapter every search filter in this panel uses — OverviewView's model - * search, UsageView's user/group search, ChartsView's scope-picker search - * all had their own copy of this identical query/normalizedQuery/hasQuery/ - * clearQuery quartet; extracted so it exists once (vue.md: "if you've - * written it twice, you owe an abstraction"). Callers still own their own - * matching logic entirely (OverviewView's modelMatches/providerMatches, + * useSearchQuery is the one raw-query → {normalized, hasQuery} adapter + * every search filter in this panel uses — OverviewView's model search, + * UsageView's user/group search, ChartsView's scope-picker search all had + * their own copy of this identical query/normalizedQuery/hasQuery + * pairing; extracted so it exists once (vue.md: "if you've written it + * twice, you owe an abstraction"). Callers still own their own matching + * logic entirely (OverviewView's modelMatches/providerMatches, * lib/usage-search.ts's helpers) — this only normalizes the raw input the * same way every call site already agreed on: trimmed, lowercased. + * + * No `clear()` here — clearing is just setting `query.value = ''`, and + * every caller already gets that for free from SearchInput.vue's own + * clear button emitting `update:modelValue('')` through the `v-model` + * binding, so a separate clear function would be dead API surface no + * caller needs. */ export function useSearchQuery() { const query = ref('') const normalized = computed(() => query.value.trim().toLowerCase()) const hasQuery = computed(() => normalized.value.length > 0) - function clear(): void { - query.value = '' - } - - return { query, normalized, hasQuery, clear } + return { query, normalized, hasQuery } }