lib/admin-sections.ts
Rama main · réplica de lectura del repositorio original.
"use client";
import { useMemo } from "react";
import { Activity, ServerStack01Icon, DollarCircleIcon as CircleDollarSign, FileExportIcon, FlowIcon as Workflow, KeyRoundIcon as KeyRound, Mic01Icon as Mic, PieChart, ReceiptText, Search01Icon, Sparkles, UserGroupIcon as Users, VectorSquareIcon } from "@hugeicons/core-free-icons";
import { useTranslations } from "next-intl";
import type { AppIcon } from "@/components/icon";
import type { MessageKey } from "@/lib/i18n-keys";
import {
AI_MODEL_CONFIG_FIELDS,
type AiConfigField,
type AiConfigGroup,
} from "@/lib/ai-model-config";
import type { AdminTabId } from "@/lib/admin-tabs";
/** Stable section ids shared by search results and their DOM destinations. */
export const ADMIN_SECTIONS = {
accountsSummary: "accounts-summary",
accountsSupport: "accounts-support",
costsSummary: "costs-summary",
costsChart: "costs-chart",
costsByFeature: "costs-by-feature",
costsLogs: "costs-logs",
modelsFile: "models-file",
modelsAssistant: "models-assistant",
modelsEmbeddings: "models-embeddings",
modelsVoice: "models-voice",
modelsDecision: "models-decision",
modelsByok: "models-byok",
instanceVariables: "instance-variables",
} as const;
export type AdminSectionId =
(typeof ADMIN_SECTIONS)[keyof typeof ADMIN_SECTIONS];
export const ADMIN_SECTION_PARAM = "section";
export function adminSectionAnchor(id: AdminSectionId): string {
return `admin-section-${id}`;
}
export type AdminSection = {
id: AdminSectionId;
tab: AdminTabId;
icon: AppIcon;
title: string;
tabLabel: string;
keywords: string[];
};
type AdminKey = MessageKey<"Admin">;
const MODEL_GROUP_SECTIONS: Record<
AiConfigGroup,
{ id: AdminSectionId; icon: AppIcon }
> = {
assistant: { id: ADMIN_SECTIONS.modelsAssistant, icon: Sparkles },
embeddings: { id: ADMIN_SECTIONS.modelsEmbeddings, icon: VectorSquareIcon },
voice: { id: ADMIN_SECTIONS.modelsVoice, icon: Mic },
decision: { id: ADMIN_SECTIONS.modelsDecision, icon: Workflow },
byok: { id: ADMIN_SECTIONS.modelsByok, icon: KeyRound },
};
/**
* Administration search catalog in the current locale.
*
* Model rows remain grouped by their visible card, just like account/project
* settings search: a query for “Reasoning level” returns the Assistant card.
* Raw config keys stay in the keyword set so English technical terms remain
* discoverable from every locale.
*/
export function useAdminSections(): AdminSection[] {
const t = useTranslations("Admin");
return useMemo(() => {
const tabs: Record<AdminTabId, string> = {
accounts: t("tabs.accounts"),
costs: t("tabs.costs"),
models: t("tabs.models"),
instance: t("tabs.instance"),
};
const section = (value: Omit<AdminSection, "tabLabel">): AdminSection => ({
...value,
tabLabel: tabs[value.tab],
});
const fieldLabel = (field: AiConfigField): string => {
const key = `fields.${field.key}.label` as AdminKey;
return field.adminLabel ?? (t.has(key) ? t(key) : field.key);
};
const modelSections = (
Object.keys(MODEL_GROUP_SECTIONS) as AiConfigGroup[]
).map((group) => {
const meta = MODEL_GROUP_SECTIONS[group];
const fields = AI_MODEL_CONFIG_FIELDS.filter((field) => field.group === group);
return section({
id: meta.id,
tab: "models",
icon: meta.icon,
title: t(`groups.${group}.title`),
keywords: fields.flatMap((field) => [field.key, fieldLabel(field)]),
});
});
return [
section({
id: ADMIN_SECTIONS.accountsSummary,
tab: "accounts",
icon: Users,
title: t("accounts.countLabel"),
keywords: ["accounts", "users", "count"],
}),
section({
id: ADMIN_SECTIONS.accountsSupport,
tab: "accounts",
icon: Search01Icon,
title: t("accounts.supportTitle"),
keywords: [
t("users.usageTitle"),
t("billing.title"),
t("quotas.reset"),
"email",
"quota",
"budget",
"gift plan",
"usage",
],
}),
section({
id: ADMIN_SECTIONS.costsSummary,
tab: "costs",
icon: CircleDollarSign,
title: t("costs.summaryTitle"),
keywords: [t("costs.kpiSpend"), t("costs.kpiRequests"), t("costs.kpiTokens"), "cost", "spend", "month"],
}),
section({
id: ADMIN_SECTIONS.costsChart,
tab: "costs",
icon: Activity,
title: t("costs.spendByModelTitle"),
keywords: [t("costs.topModelsTitle"), t("costs.requestsByModelTitle"), "chart", "daily", "model"],
}),
section({
id: ADMIN_SECTIONS.costsByFeature,
tab: "costs",
icon: PieChart,
title: t("costs.topFeaturesTitle"),
keywords: [t("costs.spendByGroupTitle"), "feature", "cost", "usage"],
}),
section({
id: ADMIN_SECTIONS.costsLogs,
tab: "costs",
icon: ReceiptText,
title: t("costs.logsTitle"),
keywords: ["log", "runs", "calls", "model", "tokens"],
}),
section({
id: ADMIN_SECTIONS.modelsFile,
tab: "models",
icon: FileExportIcon,
title: t("modelsFile.title"),
keywords: [t("modelsFile.export"), t("modelsFile.import"), "export", "import", "json", "file"],
}),
...modelSections,
section({
id: ADMIN_SECTIONS.instanceVariables,
tab: "instance",
icon: ServerStack01Icon,
title: t("instance.title"),
keywords: [
"environment",
"variables",
".env",
"OPENROUTER_API_KEY",
"ADMIN_EMAILS",
"EMAIL_PROVIDER",
"GITHUB_APP_ID",
"GITLAB_OAUTH_CLIENT_ID",
"POSTHOG_API_KEY",
],
}),
];
}, [t]);
}