bcfeeb5140
Sessions and Activity Feed are two views of the same event pipeline, so they now share one page: /sessions renders a tab bar (Sessions | Activity Feed) with the active view in the ?view= query param, and /activity redirects to /sessions?view=events. The sidebar entry is dropped. The tab bar itself moves out of SettingsHub into components/PageTabs so both hubs share it, and DocumentTitle loses the two branches for paths that now only redirect.
74 lines
2.5 KiB
TypeScript
74 lines
2.5 KiB
TypeScript
/**
|
|
* @file SettingsHub.tsx
|
|
* @description Tabbed shell for /settings that hosts the dashboard's own settings plus per-AI-tool configuration panels (Claude Code today, Codex/Cursor reserved), with the active tab kept in the `?tool=` query param.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
import type { ComponentType } from "react";
|
|
import { useSearchParams } from "react-router-dom";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Boxes, Settings as SettingsIcon, SquareTerminal, MousePointerClick } from "lucide-react";
|
|
import { PageTabs } from "../components/PageTabs";
|
|
import { Settings } from "./Settings";
|
|
import { CcConfig } from "./CcConfig";
|
|
|
|
interface ToolTab {
|
|
id: string;
|
|
labelKey: string;
|
|
Icon: ComponentType<{ className?: string }>;
|
|
/** Omit for tools that are not integrated yet - the tab renders a placeholder. */
|
|
Panel?: ComponentType;
|
|
}
|
|
|
|
const DASHBOARD_TAB: ToolTab = {
|
|
id: "dashboard",
|
|
labelKey: "tools.dashboard",
|
|
Icon: SettingsIcon,
|
|
Panel: Settings,
|
|
};
|
|
|
|
/** Add a tool here to give it a tab; that is the whole extension point. */
|
|
const TOOL_TABS: ToolTab[] = [
|
|
DASHBOARD_TAB,
|
|
{ id: "claude-code", labelKey: "tools.claudeCode", Icon: Boxes, Panel: CcConfig },
|
|
{ id: "codex", labelKey: "tools.codex", Icon: SquareTerminal },
|
|
{ id: "cursor", labelKey: "tools.cursor", Icon: MousePointerClick },
|
|
];
|
|
|
|
/**
|
|
* Settings page shell with one tab per configuration surface.
|
|
* @returns The tab bar plus the active tab's panel.
|
|
*/
|
|
export function SettingsHub() {
|
|
const { t } = useTranslation("settings");
|
|
const [params, setParams] = useSearchParams();
|
|
const requested = params.get("tool");
|
|
const active = TOOL_TABS.find((tab) => tab.id === requested) ?? DASHBOARD_TAB;
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<PageTabs
|
|
ariaLabel={t("tools.label", "Tools")}
|
|
activeId={active.id}
|
|
onSelect={(id) => setParams(id === DASHBOARD_TAB.id ? {} : { tool: id }, { replace: true })}
|
|
tabs={TOOL_TABS.map(({ id, labelKey, Icon, Panel }) => ({
|
|
id,
|
|
Icon,
|
|
label: t(labelKey),
|
|
...(Panel ? {} : { badge: t("tools.soonBadge", "soon") }),
|
|
}))}
|
|
/>
|
|
|
|
{active.Panel ? (
|
|
<active.Panel />
|
|
) : (
|
|
<div className="card p-8 text-center space-y-1">
|
|
<p className="text-sm font-medium text-fg-primary">
|
|
{t("tools.comingSoon", { tool: t(active.labelKey) })}
|
|
</p>
|
|
<p className="text-xs text-fg-muted">{t("tools.comingSoonDesc")}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|