Reusable Duration Select and update team member budget UI

This commit is contained in:
yuneng-jiang 2026-01-06 18:15:16 -08:00
parent 6078f4687e
commit 8b3782b06a
3 changed files with 78 additions and 0 deletions

View file

@ -0,0 +1,49 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, it, expect, vi } from "vitest";
import DurationSelect from "./DurationSelect";
describe("DurationSelect", () => {
it("should render", () => {
render(<DurationSelect />);
expect(screen.getByRole("combobox")).toBeInTheDocument();
});
it("should render all three duration options", async () => {
const user = userEvent.setup();
render(<DurationSelect />);
const select = screen.getByRole("combobox");
await user.click(select);
expect(screen.getByText("Daily")).toBeInTheDocument();
expect(screen.getByText("Weekly")).toBeInTheDocument();
expect(screen.getByText("Monthly")).toBeInTheDocument();
});
it("should apply className prop", () => {
render(<DurationSelect className="test-class" />);
const select = screen.getByRole("combobox");
expect(select.closest(".test-class")).toBeInTheDocument();
});
it("should call onChange when an option is selected", async () => {
const user = userEvent.setup();
const onChange = vi.fn();
render(<DurationSelect onChange={onChange} />);
const select = screen.getByRole("combobox");
await user.click(select);
const dailyOption = screen.getByText("Daily");
await user.click(dailyOption);
expect(onChange).toHaveBeenCalledWith("24h", expect.any(Object));
});
it("should accept and pass value prop to Select", () => {
render(<DurationSelect value="7d" />);
const select = screen.getByRole("combobox");
expect(select).toBeInTheDocument();
});
});

View file

@ -0,0 +1,17 @@
import { Select } from "antd";
interface DurationSelectProps {
className?: string;
value?: string;
onChange?: (value: string) => void;
}
export default function DurationSelect({ className, value, onChange }: DurationSelectProps) {
return (
<Select className={className} value={value} onChange={onChange}>
<Select.Option value="24h">Daily</Select.Option>
<Select.Option value="7d">Weekly</Select.Option>
<Select.Option value="30d">Monthly</Select.Option>
</Select>
);
}

View file

@ -48,6 +48,7 @@ import EditLoggingSettings from "./EditLoggingSettings";
import MemberModal from "./EditMembership";
import MemberPermissions from "./member_permissions";
import TeamMembersComponent from "./team_member_view";
import DurationSelect from "../common_components/DurationSelect";
export interface TeamMembership {
user_id: string;
@ -413,6 +414,7 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
};
updateData.max_budget = mapEmptyStringToNull(updateData.max_budget);
updateData.team_member_budget_duration = values.team_member_budget_duration;
if (values.team_member_budget !== undefined) {
updateData.team_member_budget = Number(values.team_member_budget);
@ -650,6 +652,8 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
budget_duration: info.budget_duration,
team_member_tpm_limit: info.team_member_budget_table?.tpm_limit,
team_member_rpm_limit: info.team_member_budget_table?.rpm_limit,
team_member_budget: info.team_member_budget_table?.max_budget,
team_member_budget_duration: info.team_member_budget_table?.budget_duration,
guardrails: info.metadata?.guardrails || [],
disable_global_guardrails: info.metadata?.disable_global_guardrails || false,
metadata: info.metadata
@ -747,6 +751,13 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
<NumericalInput step={0.01} precision={2} style={{ width: "100%" }} />
</Form.Item>
<Form.Item label="Team Member Budget Duration" name="team_member_budget_duration">
<DurationSelect
onChange={(value) => form.setFieldValue("team_member_budget_duration", value)}
value={form.getFieldValue("team_member_budget_duration")}
/>
</Form.Item>
<Form.Item
label="Team Member Key Duration (eg: 1d, 1mo)"
name="team_member_key_duration"
@ -991,6 +1002,7 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
</Tooltip>
</Text>
<div>Max Budget: {info.team_member_budget_table?.max_budget || "No Limit"}</div>
<div>Budget Duration: {info.team_member_budget_table?.budget_duration || "No Limit"}</div>
<div>Key Duration: {info.metadata?.team_member_key_duration || "No Limit"}</div>
<div>TPM Limit: {info.team_member_budget_table?.tpm_limit || "No Limit"}</div>
<div>RPM Limit: {info.team_member_budget_table?.rpm_limit || "No Limit"}</div>