feat(ui): i18n for budgets

This commit is contained in:
吹雪 2026-06-11 05:39:46 +09:00
parent 4b0371ae44
commit c81c0265ec
5 changed files with 205 additions and 54 deletions

View file

@ -1,6 +1,7 @@
import React from "react";
import { TextInput, Accordion, AccordionHeader, AccordionBody } from "@tremor/react";
import { Button as Button2, Modal, Form, InputNumber, Select } from "antd";
import { useTranslation } from "react-i18next";
import { useCreateBudget } from "@/app/(dashboard)/hooks/budgets/useBudgets";
import NotificationsManager from "../molecules/notifications_manager";
@ -9,6 +10,7 @@ interface BudgetModalProps {
setIsModalVisible: React.Dispatch<React.SetStateAction<boolean>>;
}
const BudgetModal: React.FC<BudgetModalProps> = ({ isModalVisible, setIsModalVisible }) => {
const { t } = useTranslation();
const [form] = Form.useForm();
const createBudget = useCreateBudget();
@ -24,20 +26,20 @@ const BudgetModal: React.FC<BudgetModalProps> = ({ isModalVisible, setIsModalVis
const handleCreate = async (formValues: Record<string, any>) => {
try {
NotificationsManager.info("Making API Call");
NotificationsManager.info(t("budgets.budgetModal.makingApiCall"));
await createBudget.mutateAsync(formValues);
NotificationsManager.success("Budget Created");
NotificationsManager.success(t("budgets.budgetModal.budgetCreated"));
form.resetFields();
setIsModalVisible(false);
} catch (error) {
console.error("Error creating the budget:", error);
NotificationsManager.fromBackend(`Error creating the budget: ${error}`);
NotificationsManager.fromBackend(t("budgets.budgetModal.errorCreating", { error }));
}
};
return (
<Modal
title="Create Budget"
title={t("budgets.budgetModal.title")}
open={isModalVisible}
width={800}
footer={null}
@ -47,38 +49,46 @@ const BudgetModal: React.FC<BudgetModalProps> = ({ isModalVisible, setIsModalVis
<Form form={form} onFinish={handleCreate} labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} labelAlign="left">
<>
<Form.Item
label="Budget ID"
label={t("budgets.budgetModal.budgetIdLabel")}
name="budget_id"
rules={[
{
required: true,
message: "Please input a human-friendly name for the budget",
message: t("budgets.budgetModal.budgetIdRequired"),
},
]}
help="A human-friendly name for the budget"
help={t("budgets.budgetModal.budgetIdHelp")}
>
<TextInput placeholder="" />
</Form.Item>
<Form.Item label="Max Tokens per minute" name="tpm_limit" help="Default is model limit.">
<Form.Item
label={t("budgets.budgetModal.tpmLimitLabel")}
name="tpm_limit"
help={t("budgets.budgetModal.defaultModelLimit")}
>
<InputNumber step={1} precision={2} width={200} />
</Form.Item>
<Form.Item label="Max Requests per minute" name="rpm_limit" help="Default is model limit.">
<Form.Item
label={t("budgets.budgetModal.rpmLimitLabel")}
name="rpm_limit"
help={t("budgets.budgetModal.defaultModelLimit")}
>
<InputNumber step={1} precision={2} width={200} />
</Form.Item>
<Accordion className="mt-20 mb-8">
<AccordionHeader>
<b>Optional Settings</b>
<b>{t("budgets.budgetModal.optionalSettings")}</b>
</AccordionHeader>
<AccordionBody>
<Form.Item label="Max Budget (USD)" name="max_budget">
<Form.Item label={t("budgets.budgetModal.maxBudgetLabel")} name="max_budget">
<InputNumber step={0.01} precision={2} width={200} />
</Form.Item>
<Form.Item className="mt-8" label="Reset Budget" name="budget_duration">
<Form.Item className="mt-8" label={t("budgets.budgetModal.resetBudgetLabel")} name="budget_duration">
<Select defaultValue={null} placeholder="n/a">
<Select.Option value="24h">daily</Select.Option>
<Select.Option value="7d">weekly</Select.Option>
<Select.Option value="30d">monthly</Select.Option>
<Select.Option value="24h">{t("budgets.budgetModal.daily")}</Select.Option>
<Select.Option value="7d">{t("budgets.budgetModal.weekly")}</Select.Option>
<Select.Option value="30d">{t("budgets.budgetModal.monthly")}</Select.Option>
</Select>
</Form.Item>
</AccordionBody>
@ -86,7 +96,7 @@ const BudgetModal: React.FC<BudgetModalProps> = ({ isModalVisible, setIsModalVis
</>
<div style={{ textAlign: "right", marginTop: "10px" }}>
<Button2 htmlType="submit">Create Budget</Button2>
<Button2 htmlType="submit">{t("budgets.budgetModal.createBudgetButton")}</Button2>
</div>
</Form>
</Modal>

View file

@ -20,6 +20,7 @@ import {
Text,
} from "@tremor/react";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import DeleteResourceModal from "../common_components/DeleteResourceModal";
import TableIconActionButton from "../common_components/IconActionButton/TableIconActionButtons/TableIconActionButton";
@ -44,6 +45,7 @@ export interface budgetItem {
}
const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
const { t } = useTranslation();
const [isCreateModelVisible, setIsCreateModelVisible] = useState(false);
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
const [selectedBudget, setSelectedBudget] = useState<budgetItem | null>(null);
@ -75,13 +77,13 @@ const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
}
try {
await deleteBudget.mutateAsync(selectedBudget.budget_id);
NotificationsManager.success("Budget deleted.");
NotificationsManager.success(t("budgets.budgetPanel.budgetDeleted"));
} catch (error) {
console.error("Error deleting budget:", error);
if (typeof NotificationsManager.fromBackend === "function") {
NotificationsManager.fromBackend("Failed to delete budget");
NotificationsManager.fromBackend(t("budgets.budgetPanel.failedToDelete"));
} else {
NotificationsManager.info("Failed to delete budget");
NotificationsManager.info(t("budgets.budgetPanel.failedToDelete"));
}
} finally {
setIsDeleteModalVisible(false);
@ -97,13 +99,13 @@ const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
<div className="w-full mx-auto flex-auto overflow-y-auto m-8 p-2">
{canModify && (
<Button size="sm" variant="primary" className="mb-2" onClick={() => setIsCreateModelVisible(true)}>
+ Create Budget
{t("budgets.budgetPanel.createBudgetButton")}
</Button>
)}
<TabGroup>
<TabList>
<Tab>Budgets</Tab>
<Tab>Examples</Tab>
<Tab>{t("budgets.budgetPanel.tabBudgets")}</Tab>
<Tab>{t("budgets.budgetPanel.tabExamples")}</Tab>
</TabList>
<TabPanels>
<TabPanel>
@ -117,14 +119,14 @@ const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
/>
)}
<Card>
<Text>Create a budget to assign to customers.</Text>
<Text>{t("budgets.budgetPanel.createBudgetDescription")}</Text>
<Table>
<TableHead>
<TableRow>
<TableHeaderCell>Budget ID</TableHeaderCell>
<TableHeaderCell>Max Budget</TableHeaderCell>
<TableHeaderCell>TPM</TableHeaderCell>
<TableHeaderCell>RPM</TableHeaderCell>
<TableHeaderCell>{t("budgets.budgetPanel.colBudgetId")}</TableHeaderCell>
<TableHeaderCell>{t("budgets.budgetPanel.colMaxBudget")}</TableHeaderCell>
<TableHeaderCell>{t("budgets.budgetPanel.colTpm")}</TableHeaderCell>
<TableHeaderCell>{t("budgets.budgetPanel.colRpm")}</TableHeaderCell>
</TableRow>
</TableHead>
@ -142,13 +144,13 @@ const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
<>
<TableIconActionButton
variant="Edit"
tooltipText="Edit budget"
tooltipText={t("budgets.budgetPanel.editBudgetTooltip")}
onClick={() => handleEditCall(value)}
dataTestId="edit-budget-button"
/>
<TableIconActionButton
variant="Delete"
tooltipText="Delete budget"
tooltipText={t("budgets.budgetPanel.deleteBudgetTooltip")}
onClick={() => handleDeleteClick(value)}
dataTestId="delete-budget-button"
/>
@ -161,14 +163,14 @@ const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
</Card>
<DeleteResourceModal
isOpen={isDeleteModalVisible}
title="Delete Budget?"
message="Are you sure you want to delete this budget? This action cannot be undone."
resourceInformationTitle="Budget Information"
title={t("budgets.budgetPanel.deleteTitle")}
message={t("budgets.budgetPanel.deleteMessage")}
resourceInformationTitle={t("budgets.budgetPanel.budgetInformation")}
resourceInformation={[
{ label: "Budget ID", value: selectedBudget?.budget_id, code: true },
{ label: "Max Budget", value: selectedBudget?.max_budget },
{ label: "TPM", value: selectedBudget?.tpm_limit },
{ label: "RPM", value: selectedBudget?.rpm_limit },
{ label: t("budgets.budgetPanel.colBudgetId"), value: selectedBudget?.budget_id, code: true },
{ label: t("budgets.budgetPanel.colMaxBudget"), value: selectedBudget?.max_budget },
{ label: t("budgets.budgetPanel.colTpm"), value: selectedBudget?.tpm_limit },
{ label: t("budgets.budgetPanel.colRpm"), value: selectedBudget?.rpm_limit },
]}
onCancel={handleDeleteCancel}
onOk={handleDeleteConfirm}
@ -178,12 +180,12 @@ const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
</TabPanel>
<TabPanel>
<div className="mt-6">
<Text className="text-base">How to use budget id</Text>
<Text className="text-base">{t("budgets.budgetPanel.howToUseBudgetId")}</Text>
<TabGroup>
<TabList>
<Tab>Assign Budget to Customer</Tab>
<Tab>Test it (Curl)</Tab>
<Tab>Test it (OpenAI SDK)</Tab>
<Tab>{t("budgets.budgetPanel.tabAssignBudget")}</Tab>
<Tab>{t("budgets.budgetPanel.tabTestCurl")}</Tab>
<Tab>{t("budgets.budgetPanel.tabTestSdk")}</Tab>
</TabList>
<TabPanels>
<TabPanel>

View file

@ -1,6 +1,7 @@
import React, { useEffect } from "react";
import { TextInput, Accordion, AccordionHeader, AccordionBody } from "@tremor/react";
import { Button as Button2, Modal, Form, InputNumber, Select } from "antd";
import { useTranslation } from "react-i18next";
import { useUpdateBudget } from "@/app/(dashboard)/hooks/budgets/useBudgets";
import { budgetItem } from "./budget_panel";
import NotificationsManager from "../molecules/notifications_manager";
@ -11,6 +12,7 @@ interface EditBudgetModalProps {
existingBudget: budgetItem;
}
const EditBudgetModal: React.FC<EditBudgetModalProps> = ({ isModalVisible, setIsModalVisible, existingBudget }) => {
const { t } = useTranslation();
const [form] = Form.useForm();
const updateBudget = useUpdateBudget();
@ -30,19 +32,26 @@ const EditBudgetModal: React.FC<EditBudgetModalProps> = ({ isModalVisible, setIs
const handleUpdate = async (formValues: Record<string, any>) => {
try {
NotificationsManager.info("Making API Call");
NotificationsManager.info(t("budgets.editBudgetModal.makingApiCall"));
await updateBudget.mutateAsync(formValues);
NotificationsManager.success("Budget Updated");
NotificationsManager.success(t("budgets.editBudgetModal.budgetUpdated"));
form.resetFields();
setIsModalVisible(false);
} catch (error) {
console.error("Error updating the budget:", error);
NotificationsManager.fromBackend(`Error updating the budget: ${error}`);
NotificationsManager.fromBackend(t("budgets.editBudgetModal.errorUpdating", { error }));
}
};
return (
<Modal title="Edit Budget" open={isModalVisible} width={800} footer={null} onOk={handleOk} onCancel={handleCancel}>
<Modal
title={t("budgets.editBudgetModal.title")}
open={isModalVisible}
width={800}
footer={null}
onOk={handleOk}
onCancel={handleCancel}
>
<Form
form={form}
onFinish={handleUpdate}
@ -52,29 +61,41 @@ const EditBudgetModal: React.FC<EditBudgetModalProps> = ({ isModalVisible, setIs
initialValues={existingBudget}
>
<>
<Form.Item label="Budget ID" name="budget_id" help="Budget ID cannot be changed after creation">
<Form.Item
label={t("budgets.editBudgetModal.budgetIdLabel")}
name="budget_id"
help={t("budgets.editBudgetModal.budgetIdHelp")}
>
<TextInput placeholder="" disabled={true} />
</Form.Item>
<Form.Item label="Max Tokens per minute" name="tpm_limit" help="Default is model limit.">
<Form.Item
label={t("budgets.editBudgetModal.tpmLimitLabel")}
name="tpm_limit"
help={t("budgets.editBudgetModal.defaultModelLimit")}
>
<InputNumber step={1} precision={2} width={200} />
</Form.Item>
<Form.Item label="Max Requests per minute" name="rpm_limit" help="Default is model limit.">
<Form.Item
label={t("budgets.editBudgetModal.rpmLimitLabel")}
name="rpm_limit"
help={t("budgets.editBudgetModal.defaultModelLimit")}
>
<InputNumber step={1} precision={2} width={200} />
</Form.Item>
<Accordion className="mt-20 mb-8">
<AccordionHeader>
<b>Optional Settings</b>
<b>{t("budgets.editBudgetModal.optionalSettings")}</b>
</AccordionHeader>
<AccordionBody>
<Form.Item label="Max Budget (USD)" name="max_budget">
<Form.Item label={t("budgets.editBudgetModal.maxBudgetLabel")} name="max_budget">
<InputNumber step={0.01} precision={2} width={200} />
</Form.Item>
<Form.Item className="mt-8" label="Reset Budget" name="budget_duration">
<Form.Item className="mt-8" label={t("budgets.editBudgetModal.resetBudgetLabel")} name="budget_duration">
<Select defaultValue={null} placeholder="n/a">
<Select.Option value="24h">daily</Select.Option>
<Select.Option value="7d">weekly</Select.Option>
<Select.Option value="30d">monthly</Select.Option>
<Select.Option value="24h">{t("budgets.editBudgetModal.daily")}</Select.Option>
<Select.Option value="7d">{t("budgets.editBudgetModal.weekly")}</Select.Option>
<Select.Option value="30d">{t("budgets.editBudgetModal.monthly")}</Select.Option>
</Select>
</Form.Item>
</AccordionBody>
@ -82,7 +103,7 @@ const EditBudgetModal: React.FC<EditBudgetModalProps> = ({ isModalVisible, setIs
</>
<div style={{ textAlign: "right", marginTop: "10px" }}>
<Button2 htmlType="submit">Save</Button2>
<Button2 htmlType="submit">{t("common.save")}</Button2>
</div>
</Form>
</Modal>

View file

@ -7616,5 +7616,64 @@
"needHelp": "Need Help?",
"reuseCredentials": "Reuse Credentials"
}
},
"budgets": {
"budgetModal": {
"title": "Create Budget",
"makingApiCall": "Making API Call",
"budgetCreated": "Budget Created",
"errorCreating": "Error creating the budget: {{error}}",
"budgetIdLabel": "Budget ID",
"budgetIdRequired": "Please input a human-friendly name for the budget",
"budgetIdHelp": "A human-friendly name for the budget",
"tpmLimitLabel": "Max Tokens per minute",
"rpmLimitLabel": "Max Requests per minute",
"defaultModelLimit": "Default is model limit.",
"optionalSettings": "Optional Settings",
"maxBudgetLabel": "Max Budget (USD)",
"resetBudgetLabel": "Reset Budget",
"daily": "daily",
"weekly": "weekly",
"monthly": "monthly",
"createBudgetButton": "Create Budget"
},
"budgetPanel": {
"budgetDeleted": "Budget deleted.",
"failedToDelete": "Failed to delete budget",
"createBudgetButton": "+ Create Budget",
"tabBudgets": "Budgets",
"tabExamples": "Examples",
"createBudgetDescription": "Create a budget to assign to customers.",
"colBudgetId": "Budget ID",
"colMaxBudget": "Max Budget",
"colTpm": "TPM",
"colRpm": "RPM",
"editBudgetTooltip": "Edit budget",
"deleteBudgetTooltip": "Delete budget",
"deleteTitle": "Delete Budget?",
"deleteMessage": "Are you sure you want to delete this budget? This action cannot be undone.",
"budgetInformation": "Budget Information",
"howToUseBudgetId": "How to use budget id",
"tabAssignBudget": "Assign Budget to Customer",
"tabTestCurl": "Test it (Curl)",
"tabTestSdk": "Test it (OpenAI SDK)"
},
"editBudgetModal": {
"title": "Edit Budget",
"makingApiCall": "Making API Call",
"budgetUpdated": "Budget Updated",
"errorUpdating": "Error updating the budget: {{error}}",
"budgetIdLabel": "Budget ID",
"budgetIdHelp": "Budget ID cannot be changed after creation",
"tpmLimitLabel": "Max Tokens per minute",
"rpmLimitLabel": "Max Requests per minute",
"defaultModelLimit": "Default is model limit.",
"optionalSettings": "Optional Settings",
"maxBudgetLabel": "Max Budget (USD)",
"resetBudgetLabel": "Reset Budget",
"daily": "daily",
"weekly": "weekly",
"monthly": "monthly"
}
}
}

View file

@ -7616,5 +7616,64 @@
"needHelp": "需要帮助?",
"reuseCredentials": "复用凭据"
}
},
"budgets": {
"budgetModal": {
"title": "创建预算",
"makingApiCall": "正在调用 API",
"budgetCreated": "预算已创建",
"errorCreating": "创建预算时出错:{{error}}",
"budgetIdLabel": "预算 ID",
"budgetIdRequired": "请输入一个易于识别的预算名称",
"budgetIdHelp": "一个易于识别的预算名称",
"tpmLimitLabel": "每分钟最大 Token 数",
"rpmLimitLabel": "每分钟最大请求数",
"defaultModelLimit": "默认使用模型限制。",
"optionalSettings": "可选设置",
"maxBudgetLabel": "最大预算(美元)",
"resetBudgetLabel": "重置预算",
"daily": "每天",
"weekly": "每周",
"monthly": "每月",
"createBudgetButton": "创建预算"
},
"budgetPanel": {
"budgetDeleted": "预算已删除。",
"failedToDelete": "删除预算失败",
"createBudgetButton": "+ 创建预算",
"tabBudgets": "预算",
"tabExamples": "示例",
"createBudgetDescription": "创建预算以分配给客户。",
"colBudgetId": "预算 ID",
"colMaxBudget": "最大预算",
"colTpm": "TPM",
"colRpm": "RPM",
"editBudgetTooltip": "编辑预算",
"deleteBudgetTooltip": "删除预算",
"deleteTitle": "删除预算?",
"deleteMessage": "确定要删除此预算吗?此操作不可撤销。",
"budgetInformation": "预算信息",
"howToUseBudgetId": "如何使用预算 ID",
"tabAssignBudget": "分配预算给客户",
"tabTestCurl": "测试(Curl)",
"tabTestSdk": "测试(OpenAI SDK)"
},
"editBudgetModal": {
"title": "编辑预算",
"makingApiCall": "正在调用 API",
"budgetUpdated": "预算已更新",
"errorUpdating": "更新预算时出错:{{error}}",
"budgetIdLabel": "预算 ID",
"budgetIdHelp": "预算 ID 创建后不可更改",
"tpmLimitLabel": "每分钟最大 Token 数",
"rpmLimitLabel": "每分钟最大请求数",
"defaultModelLimit": "默认使用模型限制。",
"optionalSettings": "可选设置",
"maxBudgetLabel": "最大预算(美元)",
"resetBudgetLabel": "重置预算",
"daily": "每天",
"weekly": "每周",
"monthly": "每月"
}
}
}