import { type LucideIcon } from "lucide-react"; import { useAccountPersistedState } from "../lib/storage"; import CountBadge from "./CountBadge"; // Reusable pill tab-rail for in-page sub-navigation (Configuration, Users & roles, the Settings // sections, the Stats overview/system split…). Horizontal by default; `orientation="vertical"` gives // the Settings-style side rail (full-width, left-aligned, icon + label). The active tab is persisted // to localStorage so a reload keeps the user where they were. export interface TabDef { id: string; label: string; badge?: number; // optional count pill (e.g. pending requests); hidden when 0/undefined icon?: LucideIcon; // optional leading icon (the vertical settings rail) testId?: string; // optional data-testid, to preserve existing e2e hooks } /** Persisted active-tab state, scoped to the current account (so one account's last tab doesn't * carry into another in the same browser) — re-exported under its original name for call sites. */ export const usePersistedTab = useAccountPersistedState; export default function Tabs({ tabs, active, onChange, orientation = "horizontal", className, }: { tabs: TabDef[]; active: string; onChange: (id: string) => void; // "vertical" gives the Settings-style side rail; "horizontal" (default) is the standard row. orientation?: "horizontal" | "vertical"; // Override the row/rail wrapper — e.g. the channel manager embeds the rail in a justify-between // header, or the settings rail adds a width + right border. Omit for the orientation default. className?: string; }) { const vertical = orientation === "vertical"; const wrapCls = className ?? (vertical ? "flex flex-col gap-1" : "flex flex-wrap gap-1 mb-4"); return (
{tabs.map((tb) => { const on = tb.id === active; const Icon = tb.icon; return ( ); })}
); }