refactor(ui): shared form primitives (Switch, Section, SettingRow, HintLabel)

components/ui/form.tsx replaces the per-panel copies:
- Switch — was duplicated in SettingsPanel (Switch) + ConfigPanel (Toggle) +
  Sidebar (inline labeled toggle).
- Section (with a card variant) — was in SettingsPanel + Stats (plain) and
  AdminUsers (glass card).
- SettingRow + HintLabel — the label+hint+control row was identical in SettingsPanel + Stats.
No visual change intended (Sidebar's toggle gains the standard knob shadow).
This commit is contained in:
npeter83 2026-06-29 00:04:45 +02:00
parent 5fcd64c1e1
commit 53c75f69e2
6 changed files with 125 additions and 147 deletions

View file

@ -7,6 +7,7 @@ import { formatEta, quotaActionLabel } from "../lib/format";
import { notify } from "../lib/notifications";
import { LS, usePersistedState } from "../lib/storage";
import Tooltip from "./Tooltip";
import { Section, SettingRow } from "./ui/form";
const RANGES = [7, 30, 90] as const;
type StatsTab = "overview" | "system";
@ -47,31 +48,6 @@ export default function Stats({ me }: { me: Me }) {
);
}
function Section({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div className="mb-6">
<div className="text-xs uppercase tracking-wide text-muted mb-2">{title}</div>
{children}
</div>
);
}
function Row({ label, hint, children }: { label: string; hint?: string; children: React.ReactNode }) {
return (
<div className="flex items-center justify-between gap-3 py-1.5 text-sm">
<Tooltip hint={hint ?? ""}>
<span
className={
hint ? "underline decoration-dotted decoration-muted/40 underline-offset-4 cursor-help" : ""
}
>
{label}
</span>
</Tooltip>
{children}
</div>
);
}
// Per-user view: sync status + your own API usage + (for real accounts) the manual sync actions.
function Overview({ me }: { me: Me }) {
@ -107,29 +83,29 @@ function Overview({ me }: { me: Me }) {
<Section title={t("stats.sync.myStatus")}>
{s ? (
<div className="space-y-1.5 text-sm">
<Row label={t("stats.sync.channels")} hint={t("stats.sync.channelsHint")}>
<SettingRow label={t("stats.sync.channels")} hint={t("stats.sync.channelsHint")}>
{s.channels_total}
</Row>
<Row label={t("stats.sync.recentSynced")} hint={t("stats.sync.recentSyncedHint")}>
</SettingRow>
<SettingRow label={t("stats.sync.recentSynced")} hint={t("stats.sync.recentSyncedHint")}>
{`${s.channels_recent_synced}/${s.channels_total}`}
</Row>
<Row label={t("stats.sync.fullHistory")} hint={t("stats.sync.fullHistoryHint")}>
</SettingRow>
<SettingRow label={t("stats.sync.fullHistory")} hint={t("stats.sync.fullHistoryHint")}>
{`${s.channels_deep_done}/${s.channels_deep_requested}`}
</Row>
</SettingRow>
{s.deep_pending_count > 0 && (
<Row
<SettingRow
label={t("stats.sync.fullHistoryEta")}
hint={t("stats.sync.fullHistoryEtaHint", { count: s.deep_pending_count })}
>
{formatEta(s.deep_eta_seconds)}
</Row>
</SettingRow>
)}
<Row label={t("stats.sync.myVideos")} hint={t("stats.sync.myVideosHint")}>
<SettingRow label={t("stats.sync.myVideos")} hint={t("stats.sync.myVideosHint")}>
{s.my_videos.toLocaleString()}
</Row>
<Row label={t("stats.sync.quotaLeft")} hint={t("stats.sync.quotaLeftHint")}>
</SettingRow>
<SettingRow label={t("stats.sync.quotaLeft")} hint={t("stats.sync.quotaLeftHint")}>
{s.quota_remaining_today.toLocaleString()}
</Row>
</SettingRow>
</div>
) : (
<div className="text-muted text-sm">{t("stats.sync.loading")}</div>
@ -140,12 +116,12 @@ function Overview({ me }: { me: Me }) {
{usage.data ? (
<>
<div className="space-y-1.5 text-sm">
<Row label={t("stats.sync.today")} hint={t("stats.sync.todayHint")}>
<SettingRow label={t("stats.sync.today")} hint={t("stats.sync.todayHint")}>
{usage.data.today.toLocaleString()}
</Row>
<Row label={t("stats.sync.last7d")}>{usage.data.last_7d.toLocaleString()}</Row>
<Row label={t("stats.sync.last30d")}>{usage.data.last_30d.toLocaleString()}</Row>
<Row label={t("stats.sync.allTime")}>{usage.data.all_time.toLocaleString()}</Row>
</SettingRow>
<SettingRow label={t("stats.sync.last7d")}>{usage.data.last_7d.toLocaleString()}</SettingRow>
<SettingRow label={t("stats.sync.last30d")}>{usage.data.last_30d.toLocaleString()}</SettingRow>
<SettingRow label={t("stats.sync.allTime")}>{usage.data.all_time.toLocaleString()}</SettingRow>
</div>
{Object.keys(usage.data.by_action).length > 0 && (
<div className="mt-2 pt-2 border-t border-border space-y-1 text-xs text-muted">