mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): i18n for access groups
This commit is contained in:
parent
caf77c8419
commit
cd547a117b
7 changed files with 206 additions and 62 deletions
|
|
@ -17,6 +17,7 @@ import {
|
|||
} from "antd";
|
||||
import { ArrowLeftIcon, BotIcon, EditIcon, KeyIcon, LayersIcon, ServerIcon, UsersIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DefaultProxyAdminTag from "../common_components/DefaultProxyAdminTag";
|
||||
import { AccessGroupEditModal } from "./AccessGroupsModal/AccessGroupEditModal";
|
||||
|
||||
|
|
@ -29,6 +30,7 @@ interface AccessGroupDetailProps {
|
|||
}
|
||||
|
||||
export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailProps) {
|
||||
const { t } = useTranslation();
|
||||
const { data: accessGroup, isLoading } = useAccessGroupDetails(accessGroupId);
|
||||
const { token } = theme.useToken();
|
||||
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
|
||||
|
|
@ -61,7 +63,7 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
|
|||
}}
|
||||
>
|
||||
<Button icon={<ArrowLeftIcon size={16} />} onClick={onBack} type="text" style={{ marginBottom: 16 }} />
|
||||
<Empty description="Access group not found" />
|
||||
<Empty description={t("accessGroups.accessGroupsDetailsPage.notFound")} />
|
||||
</Content>
|
||||
);
|
||||
}
|
||||
|
|
@ -85,7 +87,7 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
|
|||
label: (
|
||||
<Flex align="center" gap={8}>
|
||||
<LayersIcon size={16} />
|
||||
Models
|
||||
{t("accessGroups.accessGroupsDetailsPage.tabModels")}
|
||||
<Tag style={{ marginInlineEnd: 0 }}>{modelIds?.length}</Tag>
|
||||
</Flex>
|
||||
),
|
||||
|
|
@ -103,7 +105,7 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
|
|||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="No models assigned to this group" />
|
||||
<Empty description={t("accessGroups.accessGroupsDetailsPage.noModels")} />
|
||||
),
|
||||
},
|
||||
{
|
||||
|
|
@ -111,7 +113,7 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
|
|||
label: (
|
||||
<Flex align="center" gap={8}>
|
||||
<ServerIcon size={16} />
|
||||
MCP Servers
|
||||
{t("accessGroups.accessGroupsDetailsPage.tabMcpServers")}
|
||||
<Tag>{mcpServerIds?.length}</Tag>
|
||||
</Flex>
|
||||
),
|
||||
|
|
@ -129,7 +131,7 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
|
|||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="No MCP servers assigned to this group" />
|
||||
<Empty description={t("accessGroups.accessGroupsDetailsPage.noMcpServers")} />
|
||||
),
|
||||
},
|
||||
{
|
||||
|
|
@ -137,7 +139,7 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
|
|||
label: (
|
||||
<Flex align="center" gap={8}>
|
||||
<BotIcon size={16} />
|
||||
Agents
|
||||
{t("accessGroups.accessGroupsDetailsPage.tabAgents")}
|
||||
<Tag>{agentIds?.length}</Tag>
|
||||
</Flex>
|
||||
),
|
||||
|
|
@ -155,7 +157,7 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
|
|||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty description="No agents assigned to this group" />
|
||||
<Empty description={t("accessGroups.accessGroupsDetailsPage.noAgents")} />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
|
@ -183,29 +185,29 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
|
|||
</div>
|
||||
</div>
|
||||
<Button type="primary" icon={<EditIcon size={16} />} onClick={handleEdit}>
|
||||
Edit Access Group
|
||||
{t("accessGroups.accessGroupsDetailsPage.editButton")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Group Details */}
|
||||
<Row style={{ marginBottom: 24 }}>
|
||||
<Card>
|
||||
<Descriptions title="Group Details" column={1}>
|
||||
<Descriptions.Item label="Description">{accessGroup.description || "—"}</Descriptions.Item>
|
||||
<Descriptions.Item label="Created">
|
||||
<Descriptions title={t("accessGroups.accessGroupsDetailsPage.groupDetails")} column={1}>
|
||||
<Descriptions.Item label={t("common.description")}>{accessGroup.description || "—"}</Descriptions.Item>
|
||||
<Descriptions.Item label={t("accessGroups.accessGroupsDetailsPage.created")}>
|
||||
{new Date(accessGroup.created_at).toLocaleString()}
|
||||
{accessGroup.created_by && (
|
||||
<Text>
|
||||
{"by"}
|
||||
{t("accessGroups.accessGroupsDetailsPage.by")}
|
||||
<DefaultProxyAdminTag userId={accessGroup.created_by} />
|
||||
</Text>
|
||||
)}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Last Updated">
|
||||
<Descriptions.Item label={t("accessGroups.accessGroupsDetailsPage.lastUpdated")}>
|
||||
{new Date(accessGroup.updated_at).toLocaleString()}
|
||||
{accessGroup.updated_by && (
|
||||
<Text>
|
||||
{"by"}
|
||||
{t("accessGroups.accessGroupsDetailsPage.by")}
|
||||
<DefaultProxyAdminTag userId={accessGroup.updated_by} />
|
||||
</Text>
|
||||
)}
|
||||
|
|
@ -221,14 +223,16 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
|
|||
title={
|
||||
<Flex align="center" gap={8}>
|
||||
<KeyIcon size={16} />
|
||||
Attached Keys
|
||||
{t("accessGroups.accessGroupsDetailsPage.attachedKeys")}
|
||||
<Tag>{keyIds?.length}</Tag>
|
||||
</Flex>
|
||||
}
|
||||
extra={
|
||||
keyIds?.length > MAX_PREVIEW ? (
|
||||
<Button type="link" onClick={() => setShowAllKeys(!showAllKeys)}>
|
||||
{showAllKeys ? "Show Less" : `View All (${keyIds?.length})`}
|
||||
{showAllKeys
|
||||
? t("accessGroups.accessGroupsDetailsPage.showLess")
|
||||
: t("accessGroups.accessGroupsDetailsPage.viewAll", { count: keyIds?.length })}
|
||||
</Button>
|
||||
) : null
|
||||
}
|
||||
|
|
@ -244,7 +248,10 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
|
|||
))}
|
||||
</Flex>
|
||||
) : (
|
||||
<Empty description="No keys attached" image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
<Empty
|
||||
description={t("accessGroups.accessGroupsDetailsPage.noKeys")}
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
</Col>
|
||||
|
|
@ -253,14 +260,16 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
|
|||
title={
|
||||
<Flex align="center" gap={8}>
|
||||
<UsersIcon size={16} />
|
||||
Attached Teams
|
||||
{t("accessGroups.accessGroupsDetailsPage.attachedTeams")}
|
||||
<Tag>{teamIds?.length}</Tag>
|
||||
</Flex>
|
||||
}
|
||||
extra={
|
||||
teamIds?.length > MAX_PREVIEW ? (
|
||||
<Button type="link" onClick={() => setShowAllTeams(!showAllTeams)}>
|
||||
{showAllTeams ? "Show Less" : `View All (${teamIds?.length})`}
|
||||
{showAllTeams
|
||||
? t("accessGroups.accessGroupsDetailsPage.showLess")
|
||||
: t("accessGroups.accessGroupsDetailsPage.viewAll", { count: teamIds?.length })}
|
||||
</Button>
|
||||
) : null
|
||||
}
|
||||
|
|
@ -276,7 +285,10 @@ export function AccessGroupDetail({ accessGroupId, onBack }: AccessGroupDetailPr
|
|||
))}
|
||||
</Flex>
|
||||
) : (
|
||||
<Empty description="No teams attached" image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
<Empty
|
||||
description={t("accessGroups.accessGroupsDetailsPage.noTeams")}
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
</Col>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { ModelSelect } from "@/components/ModelSelect/ModelSelect";
|
|||
import type { FormInstance } from "antd";
|
||||
import { Form, Input, Select, Space, Tabs } from "antd";
|
||||
import { BotIcon, InfoIcon, LayersIcon, ServerIcon } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
|
|
@ -21,6 +22,7 @@ interface AccessGroupBaseFormProps {
|
|||
}
|
||||
|
||||
export function AccessGroupBaseForm({ form, isNameDisabled = false }: AccessGroupBaseFormProps) {
|
||||
const { t } = useTranslation();
|
||||
const { data: agentsData } = useAgents();
|
||||
const { data: mcpServersData } = useMCPServers();
|
||||
|
||||
|
|
@ -32,25 +34,25 @@ export function AccessGroupBaseForm({ form, isNameDisabled = false }: AccessGrou
|
|||
label: (
|
||||
<Space align="center" size={4}>
|
||||
<InfoIcon size={16} />
|
||||
General Info
|
||||
{t("accessGroups.accessGroupBaseForm.tabGeneralInfo")}
|
||||
</Space>
|
||||
),
|
||||
children: (
|
||||
<div style={{ paddingTop: 16 }}>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="Group Name"
|
||||
label={t("accessGroups.accessGroupBaseForm.labelGroupName")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "Please enter the access group name",
|
||||
message: t("accessGroups.accessGroupBaseForm.nameRequired"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input placeholder="e.g. Engineering Team" disabled={isNameDisabled} />
|
||||
<Input placeholder={t("accessGroups.accessGroupBaseForm.namePlaceholder")} disabled={isNameDisabled} />
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label="Description">
|
||||
<TextArea rows={4} placeholder="Describe the purpose of this access group..." />
|
||||
<Form.Item name="description" label={t("common.description")}>
|
||||
<TextArea rows={4} placeholder={t("accessGroups.accessGroupBaseForm.descriptionPlaceholder")} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
),
|
||||
|
|
@ -60,12 +62,12 @@ export function AccessGroupBaseForm({ form, isNameDisabled = false }: AccessGrou
|
|||
label: (
|
||||
<Space align="center" size={4}>
|
||||
<LayersIcon size={16} />
|
||||
Models
|
||||
{t("accessGroups.accessGroupBaseForm.tabModels")}
|
||||
</Space>
|
||||
),
|
||||
children: (
|
||||
<div style={{ paddingTop: 16 }}>
|
||||
<Form.Item name="modelIds" label="Allowed Models">
|
||||
<Form.Item name="modelIds" label={t("accessGroups.accessGroupBaseForm.labelAllowedModels")}>
|
||||
<ModelSelect
|
||||
context="global"
|
||||
value={form.getFieldValue("modelIds") ?? []}
|
||||
|
|
@ -81,15 +83,15 @@ export function AccessGroupBaseForm({ form, isNameDisabled = false }: AccessGrou
|
|||
label: (
|
||||
<Space align="center" size={4}>
|
||||
<ServerIcon size={16} />
|
||||
MCP Servers
|
||||
{t("accessGroups.accessGroupBaseForm.tabMcpServers")}
|
||||
</Space>
|
||||
),
|
||||
children: (
|
||||
<div style={{ paddingTop: 16 }}>
|
||||
<Form.Item name="mcpServerIds" label="Allowed MCP Servers">
|
||||
<Form.Item name="mcpServerIds" label={t("accessGroups.accessGroupBaseForm.labelAllowedMcpServers")}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder="Select MCP servers"
|
||||
placeholder={t("accessGroups.accessGroupBaseForm.mcpServersPlaceholder")}
|
||||
style={{ width: "100%" }}
|
||||
optionFilterProp="label"
|
||||
allowClear
|
||||
|
|
@ -107,15 +109,15 @@ export function AccessGroupBaseForm({ form, isNameDisabled = false }: AccessGrou
|
|||
label: (
|
||||
<Space align="center" size={4}>
|
||||
<BotIcon size={16} />
|
||||
Agents
|
||||
{t("accessGroups.accessGroupBaseForm.tabAgents")}
|
||||
</Space>
|
||||
),
|
||||
children: (
|
||||
<div style={{ paddingTop: 16 }}>
|
||||
<Form.Item name="agentIds" label="Allowed Agents">
|
||||
<Form.Item name="agentIds" label={t("accessGroups.accessGroupBaseForm.labelAllowedAgents")}>
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder="Select agents"
|
||||
placeholder={t("accessGroups.accessGroupBaseForm.agentsPlaceholder")}
|
||||
style={{ width: "100%" }}
|
||||
optionFilterProp="label"
|
||||
allowClear
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from "react";
|
||||
import { Modal, Form } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import MessageManager from "@/components/molecules/message_manager";
|
||||
import { AccessGroupBaseForm, AccessGroupFormValues } from "./AccessGroupBaseForm";
|
||||
import {
|
||||
|
|
@ -14,6 +15,7 @@ interface AccessGroupCreateModalProps {
|
|||
}
|
||||
|
||||
export function AccessGroupCreateModal({ visible, onCancel, onSuccess }: AccessGroupCreateModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm<AccessGroupFormValues>();
|
||||
const createMutation = useCreateAccessGroup();
|
||||
|
||||
|
|
@ -31,7 +33,7 @@ export function AccessGroupCreateModal({ visible, onCancel, onSuccess }: AccessG
|
|||
|
||||
createMutation.mutate(params, {
|
||||
onSuccess: () => {
|
||||
MessageManager.success("Access group created successfully");
|
||||
MessageManager.success(t("accessGroups.accessGroupCreateModal.createSuccess"));
|
||||
form.resetFields();
|
||||
onSuccess?.();
|
||||
onCancel();
|
||||
|
|
@ -45,13 +47,13 @@ export function AccessGroupCreateModal({ visible, onCancel, onSuccess }: AccessG
|
|||
|
||||
return (
|
||||
<Modal
|
||||
title="Create Access Group"
|
||||
title={t("accessGroups.accessGroupCreateModal.title")}
|
||||
open={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={onCancel}
|
||||
width={700}
|
||||
okText="Create Group"
|
||||
cancelText="Cancel"
|
||||
okText={t("accessGroups.accessGroupCreateModal.okText")}
|
||||
cancelText={t("common.cancel")}
|
||||
confirmLoading={createMutation.isPending}
|
||||
destroyOnClose
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useEffect } from "react";
|
||||
import { Modal, Form } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import MessageManager from "@/components/molecules/message_manager";
|
||||
import { AccessGroupBaseForm, AccessGroupFormValues } from "./AccessGroupBaseForm";
|
||||
import { useEditAccessGroup, AccessGroupUpdateParams } from "@/app/(dashboard)/hooks/accessGroups/useEditAccessGroup";
|
||||
|
|
@ -13,6 +14,7 @@ interface AccessGroupEditModalProps {
|
|||
}
|
||||
|
||||
export function AccessGroupEditModal({ visible, accessGroup, onCancel, onSuccess }: AccessGroupEditModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm<AccessGroupFormValues>();
|
||||
const editMutation = useEditAccessGroup();
|
||||
|
||||
|
|
@ -45,7 +47,7 @@ export function AccessGroupEditModal({ visible, accessGroup, onCancel, onSuccess
|
|||
{ accessGroupId: accessGroup.access_group_id, params },
|
||||
{
|
||||
onSuccess: () => {
|
||||
MessageManager.success("Access group updated successfully");
|
||||
MessageManager.success(t("accessGroups.accessGroupEditModal.updateSuccess"));
|
||||
onSuccess?.();
|
||||
onCancel();
|
||||
},
|
||||
|
|
@ -59,13 +61,13 @@ export function AccessGroupEditModal({ visible, accessGroup, onCancel, onSuccess
|
|||
|
||||
return (
|
||||
<Modal
|
||||
title="Edit Access Group"
|
||||
title={t("accessGroups.accessGroupEditModal.title")}
|
||||
open={visible}
|
||||
onOk={handleOk}
|
||||
onCancel={onCancel}
|
||||
width={700}
|
||||
okText="Save Changes"
|
||||
cancelText="Cancel"
|
||||
okText={t("accessGroups.accessGroupEditModal.okText")}
|
||||
cancelText={t("common.cancel")}
|
||||
confirmLoading={editMutation.isPending}
|
||||
destroyOnHidden
|
||||
>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
import { Button, Card, Flex, Input, Layout, Pagination, Space, Table, Tag, theme, Tooltip, Typography } from "antd";
|
||||
import { BotIcon, LayersIcon, SearchIcon, ServerIcon } from "lucide-react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DeleteResourceModal from "../common_components/DeleteResourceModal";
|
||||
import TableIconActionButton from "../common_components/IconActionButton/TableIconActionButtons/TableIconActionButton";
|
||||
import {
|
||||
|
|
@ -102,6 +103,7 @@ function buildAntdColumns(
|
|||
}
|
||||
|
||||
export function AccessGroupsPage() {
|
||||
const { t } = useTranslation();
|
||||
const { token } = theme.useToken();
|
||||
const { userRole } = useAuthorized();
|
||||
// Admin Viewer follows the read-parity rule: see access groups, no writes.
|
||||
|
|
@ -162,13 +164,13 @@ export function AccessGroupsPage() {
|
|||
{
|
||||
id: "name",
|
||||
accessorKey: "name",
|
||||
header: () => <span>Name</span>,
|
||||
header: () => <span>{t("common.name")}</span>,
|
||||
enableSorting: true,
|
||||
cell: ({ getValue }) => getValue() as string,
|
||||
},
|
||||
{
|
||||
id: "resources",
|
||||
header: () => <span>Resources</span>,
|
||||
header: () => <span>{t("accessGroups.accessGroupsPage.colResources")}</span>,
|
||||
enableSorting: false,
|
||||
cell: ({ row }) => {
|
||||
const record = row.original;
|
||||
|
|
@ -177,7 +179,7 @@ export function AccessGroupsPage() {
|
|||
const agentIds = record.agentIds ?? [];
|
||||
return (
|
||||
<Flex gap={12} align="center">
|
||||
<Tooltip title={`${modelIds?.length} Models`}>
|
||||
<Tooltip title={t("accessGroups.accessGroupsPage.tooltipModels", { count: modelIds?.length })}>
|
||||
<Tag color="blue" style={{ fontSize: 14, padding: "2px 8px", margin: 0 }}>
|
||||
<Flex align="center" gap={6}>
|
||||
<LayersIcon size={14} />
|
||||
|
|
@ -185,7 +187,7 @@ export function AccessGroupsPage() {
|
|||
</Flex>
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
<Tooltip title={`${mcpServerIds?.length} MCP Servers`}>
|
||||
<Tooltip title={t("accessGroups.accessGroupsPage.tooltipMcpServers", { count: mcpServerIds?.length })}>
|
||||
<Tag color="cyan" style={{ fontSize: 14, padding: "2px 8px", margin: 0 }}>
|
||||
<Flex align="center" gap={6}>
|
||||
<ServerIcon size={14} />
|
||||
|
|
@ -193,7 +195,7 @@ export function AccessGroupsPage() {
|
|||
</Flex>
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
<Tooltip title={`${agentIds?.length} Agents`}>
|
||||
<Tooltip title={t("accessGroups.accessGroupsPage.tooltipAgents", { count: agentIds?.length })}>
|
||||
<Tag color="purple" style={{ fontSize: 14, padding: "2px 8px", margin: 0 }}>
|
||||
<Flex align="center" gap={6}>
|
||||
<BotIcon size={14} />
|
||||
|
|
@ -208,7 +210,7 @@ export function AccessGroupsPage() {
|
|||
{
|
||||
id: "createdAt",
|
||||
accessorKey: "createdAt",
|
||||
header: () => <span>Created</span>,
|
||||
header: () => <span>{t("common.createdAt")}</span>,
|
||||
enableSorting: true,
|
||||
sortingFn: "datetime",
|
||||
cell: ({ getValue }) => new Date(getValue() as string).toLocaleDateString(),
|
||||
|
|
@ -217,7 +219,7 @@ export function AccessGroupsPage() {
|
|||
{
|
||||
id: "updatedAt",
|
||||
accessorKey: "updatedAt",
|
||||
header: () => <span>Updated</span>,
|
||||
header: () => <span>{t("common.updatedAt")}</span>,
|
||||
enableSorting: false,
|
||||
cell: ({ getValue }) => new Date(getValue() as string).toLocaleDateString(),
|
||||
meta: { responsive: ["xl"] },
|
||||
|
|
@ -226,13 +228,13 @@ export function AccessGroupsPage() {
|
|||
? [
|
||||
{
|
||||
id: "actions",
|
||||
header: () => <span>Actions</span>,
|
||||
header: () => <span>{t("common.actions")}</span>,
|
||||
enableSorting: false,
|
||||
cell: ({ row }: { row: Row<AccessGroup> }) => (
|
||||
<Space>
|
||||
<TableIconActionButton
|
||||
variant="Delete"
|
||||
tooltipText="Delete access group"
|
||||
tooltipText={t("accessGroups.accessGroupsPage.deleteTooltip")}
|
||||
onClick={() => setGroupToDelete(row.original)}
|
||||
/>
|
||||
</Space>
|
||||
|
|
@ -243,7 +245,7 @@ export function AccessGroupsPage() {
|
|||
],
|
||||
// setSelectedGroup is stable (useState setter)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[canModify],
|
||||
[canModify, t],
|
||||
);
|
||||
|
||||
// ---------- TanStack table instance ----------
|
||||
|
|
@ -281,13 +283,13 @@ export function AccessGroupsPage() {
|
|||
<Flex justify="space-between" align="center" style={{ marginBottom: 16 }}>
|
||||
<Space direction="vertical" size={0}>
|
||||
<Title level={2} style={{ margin: 0 }}>
|
||||
Access Groups
|
||||
{t("accessGroups.accessGroupsPage.title")}
|
||||
</Title>
|
||||
<Text type="secondary">Manage resource permissions for your organization</Text>
|
||||
<Text type="secondary">{t("accessGroups.accessGroupsPage.subtitle")}</Text>
|
||||
</Space>
|
||||
{canModify && (
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={() => setIsCreateModalVisible(true)}>
|
||||
Create Access Group
|
||||
{t("accessGroups.accessGroupsPage.createButton")}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
|
@ -302,7 +304,7 @@ export function AccessGroupsPage() {
|
|||
>
|
||||
<Input
|
||||
prefix={<SearchIcon size={16} />}
|
||||
placeholder="Search groups by name, ID, or description..."
|
||||
placeholder={t("accessGroups.accessGroupsPage.searchPlaceholder")}
|
||||
style={{ maxWidth: 400 }}
|
||||
value={searchText}
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
|
|
@ -314,7 +316,7 @@ export function AccessGroupsPage() {
|
|||
pageSize={pageSize}
|
||||
onChange={(page) => setCurrentPage(page)}
|
||||
size="small"
|
||||
showTotal={(total) => `${total} groups`}
|
||||
showTotal={(total) => t("accessGroups.accessGroupsPage.totalGroups", { total })}
|
||||
showSizeChanger={false}
|
||||
/>
|
||||
</Flex>
|
||||
|
|
@ -325,13 +327,13 @@ export function AccessGroupsPage() {
|
|||
|
||||
<DeleteResourceModal
|
||||
isOpen={!!groupToDelete}
|
||||
title="Delete Access Group"
|
||||
message="Are you sure you want to delete this access group? This action cannot be undone."
|
||||
resourceInformationTitle="Access Group Information"
|
||||
title={t("accessGroups.accessGroupsPage.deleteTitle")}
|
||||
message={t("accessGroups.accessGroupsPage.deleteMessage")}
|
||||
resourceInformationTitle={t("accessGroups.accessGroupsPage.resourceInfoTitle")}
|
||||
resourceInformation={[
|
||||
{ label: "ID", value: groupToDelete?.id, code: true },
|
||||
{ label: "Name", value: groupToDelete?.name },
|
||||
{ label: "Description", value: groupToDelete?.description || "—" },
|
||||
{ label: t("common.name"), value: groupToDelete?.name },
|
||||
{ label: t("common.description"), value: groupToDelete?.description || "—" },
|
||||
]}
|
||||
onCancel={() => setGroupToDelete(null)}
|
||||
onOk={() => {
|
||||
|
|
|
|||
|
|
@ -6997,5 +6997,67 @@
|
|||
"revokeConnection": "Revoke connection",
|
||||
"revokeError": "Failed to revoke connection. Please try again."
|
||||
}
|
||||
},
|
||||
"accessGroups": {
|
||||
"accessGroupBaseForm": {
|
||||
"tabGeneralInfo": "General Info",
|
||||
"labelGroupName": "Group Name",
|
||||
"nameRequired": "Please enter the access group name",
|
||||
"namePlaceholder": "e.g. Engineering Team",
|
||||
"descriptionPlaceholder": "Describe the purpose of this access group...",
|
||||
"tabModels": "Models",
|
||||
"labelAllowedModels": "Allowed Models",
|
||||
"tabMcpServers": "MCP Servers",
|
||||
"labelAllowedMcpServers": "Allowed MCP Servers",
|
||||
"mcpServersPlaceholder": "Select MCP servers",
|
||||
"tabAgents": "Agents",
|
||||
"labelAllowedAgents": "Allowed Agents",
|
||||
"agentsPlaceholder": "Select agents"
|
||||
},
|
||||
"accessGroupCreateModal": {
|
||||
"title": "Create Access Group",
|
||||
"okText": "Create Group",
|
||||
"createSuccess": "Access group created successfully"
|
||||
},
|
||||
"accessGroupEditModal": {
|
||||
"title": "Edit Access Group",
|
||||
"okText": "Save Changes",
|
||||
"updateSuccess": "Access group updated successfully"
|
||||
},
|
||||
"accessGroupsDetailsPage": {
|
||||
"notFound": "Access group not found",
|
||||
"editButton": "Edit Access Group",
|
||||
"groupDetails": "Group Details",
|
||||
"by": "by",
|
||||
"tabModels": "Models",
|
||||
"noModels": "No models assigned to this group",
|
||||
"tabMcpServers": "MCP Servers",
|
||||
"noMcpServers": "No MCP servers assigned to this group",
|
||||
"tabAgents": "Agents",
|
||||
"noAgents": "No agents assigned to this group",
|
||||
"attachedKeys": "Attached Keys",
|
||||
"showLess": "Show Less",
|
||||
"viewAll": "View All ({{count}})",
|
||||
"noKeys": "No keys attached",
|
||||
"attachedTeams": "Attached Teams",
|
||||
"noTeams": "No teams attached",
|
||||
"created": "Created",
|
||||
"lastUpdated": "Last Updated"
|
||||
},
|
||||
"accessGroupsPage": {
|
||||
"title": "Access Groups",
|
||||
"subtitle": "Manage resource permissions for your organization",
|
||||
"createButton": "Create Access Group",
|
||||
"searchPlaceholder": "Search groups by name, ID, or description...",
|
||||
"totalGroups": "{{total}} groups",
|
||||
"colResources": "Resources",
|
||||
"tooltipModels": "{{count}} Models",
|
||||
"tooltipMcpServers": "{{count}} MCP Servers",
|
||||
"tooltipAgents": "{{count}} Agents",
|
||||
"deleteTooltip": "Delete access group",
|
||||
"deleteTitle": "Delete Access Group",
|
||||
"deleteMessage": "Are you sure you want to delete this access group? This action cannot be undone.",
|
||||
"resourceInfoTitle": "Access Group Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6997,5 +6997,67 @@
|
|||
"revokeConnection": "撤销连接",
|
||||
"revokeError": "撤销连接失败,请重试。"
|
||||
}
|
||||
},
|
||||
"accessGroups": {
|
||||
"accessGroupBaseForm": {
|
||||
"tabGeneralInfo": "基本信息",
|
||||
"labelGroupName": "组名称",
|
||||
"nameRequired": "请输入访问组名称",
|
||||
"namePlaceholder": "例如:工程团队",
|
||||
"descriptionPlaceholder": "描述此访问组的用途...",
|
||||
"tabModels": "模型",
|
||||
"labelAllowedModels": "允许的模型",
|
||||
"tabMcpServers": "MCP 服务器",
|
||||
"labelAllowedMcpServers": "允许的 MCP 服务器",
|
||||
"mcpServersPlaceholder": "选择 MCP 服务器",
|
||||
"tabAgents": "Agent",
|
||||
"labelAllowedAgents": "允许的 Agent",
|
||||
"agentsPlaceholder": "选择 Agent"
|
||||
},
|
||||
"accessGroupCreateModal": {
|
||||
"title": "创建访问组",
|
||||
"okText": "创建组",
|
||||
"createSuccess": "访问组创建成功"
|
||||
},
|
||||
"accessGroupEditModal": {
|
||||
"title": "编辑访问组",
|
||||
"okText": "保存更改",
|
||||
"updateSuccess": "访问组更新成功"
|
||||
},
|
||||
"accessGroupsDetailsPage": {
|
||||
"notFound": "访问组不存在",
|
||||
"editButton": "编辑访问组",
|
||||
"groupDetails": "组详情",
|
||||
"by": "由",
|
||||
"tabModels": "模型",
|
||||
"noModels": "此组未分配任何模型",
|
||||
"tabMcpServers": "MCP 服务器",
|
||||
"noMcpServers": "此组未分配任何 MCP 服务器",
|
||||
"tabAgents": "Agent",
|
||||
"noAgents": "此组未分配任何 Agent",
|
||||
"attachedKeys": "关联密钥",
|
||||
"showLess": "收起",
|
||||
"viewAll": "查看全部({{count}})",
|
||||
"noKeys": "暂无关联密钥",
|
||||
"attachedTeams": "关联团队",
|
||||
"noTeams": "暂无关联团队",
|
||||
"created": "创建时间",
|
||||
"lastUpdated": "最后更新"
|
||||
},
|
||||
"accessGroupsPage": {
|
||||
"title": "访问组",
|
||||
"subtitle": "管理组织的资源访问权限",
|
||||
"createButton": "创建访问组",
|
||||
"searchPlaceholder": "按名称、ID 或描述搜索组...",
|
||||
"totalGroups": "共 {{total}} 个组",
|
||||
"colResources": "资源",
|
||||
"tooltipModels": "{{count}} 个模型",
|
||||
"tooltipMcpServers": "{{count}} 个 MCP 服务器",
|
||||
"tooltipAgents": "{{count}} 个 Agent",
|
||||
"deleteTooltip": "删除访问组",
|
||||
"deleteTitle": "删除访问组",
|
||||
"deleteMessage": "确定要删除此访问组吗?此操作无法撤销。",
|
||||
"resourceInfoTitle": "访问组信息"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue