Files
UHD_Media_Tool/apps/web/app/components/settings/sections.tsx
T
rootandClaude Opus 5.5 c67f6a9682 feat(web): settings with 9 sidebar sections and Agent upstream management
- /console/settings#<section>[/<anchor>]; legacy ?tab= redirected in clientLoader
- SETTINGS_SECTIONS in contracts with field-ownership test
- Agent section: upstream list, create wizard (provider -> credentials/model -> login/test/default),
  edit drawer with OAuth browser/device login, test, set default, delete
- sites section with site registration, snapshot/rate-limit drawer and account credentials
- path mappings moved into downloader / media-server drawers; metadata and account sections

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 03:05:51 +08:00

38 lines
2.1 KiB
TypeScript

import { SETTINGS_SECTIONS, type SettingsSectionDef, type SettingsSectionId } from "@umt/contracts";
import { Bot, FolderTree, KeyRound, type LucideIcon, Server, Shield, Tags, Tv, UserCog, Zap } from "lucide-react";
import type { ReactNode } from "react";
import { AccountSection } from "./account.tsx";
import { AgentSection } from "./agent.tsx";
import { ApiKeysSection } from "./api-keys.tsx";
import { AutomationSection } from "./automation.tsx";
import { DownloadersSection, MediaServersSection } from "./instances.tsx";
import { LibrarySection } from "./library.tsx";
import { MetadataSection } from "./metadata.tsx";
import { SitesSection } from "./sites.tsx";
// 设置分区:文案与字段归属在 contracts 的 SETTINGS_SECTIONS,这里只挂图标与渲染
const UI: Record<SettingsSectionId, { icon: LucideIcon; render: () => ReactNode }> = {
automation: { icon: Zap, render: () => <AutomationSection /> },
sites: { icon: UserCog, render: () => <SitesSection /> },
downloaders: { icon: Server, render: () => <DownloadersSection /> },
library: { icon: FolderTree, render: () => <LibrarySection /> },
"media-servers": { icon: Tv, render: () => <MediaServersSection /> },
metadata: { icon: Tags, render: () => <MetadataSection /> },
agent: { icon: Bot, render: () => <AgentSection /> },
"api-keys": { icon: KeyRound, render: () => <ApiKeysSection /> },
account: { icon: Shield, render: () => <AccountSection /> },
};
export type SectionView = SettingsSectionDef & { icon: LucideIcon; render: () => ReactNode };
export const SECTIONS: SectionView[] = SETTINGS_SECTIONS.map((s) => ({ ...s, ...UI[s.id] }));
/** URL hash:`#<分区>` 或 `#<分区>/<节>`;不认识的落到默认分区 */
export function parseSettingsHash(hash: string): { section: SectionView; anchor: string | null } {
const [id, anchor] = decodeURIComponent(hash.replace(/^#/, "")).split("/");
const section = SECTIONS.find((s) => s.id === id) ?? (SECTIONS[0] as SectionView);
return { section, anchor: section.id === id && anchor ? anchor : null };
}
export const settingsHash = (section: string, anchor?: string | null) => `#${section}${anchor ? `/${anchor}` : ""}`;