diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.tsx index 11d75c0f750..f0529121b21 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsDetailsPage.tsx @@ -1,4 +1,5 @@ import { useAccessGroupDetails } from "@/app/(dashboard)/hooks/accessGroups/useAccessGroupDetails"; +import type { TFunction } from "i18next"; import { ArrowLeftIcon, BotIcon, @@ -10,6 +11,7 @@ import { UsersIcon, } from "lucide-react"; import { useState } from "react"; +import { useTranslation } from "react-i18next"; import DefaultProxyAdminTag from "@/components/common_components/DefaultProxyAdminTag"; import { BadgeLink } from "@/components/shared/BadgeLink"; import CopyButton from "@/components/shared/CopyButton"; @@ -34,11 +36,11 @@ const MAX_PREVIEW = 5; const shortId = (id: string) => (id.length > 20 ? `${id.slice(0, 10)}...${id.slice(-6)}` : id); -const describeAllowedTools = (tools: readonly string[] | undefined): string => { +const describeAllowedTools = (tools: readonly string[] | undefined, t: TFunction): string => { if (tools === undefined) { - return "All tools"; + return t("accessGroups.detail.toolPermissions.allTools"); } - return tools.length === 0 ? "No tools" : tools.join(", "); + return tools.length === 0 ? t("accessGroups.detail.toolPermissions.noTools") : tools.join(", "); }; function MCPToolPermissionList({ @@ -48,16 +50,17 @@ function MCPToolPermissionList({ servers: readonly AccessGroupResource[]; toolPermissions: Readonly> | null; }) { + const { t } = useTranslation(); if (toolPermissions === null || servers.length === 0) { return null; } return ( -
+
{servers.map(({ id, name }) => { return (
{name ?? id} - {describeAllowedTools(toolPermissions[id])} + {describeAllowedTools(toolPermissions[id], t)}
); })} @@ -106,6 +109,7 @@ function ResourceBadge({ } export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailProps) { + const { t } = useTranslation(); const { data: accessGroup, isLoading } = useAccessGroupDetails(accessGroupId); const [isEditModalVisible, setIsEditModalVisible] = useState(false); const [showAllKeys, setShowAllKeys] = useState(false); @@ -124,10 +128,16 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr if (!accessGroup) { return (
- -

Access group not found

+

{t("accessGroups.detail.notFound")}

); } @@ -146,47 +156,50 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
-

{accessGroup.access_group_name}

- ID: {accessGroup.access_group_id} - + + {t("accessGroups.detail.idLabel")} + {accessGroup.access_group_id} + +
- Group Details + {t("accessGroups.detail.groupDetails")}
-
Description
+
{t("accessGroups.detail.description")}
{accessGroup.description || "—"}
-
Created
+
{t("accessGroups.detail.created")}
{new Date(accessGroup.created_at).toLocaleString()} {accessGroup.created_by && ( <> - by + {t("accessGroups.detail.by")} )}
-
Last Updated
+
{t("accessGroups.detail.lastUpdated")}
{new Date(accessGroup.updated_at).toLocaleString()} {accessGroup.updated_by && ( <> - by + {t("accessGroups.detail.by")} )} @@ -200,13 +213,15 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr - Attached Keys + {t("accessGroups.detail.attachedKeys")} {keys.length} {keys.length > MAX_PREVIEW && ( )} @@ -219,7 +234,7 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr ))}
) : ( -

No keys attached

+

{t("accessGroups.detail.noKeys")}

)} @@ -228,13 +243,15 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr - Attached Teams + {t("accessGroups.detail.attachedTeams")} {teams.length} {teams.length > MAX_PREVIEW && ( )} @@ -247,7 +264,7 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr ))} ) : ( -

No teams attached

+

{t("accessGroups.detail.noTeams")}

)} @@ -259,37 +276,37 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr - Models + {t("accessGroups.detail.tabs.models")} {models.length} - MCP Servers + {t("accessGroups.detail.tabs.mcpServers")} {mcpServers.length} - Agents + {t("accessGroups.detail.tabs.agents")} {agents.length} - Skills + {t("accessGroups.detail.tabs.skills")} {skills.length} - + - + - + - + diff --git a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx index 43eaf11b60d..35089234d06 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/access-groups/_components/AccessGroupsModal/AccessGroupBaseForm.tsx @@ -2,6 +2,7 @@ import { BotIcon, InfoIcon, LayersIcon, ServerIcon, SparklesIcon } from "lucide-react"; import type { UseFormReturn } from "react-hook-form"; +import { useTranslation } from "react-i18next"; import { z } from "zod"; import { useAgents } from "@/app/(dashboard)/hooks/agents/useAgents"; @@ -16,9 +17,10 @@ import SkillSelector from "@/components/skills/SkillSelector"; import { Input } from "@/components/ui/input"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Textarea } from "@/components/ui/textarea"; +import i18n from "@/i18n/i18n"; export const accessGroupFormSchema = z.object({ - name: z.string().min(1, "Please enter the access group name"), + name: z.string().min(1, { error: () => i18n.t("accessGroups.form.name.required") }), description: z.string(), modelIds: z.array(z.string()), mcpServerIds: z.array(z.string()), @@ -80,6 +82,7 @@ export function AccessGroupBaseForm({ onTabChange, initialServerIds, }: AccessGroupBaseFormProps) { + const { t } = useTranslation(); const { accessToken } = useAuthorized(); const { data: agentsData } = useAgents(); const { data: mcpServersData } = useMCPServers(); @@ -98,56 +101,61 @@ export function AccessGroupBaseForm({ - General Info + {t("accessGroups.form.tabs.general")} - Models + {t("accessGroups.form.tabs.models")} - MCP Servers + {t("accessGroups.form.tabs.mcpServers")} - Agents + {t("accessGroups.form.tabs.agents")} - Skills + {t("accessGroups.form.tabs.skills")} - + {({ ref, ...field }) => ( - + )} - + {({ ref, ...field }) => ( -