diff --git a/messages/en-US.json b/messages/en-US.json index 9acb93b45..6dea651c9 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1657,6 +1657,9 @@ "aiProviderBudgetSettingsDescription": "Configure how this provider restricts usage based on spending or token limits", "aiBudgetAdd": "Add Budget", "aiBudgetEmpty": "No budgets configured yet. Click Add Budget to set a spending or token limit.", + "aiBudgetUnit": "Spend Type", + "aiBudgetPeriod": "Reset Period", + "aiBudgetAmount": "Maximum Spend", "aiBudgetAmountPlaceholder": "Maximum spend", "aiBudgetPeriodHourly": "Hourly", "aiBudgetPeriodDaily": "Daily", diff --git a/src/components/BudgetsEditor.tsx b/src/components/BudgetsEditor.tsx index 1327998cf..edef2906b 100644 --- a/src/components/BudgetsEditor.tsx +++ b/src/components/BudgetsEditor.tsx @@ -1,14 +1,18 @@ "use client"; import { + SettingsFormCell, + SettingsFormGrid, SettingsSection, SettingsSectionBody, SettingsSectionDescription, SettingsSectionFooter, + SettingsSectionForm, SettingsSectionHeader, SettingsSectionTitle } from "@app/components/Settings"; import { Button } from "@app/components/ui/button"; +import { DataTableEmptyState } from "@app/components/ui/data-table-empty-state"; import { Input } from "@app/components/ui/input"; import { Select, @@ -17,6 +21,14 @@ import { SelectTrigger, SelectValue } from "@app/components/ui/select"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow +} from "@app/components/ui/table"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; @@ -247,160 +259,239 @@ export function BudgetsEditor({ tokens: t("aiBudgetUnitTokens") }; + const addRowButton = ( + + ); + return ( -
-
- {title} - - {description} - -
- -
+ {title} + + {description} +
- {rows.length === 0 ? ( -

- {t("aiBudgetEmpty")} -

- ) : ( -
- {rows.map((row) => { - const showConflict = conflictingKeys.has(row.key); - const showInvalidAmount = - attemptedSave && - invalidAmountKeys.has(row.key); - return ( -
-
- - updateRow(row.key, { - amount: e.target.value - }) - } - className="flex-1" - /> - - - -
- {showConflict && ( -

- {t("aiBudgetConflictError")} -

- )} - {showInvalidAmount && ( -

- {t("aiBudgetInvalidAmountError")} -

- )} -
- ); - })} -
- )} + + + +
+ + + + + {t("aiBudgetUnit")} + + + {t("aiBudgetPeriod")} + + + {t("aiBudgetAmount")} + + + + + + {rows.length === 0 ? ( + + ) : ( + rows.map((row) => { + const showConflict = + conflictingKeys.has( + row.key + ); + const showInvalidAmount = + attemptedSave && + invalidAmountKeys.has( + row.key + ); + return ( + + + + + + + + + + updateRow( + row.key, + { + amount: e + .target + .value + } + ) + } + className="w-full min-w-0" + /> + + + + + + ); + }) + )} + +
+ {(conflictingKeys.size > 0 || + (attemptedSave && + invalidAmountKeys.size > 0)) && ( +

+ {conflictingKeys.size > 0 + ? t("aiBudgetConflictError") + : t("aiBudgetInvalidAmountError")} +

+ )} + {rows.length > 0 && addRowButton} +
+
+
+