added code to enforce unique key and team aliases in the ui

This commit is contained in:
Nick Wong 2024-05-10 15:42:07 -07:00
parent 781d5888c3
commit 759ff3f750
No known key found for this signature in database
GPG key ID: F97B88DE019A52E9
2 changed files with 495 additions and 339 deletions

View file

@ -2,7 +2,13 @@ import React, { useState, useEffect } from "react";
import Link from "next/link";
import { Typography } from "antd";
import { teamDeleteCall, teamUpdateCall, teamInfoCall } from "./networking";
import { InformationCircleIcon, PencilAltIcon, PencilIcon, StatusOnlineIcon, TrashIcon } from "@heroicons/react/outline";
import {
InformationCircleIcon,
PencilAltIcon,
PencilIcon,
StatusOnlineIcon,
TrashIcon,
} from "@heroicons/react/outline";
import {
Button as Button2,
Modal,
@ -46,8 +52,12 @@ interface EditTeamModalProps {
onSubmit: (data: FormData) => void; // Assuming FormData is the type of data to be submitted
}
import { teamCreateCall, teamMemberAddCall, Member, modelAvailableCall } from "./networking";
import {
teamCreateCall,
teamMemberAddCall,
Member,
modelAvailableCall,
} from "./networking";
const Team: React.FC<TeamProps> = ({
teams,
@ -63,7 +73,6 @@ const Team: React.FC<TeamProps> = ({
const [value, setValue] = useState("");
const [editModalVisible, setEditModalVisible] = useState(false);
const [selectedTeam, setSelectedTeam] = useState<null | any>(
teams ? teams[0] : null
);
@ -76,127 +85,125 @@ const Team: React.FC<TeamProps> = ({
// store team info as {"team_id": team_info_object}
const [perTeamInfo, setPerTeamInfo] = useState<Record<string, any>>({});
const EditTeamModal: React.FC<EditTeamModalProps> = ({
visible,
onCancel,
team,
onSubmit,
}) => {
const [form] = Form.useForm();
const EditTeamModal: React.FC<EditTeamModalProps> = ({ visible, onCancel, team, onSubmit }) => {
const [form] = Form.useForm();
const handleOk = () => {
form
.validateFields()
.then((values) => {
const updatedValues = { ...values, team_id: team.team_id };
onSubmit(updatedValues);
form.resetFields();
})
.catch((error) => {
console.error("Validation failed:", error);
});
};
const handleOk = () => {
form
.validateFields()
.then((values) => {
const updatedValues = {...values, team_id: team.team_id};
onSubmit(updatedValues);
form.resetFields();
})
.catch((error) => {
console.error("Validation failed:", error);
});
};
return (
return (
<Modal
title="Edit Team"
visible={visible}
width={800}
footer={null}
onOk={handleOk}
onCancel={onCancel}
>
<Form
form={form}
onFinish={handleEditSubmit}
initialValues={team} // Pass initial values here
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
labelAlign="left"
title="Edit Team"
visible={visible}
width={800}
footer={null}
onOk={handleOk}
onCancel={onCancel}
>
<>
<Form.Item
label="Team Name"
name="team_alias"
rules={[{ required: true, message: 'Please input a team name' }]}
>
<Input />
</Form.Item>
<Form.Item label="Models" name="models">
<Select2
mode="multiple"
placeholder="Select models"
style={{ width: "100%" }}
>
<Select2.Option key="all-proxy-models" value="all-proxy-models">
{"All Proxy Models"}
</Select2.Option>
{userModels && userModels.map((model) => (
<Select2.Option key={model} value={model}>
{model}
</Select2.Option>
))}
</Select2>
</Form.Item>
<Form.Item label="Max Budget (USD)" name="max_budget">
<InputNumber step={0.01} precision={2} width={200} />
</Form.Item>
<Form.Item
label="Tokens per minute Limit (TPM)"
name="tpm_limit"
>
<InputNumber step={1} width={400} />
</Form.Item>
<Form.Item
label="Requests per minute Limit (RPM)"
name="rpm_limit"
>
<InputNumber step={1} width={400} />
</Form.Item>
<Form.Item
label="Requests per minute Limit (RPM)"
name="team_id"
hidden={true}
></Form.Item>
</>
<div style={{ textAlign: "right", marginTop: "10px" }}>
<Button2 htmlType="submit">Edit Team</Button2>
</div>
</Form>
</Modal>
);
};
const handleEditClick = (team: any) => {
setSelectedTeam(team);
setEditModalVisible(true);
};
const handleEditCancel = () => {
setEditModalVisible(false);
setSelectedTeam(null);
};
const handleEditSubmit = async (formValues: Record<string, any>) => {
// Call API to update team with teamId and values
const teamId = formValues.team_id; // get team_id
console.log("handleEditSubmit:", formValues);
if (accessToken == null) {
return;
}
let newTeamValues = await teamUpdateCall(accessToken, formValues);
// Update the teams state with the updated team data
if (teams) {
const updatedTeams = teams.map((team) =>
team.team_id === teamId ? newTeamValues.data : team
<Form
form={form}
onFinish={handleEditSubmit}
initialValues={team} // Pass initial values here
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
labelAlign="left"
>
<>
<Form.Item
label="Team Name"
name="team_alias"
rules={[{ required: true, message: "Please input a team name" }]}
>
<Input />
</Form.Item>
<Form.Item label="Models" name="models">
<Select2
mode="multiple"
placeholder="Select models"
style={{ width: "100%" }}
>
<Select2.Option key="all-proxy-models" value="all-proxy-models">
{"All Proxy Models"}
</Select2.Option>
{userModels &&
userModels.map((model) => (
<Select2.Option key={model} value={model}>
{model}
</Select2.Option>
))}
</Select2>
</Form.Item>
<Form.Item label="Max Budget (USD)" name="max_budget">
<InputNumber step={0.01} precision={2} width={200} />
</Form.Item>
<Form.Item label="Tokens per minute Limit (TPM)" name="tpm_limit">
<InputNumber step={1} width={400} />
</Form.Item>
<Form.Item label="Requests per minute Limit (RPM)" name="rpm_limit">
<InputNumber step={1} width={400} />
</Form.Item>
<Form.Item
label="Requests per minute Limit (RPM)"
name="team_id"
hidden={true}
></Form.Item>
</>
<div style={{ textAlign: "right", marginTop: "10px" }}>
<Button2 htmlType="submit">Edit Team</Button2>
</div>
</Form>
</Modal>
);
setTeams(updatedTeams);
}
message.success("Team updated successfully");
};
setEditModalVisible(false);
setSelectedTeam(null);
};
const handleEditClick = (team: any) => {
setSelectedTeam(team);
setEditModalVisible(true);
};
const handleEditCancel = () => {
setEditModalVisible(false);
setSelectedTeam(null);
};
const handleEditSubmit = async (formValues: Record<string, any>) => {
// Call API to update team with teamId and values
const teamId = formValues.team_id; // get team_id
console.log("handleEditSubmit:", formValues);
if (accessToken == null) {
return;
}
let newTeamValues = await teamUpdateCall(accessToken, formValues);
// Update the teams state with the updated team data
if (teams) {
const updatedTeams = teams.map((team) =>
team.team_id === teamId ? newTeamValues.data : team
);
setTeams(updatedTeams);
}
message.success("Team updated successfully");
setEditModalVisible(false);
setSelectedTeam(null);
};
const handleOk = () => {
setIsTeamModalVisible(false);
@ -224,9 +231,6 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
setIsDeleteModalOpen(true);
};
const confirmDelete = async () => {
if (teamToDelete == null || teams == null || accessToken == null) {
return;
@ -235,7 +239,9 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
try {
await teamDeleteCall(accessToken, teamToDelete);
// Successfully completed the deletion. Update the state to trigger a rerender.
const filteredData = teams.filter((item) => item.team_id !== teamToDelete);
const filteredData = teams.filter(
(item) => item.team_id !== teamToDelete
);
setTeams(filteredData);
} catch (error) {
console.error("Error deleting the team:", error);
@ -253,8 +259,6 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
setTeamToDelete(null);
};
useEffect(() => {
const fetchUserModels = async () => {
try {
@ -263,7 +267,11 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
}
if (accessToken !== null) {
const model_available = await modelAvailableCall(accessToken, userID, userRole);
const model_available = await modelAvailableCall(
accessToken,
userID,
userRole
);
let available_model_names = model_available["data"].map(
(element: { id: string }) => element.id
);
@ -275,7 +283,6 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
}
};
const fetchTeamInfo = async () => {
try {
if (userID === null || userRole === null || accessToken === null) {
@ -288,22 +295,21 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
console.log("fetching team info:");
let _team_id_to_info: Record<string, any> = {};
for (let i = 0; i < teams?.length; i++) {
let _team_id = teams[i].team_id;
const teamInfo = await teamInfoCall(accessToken, _team_id);
console.log("teamInfo response:", teamInfo);
if (teamInfo !== null) {
_team_id_to_info = {..._team_id_to_info, [_team_id]: teamInfo};
_team_id_to_info = { ..._team_id_to_info, [_team_id]: teamInfo };
}
}
setPerTeamInfo(_team_id_to_info);
} catch (error) {
console.error("Error fetching team info:", error);
}
};
} catch (error) {
console.error("Error fetching team info:", error);
}
};
fetchUserModels();
fetchTeamInfo();
}, [accessToken, userID, userRole, teams]);
@ -311,6 +317,15 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
const handleCreate = async (formValues: Record<string, any>) => {
try {
if (accessToken != null) {
const newTeamAlias = formValues?.team_alias;
const existingTeamAliases = teams?.map((t) => t.team_alias) ?? [];
if (existingTeamAliases.includes(newTeamAlias)) {
throw new Error(
`Team alias ${newTeamAlias} already exists, please pick another alias`
);
}
message.info("Creating Team");
const response: any = await teamCreateCall(accessToken, formValues);
if (teams !== null) {
@ -364,7 +379,7 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
console.error("Error creating the team:", error);
}
};
console.log(`received teams ${teams}`);
console.log(`received teams ${JSON.stringify(teams)}`);
return (
<div className="w-full mx-4">
<Grid numItems={1} className="gap-2 p-8 h-[75vh] w-full mt-2">
@ -387,55 +402,124 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
{teams && teams.length > 0
? teams.map((team: any) => (
<TableRow key={team.team_id}>
<TableCell style={{ maxWidth: "4px", whiteSpace: "pre-wrap", overflow: "hidden" }}>{team["team_alias"]}</TableCell>
<TableCell style={{ maxWidth: "4px", whiteSpace: "pre-wrap", overflow: "hidden" }}>{team["spend"]}</TableCell>
<TableCell style={{ maxWidth: "4px", whiteSpace: "pre-wrap", overflow: "hidden" }}>
<TableCell
style={{
maxWidth: "4px",
whiteSpace: "pre-wrap",
overflow: "hidden",
}}
>
{team["team_alias"]}
</TableCell>
<TableCell
style={{
maxWidth: "4px",
whiteSpace: "pre-wrap",
overflow: "hidden",
}}
>
{team["spend"]}
</TableCell>
<TableCell
style={{
maxWidth: "4px",
whiteSpace: "pre-wrap",
overflow: "hidden",
}}
>
{team["max_budget"] ? team["max_budget"] : "No limit"}
</TableCell>
<TableCell style={{ maxWidth: "8-x", whiteSpace: "pre-wrap", overflow: "hidden" }}>
<TableCell
style={{
maxWidth: "8-x",
whiteSpace: "pre-wrap",
overflow: "hidden",
}}
>
{Array.isArray(team.models) ? (
<div style={{ display: "flex", flexDirection: "column" }}>
<div
style={{
display: "flex",
flexDirection: "column",
}}
>
{team.models.length === 0 ? (
<Badge size={"xs"} className="mb-1" color="red">
<Text>All Proxy Models</Text>
</Badge>
) : (
team.models.map((model: string, index: number) => (
model === "all-proxy-models" ? (
<Badge key={index} size={"xs"} className="mb-1" color="red">
<Text>All Proxy Models</Text>
</Badge>
) : (
<Badge key={index} size={"xs"} className="mb-1" color="blue">
<Text>{model.length > 30 ? `${model.slice(0, 30)}...` : model}</Text>
</Badge>
)
))
team.models.map(
(model: string, index: number) =>
model === "all-proxy-models" ? (
<Badge
key={index}
size={"xs"}
className="mb-1"
color="red"
>
<Text>All Proxy Models</Text>
</Badge>
) : (
<Badge
key={index}
size={"xs"}
className="mb-1"
color="blue"
>
<Text>
{model.length > 30
? `${model.slice(0, 30)}...`
: model}
</Text>
</Badge>
)
)
)}
</div>
) : null}
</TableCell>
<TableCell style={{ maxWidth: "4px", whiteSpace: "pre-wrap", overflow: "hidden" }}>
<TableCell
style={{
maxWidth: "4px",
whiteSpace: "pre-wrap",
overflow: "hidden",
}}
>
<Text>
TPM:{" "}
{team.tpm_limit ? team.tpm_limit : "Unlimited"}{" "}
TPM: {team.tpm_limit ? team.tpm_limit : "Unlimited"}{" "}
<br></br>RPM:{" "}
{team.rpm_limit ? team.rpm_limit : "Unlimited"}
</Text>
</TableCell>
<TableCell>
<Text>{perTeamInfo && team.team_id && perTeamInfo[team.team_id] && perTeamInfo[team.team_id].keys && perTeamInfo[team.team_id].keys.length} Keys</Text>
<Text>{perTeamInfo && team.team_id && perTeamInfo[team.team_id] && perTeamInfo[team.team_id].team_info && perTeamInfo[team.team_id].team_info.members_with_roles && perTeamInfo[team.team_id].team_info.members_with_roles.length} Members</Text>
<Text>
{perTeamInfo &&
team.team_id &&
perTeamInfo[team.team_id] &&
perTeamInfo[team.team_id].keys &&
perTeamInfo[team.team_id].keys.length}{" "}
Keys
</Text>
<Text>
{perTeamInfo &&
team.team_id &&
perTeamInfo[team.team_id] &&
perTeamInfo[team.team_id].team_info &&
perTeamInfo[team.team_id].team_info
.members_with_roles &&
perTeamInfo[team.team_id].team_info
.members_with_roles.length}{" "}
Members
</Text>
</TableCell>
<TableCell>
<Icon
<Icon
icon={PencilAltIcon}
size="sm"
onClick={() => handleEditClick(team)}
/>
<Icon
<Icon
onClick={() => handleDelete(team.team_id)}
icon={TrashIcon}
size="sm"
@ -481,7 +565,11 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
</div>
</div>
<div className="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<Button onClick={confirmDelete} color="red" className="ml-2">
<Button
onClick={confirmDelete}
color="red"
className="ml-2"
>
Delete
</Button>
<Button onClick={cancelDelete}>Cancel</Button>
@ -515,10 +603,12 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
labelAlign="left"
>
<>
<Form.Item
label="Team Name"
<Form.Item
label="Team Name"
name="team_alias"
rules={[{ required: true, message: 'Please input a team name' }]}
rules={[
{ required: true, message: "Please input a team name" },
]}
>
<TextInput placeholder="" />
</Form.Item>
@ -528,7 +618,10 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
placeholder="Select models"
style={{ width: "100%" }}
>
<Select2.Option key="all-proxy-models" value="all-proxy-models">
<Select2.Option
key="all-proxy-models"
value="all-proxy-models"
>
All Proxy Models
</Select2.Option>
{userModels.map((model) => (
@ -606,8 +699,8 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
{member["user_email"]
? member["user_email"]
: member["user_id"]
? member["user_id"]
: null}
? member["user_id"]
: null}
</TableCell>
<TableCell>{member["role"]}</TableCell>
</TableRow>
@ -618,13 +711,13 @@ const handleEditSubmit = async (formValues: Record<string, any>) => {
</Table>
</Card>
{selectedTeam && (
<EditTeamModal
visible={editModalVisible}
onCancel={handleEditCancel}
team={selectedTeam}
onSubmit={handleEditSubmit}
/>
)}
<EditTeamModal
visible={editModalVisible}
onCancel={handleEditCancel}
team={selectedTeam}
onSubmit={handleEditSubmit}
/>
)}
</Col>
<Col numColSpan={1}>
<Button