fix(ui): prepopulate team ID during team creation

This commit is contained in:
Yujong Lee 2026-09-03 08:24:47 -07:00
parent 11a02b9581
commit c12bdfa81d
2 changed files with 25 additions and 4 deletions

View file

@ -18,6 +18,7 @@ import Teams from "./Teams";
import { chooseSelectOption } from "../../tests/test-utils";
const can = vi.fn();
const UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
vi.mock("@/app/(dashboard)/hooks/useCan", () => ({
default: (...args: unknown[]) => can(...args),
}));
@ -1188,6 +1189,20 @@ describe("Teams - which fields reach the create payload depends on the open sect
);
});
it("prepopulates an editable UUID when the create form opens", async () => {
await openCreateModal();
fireEvent.change(screen.getByLabelText(/team name/i), { target: { value: "Generated ID Team" } });
toggleAdditionalSettings();
const teamIdInput = await screen.findByLabelText("Team ID");
const generatedTeamId = (teamIdInput as HTMLInputElement).value;
expect(generatedTeamId).toMatch(UUID_V4_PATTERN);
const payload = await submit();
expect(payload.team_id).toBe(generatedTeamId);
});
it("drops a value typed in Additional Settings when that section is closed again before saving", async () => {
await openCreateModal();
fireEvent.change(screen.getByLabelText(/team name/i), { target: { value: "Reclosed Team" } });
@ -1318,7 +1333,7 @@ describe("Teams - the exact bytes the create call sends", () => {
tpm_limit: undefined,
rpm_limit: undefined,
metadata: undefined,
team_id: undefined,
team_id: expect.stringMatching(UUID_V4_PATTERN),
team_member_budget: undefined,
team_member_key_duration: undefined,
team_member_rpm_limit: undefined,
@ -1339,6 +1354,7 @@ describe("Teams - the exact bytes the create call sends", () => {
team_alias: "Byte Contract Team",
organization_id: null,
models: ["no-default-models"],
team_id: expect.stringMatching(UUID_V4_PATTERN),
mcp_tool_permissions: {},
});
});
@ -1459,7 +1475,7 @@ describe("Teams - the exact bytes the create call sends", () => {
tpm_limit: undefined,
rpm_limit: undefined,
metadata: undefined,
team_id: undefined,
team_id: expect.stringMatching(UUID_V4_PATTERN),
team_member_budget: undefined,
team_member_key_duration: undefined,
team_member_rpm_limit: undefined,

View file

@ -19,6 +19,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ChevronDown, Plus, Users } from "lucide-react";
import React, { useEffect, useMemo, useState } from "react";
import { z } from "zod/v4";
import { v4 as uuidv4 } from "uuid";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { PageHeader } from "@/components/shared/PageHeader";
import { Button as UIButton } from "@/components/ui/button";
@ -77,7 +78,10 @@ const teamCreateFieldsSchema = z.object({
tpm_limit: numericInputSchema,
rpm_limit: numericInputSchema,
metadata: metadataPairsSchema.optional(),
team_id: z.string().optional(),
team_id: z
.string()
.optional()
.transform((value) => (value?.trim() ? value : undefined)),
team_member_budget: z.number().optional(),
team_member_key_duration: z.string().optional(),
team_member_rpm_limit: numericInputSchema,
@ -314,6 +318,7 @@ const Teams: React.FC<TeamProps> = ({ accessToken, userID, userRole, premiumUser
}, [accessToken, canViewPolicies]);
const openCreateTeamModal = () => {
form.setValue("team_id", uuidv4());
// Org admins must scope a team to an org, so with exactly one we preselect it.
// Proxy admins can create org-less teams, so the field stays optional regardless of org count.
if (isOrgAdmin && adminOrgs.length === 1) {
@ -829,7 +834,7 @@ const Teams: React.FC<TeamProps> = ({ accessToken, userID, userRole, premiumUser
control={form.control}
name="team_id"
label="Team ID"
description="ID of the team you want to create. If not provided, it will be generated automatically."
description="Leave blank to generate an ID when the team is created, or enter a custom ID."
>
{({ ref, value, ...field }) => <UIInput {...field} ref={ref} value={value ?? ""} />}
</FormField>