From 42d017309577627eca8dab5bc1098a75ee762cc4 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sat, 15 Aug 2026 00:53:12 +0200 Subject: [PATCH] refactor(ui): unify Channels + Stats filter chips (R13 S2c step 4) - channel-manager sync-status chips (F2) -> FilterChipGroup (rounded-lg -> the canonical rounded-full), controls-row alignment preserved via className - channel-manager user-tag chips (multi-select) restyled to filterChipClass (rounded-lg -> rounded-full) so the whole manager chip set is one look - stats system time-range chips (F4) -> FilterChipGroup (text-sm -> canonical) Adds aria-label i18n (channels.filters.syncLabel, stats.rangeLabel; HU+EN). Self-E2E'd: sync chips + tag chips rounded-full, view TABS stay rounded-lg (their own canonical), stats 7/30/90d toggles. gate green (tsc/eslint-0/ vitest 136/knip/prettier). Branch not merged. --- frontend/src/components/Channels.tsx | 22 +++++++++------------ frontend/src/components/Stats.tsx | 23 +++++++++++----------- frontend/src/i18n/locales/en/channels.json | 1 + frontend/src/i18n/locales/en/stats.json | 1 + frontend/src/i18n/locales/hu/channels.json | 1 + frontend/src/i18n/locales/hu/stats.json | 1 + 6 files changed, 24 insertions(+), 25 deletions(-) diff --git a/frontend/src/components/Channels.tsx b/frontend/src/components/Channels.tsx index 4cf4e809..b6ba7706 100644 --- a/frontend/src/components/Channels.tsx +++ b/frontend/src/components/Channels.tsx @@ -19,6 +19,7 @@ import { useChannelTagActions } from "../lib/useChannelTagActions"; import { useChannelPriority } from "../lib/useChannelPriority"; import ViewSwitcher, { type ViewOption } from "./ViewSwitcher"; import ColumnSortControl from "./ColumnSortControl"; +import FilterChipGroup, { filterChipClass } from "./FilterChipGroup"; import Pager from "./Pager"; import { CHANNEL_LAYOUTS, type ChannelLayout } from "../lib/channelLayout"; import { sortRows } from "../lib/columnSort"; @@ -294,18 +295,13 @@ export default function Channels() { // where they act. const syncFilter = typeof filters.sync === "string" ? filters.sync : ""; const statusChips = ( -
- {SYNC_CHIPS.map((c) => ( - - ))} -
+ ({ value: c.value, label: t(c.labelKey) }))} + value={syncFilter} + onChange={(sync) => setFilters({ ...filters, sync })} + /> ); // The catalog-wide stats ride on the right of the tab row (subscribed only), leaving room for @@ -479,7 +475,7 @@ export default function Channels() { key={tg.id} onClick={() => toggleTagFilter(tg.id)} aria-pressed={tagFilter.includes(String(tg.id))} - className="pill text-xs px-2 py-1 rounded-lg border" + className={filterChipClass} > {tg.name} diff --git a/frontend/src/components/Stats.tsx b/frontend/src/components/Stats.tsx index 59c4602e..55ffec50 100644 --- a/frontend/src/components/Stats.tsx +++ b/frontend/src/components/Stats.tsx @@ -9,6 +9,7 @@ import { notify } from "../lib/notifications"; import { LS, useAccountPersistedState } from "../lib/storage"; import Tooltip from "./Tooltip"; import { LoadingState, StateMessage } from "./QueryState"; +import FilterChipGroup from "./FilterChipGroup"; import { Section, SettingRow } from "./ui/form"; import { useMe } from "../lib/useMe"; @@ -228,18 +229,16 @@ function SystemStats() {

{t("stats.title")}

-
- {RANGES.map((d) => ( - - ))} -
+ ({ + value: String(d), + label: t("stats.rangeDays", { count: d }), + }))} + value={String(days)} + onChange={(v) => setDays(Number(v))} + />

diff --git a/frontend/src/i18n/locales/en/channels.json b/frontend/src/i18n/locales/en/channels.json index 136abcb2..0073af9c 100644 --- a/frontend/src/i18n/locales/en/channels.json +++ b/frontend/src/i18n/locales/en/channels.json @@ -39,6 +39,7 @@ } }, "filters": { + "syncLabel": "Sync status", "all": "All", "needsFull": "Needs full history", "fullySynced": "Fully synced" diff --git a/frontend/src/i18n/locales/en/stats.json b/frontend/src/i18n/locales/en/stats.json index 1131e73e..5de6eaec 100644 --- a/frontend/src/i18n/locales/en/stats.json +++ b/frontend/src/i18n/locales/en/stats.json @@ -5,6 +5,7 @@ }, "title": "API quota usage", "rangeDays": "{{count}}d", + "rangeLabel": "Time range", "introBefore": "YouTube Data API units attributed by who triggered the spend. ", "introSystem": "System", "introAfter": " is shared background work (scheduled backfill, enrichment, resync) that isn't tied to one person.", diff --git a/frontend/src/i18n/locales/hu/channels.json b/frontend/src/i18n/locales/hu/channels.json index 76e19a25..1f685502 100644 --- a/frontend/src/i18n/locales/hu/channels.json +++ b/frontend/src/i18n/locales/hu/channels.json @@ -39,6 +39,7 @@ } }, "filters": { + "syncLabel": "Szinkron állapot", "all": "Mind", "needsFull": "Hiányos előzmény", "fullySynced": "Teljesen szinkronizálva" diff --git a/frontend/src/i18n/locales/hu/stats.json b/frontend/src/i18n/locales/hu/stats.json index d85adb08..c7692360 100644 --- a/frontend/src/i18n/locales/hu/stats.json +++ b/frontend/src/i18n/locales/hu/stats.json @@ -5,6 +5,7 @@ }, "title": "API-kvóta használat", "rangeDays": "{{count}} nap", + "rangeLabel": "Időtartomány", "introBefore": "A YouTube Data API-egységek aszerint, hogy ki váltotta ki a felhasználást. A ", "introSystem": "Rendszer", "introAfter": " a megosztott háttérmunka (ütemezett letöltés, gazdagítás, újraszinkronizálás), amely nem köthető egy személyhez.",