Spaces:
Paused
Paused
| import React, { useState, useEffect } from "react"; | |
| import { Card, Title, Text, Divider, Button, TextInput } from "@tremor/react"; | |
| import { Typography, Spin, message, Switch, Select, Form } from "antd"; | |
| import { getDefaultTeamSettings, updateDefaultTeamSettings, modelAvailableCall } from "./networking"; | |
| import BudgetDurationDropdown, { getBudgetDurationLabel } from "./common_components/budget_duration_dropdown"; | |
| import { getModelDisplayName } from "./key_team_helpers/fetch_available_models_team_key"; | |
| interface TeamSSOSettingsProps { | |
| accessToken: string | null; | |
| userID: string; | |
| userRole: string; | |
| } | |
| const TeamSSOSettings: React.FC<TeamSSOSettingsProps> = ({ accessToken, userID, userRole }) => { | |
| const [loading, setLoading] = useState<boolean>(true); | |
| const [settings, setSettings] = useState<any>(null); | |
| const [isEditing, setIsEditing] = useState<boolean>(false); | |
| const [editedValues, setEditedValues] = useState<any>({}); | |
| const [saving, setSaving] = useState<boolean>(false); | |
| const [availableModels, setAvailableModels] = useState<string[]>([]); | |
| const { Paragraph } = Typography; | |
| const { Option } = Select; | |
| useEffect(() => { | |
| const fetchTeamSSOSettings = async () => { | |
| if (!accessToken) { | |
| setLoading(false); | |
| return; | |
| } | |
| try { | |
| const data = await getDefaultTeamSettings(accessToken); | |
| setSettings(data); | |
| setEditedValues(data.values || {}); | |
| // Fetch available models | |
| if (accessToken) { | |
| try { | |
| const modelResponse = await modelAvailableCall(accessToken, userID, userRole); | |
| if (modelResponse && modelResponse.data) { | |
| const modelNames = modelResponse.data.map((model: { id: string }) => model.id); | |
| setAvailableModels(modelNames); | |
| } | |
| } catch (error) { | |
| console.error("Error fetching available models:", error); | |
| } | |
| } | |
| } catch (error) { | |
| console.error("Error fetching team SSO settings:", error); | |
| message.error("Failed to fetch team settings"); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| fetchTeamSSOSettings(); | |
| }, [accessToken]); | |
| const handleSaveSettings = async () => { | |
| if (!accessToken) return; | |
| setSaving(true); | |
| try { | |
| const updatedSettings = await updateDefaultTeamSettings(accessToken, editedValues); | |
| setSettings({...settings, values: updatedSettings.settings}); | |
| setIsEditing(false); | |
| message.success("Default team settings updated successfully"); | |
| } catch (error) { | |
| console.error("Error updating team settings:", error); | |
| message.error("Failed to update team settings"); | |
| } finally { | |
| setSaving(false); | |
| } | |
| }; | |
| const handleTextInputChange = (key: string, value: any) => { | |
| setEditedValues((prev: Record<string, any>) => ({ | |
| ...prev, | |
| [key]: value | |
| })); | |
| }; | |
| const renderEditableField = (key: string, property: any, value: any) => { | |
| const type = property.type; | |
| if (key === "budget_duration") { | |
| return ( | |
| <BudgetDurationDropdown | |
| value={editedValues[key] || null} | |
| onChange={(value) => handleTextInputChange(key, value)} | |
| className="mt-2" | |
| /> | |
| ); | |
| } else if (type === "boolean") { | |
| return ( | |
| <div className="mt-2"> | |
| <Switch | |
| checked={!!editedValues[key]} | |
| onChange={(checked) => handleTextInputChange(key, checked)} | |
| /> | |
| </div> | |
| ); | |
| } else if (type === "array" && property.items?.enum) { | |
| return ( | |
| <Select | |
| mode="multiple" | |
| style={{ width: '100%' }} | |
| value={editedValues[key] || []} | |
| onChange={(value) => handleTextInputChange(key, value)} | |
| className="mt-2" | |
| > | |
| {property.items.enum.map((option: string) => ( | |
| <Option key={option} value={option}>{option}</Option> | |
| ))} | |
| </Select> | |
| ); | |
| } else if (key === "models") { | |
| return ( | |
| <Select | |
| mode="multiple" | |
| style={{ width: '100%' }} | |
| value={editedValues[key] || []} | |
| onChange={(value) => handleTextInputChange(key, value)} | |
| className="mt-2" | |
| > | |
| {availableModels.map((model: string) => ( | |
| <Option key={model} value={model}> | |
| {getModelDisplayName(model)} | |
| </Option> | |
| ))} | |
| </Select> | |
| ); | |
| } else if (type === "string" && property.enum) { | |
| return ( | |
| <Select | |
| style={{ width: '100%' }} | |
| value={editedValues[key] || ""} | |
| onChange={(value) => handleTextInputChange(key, value)} | |
| className="mt-2" | |
| > | |
| {property.enum.map((option: string) => ( | |
| <Option key={option} value={option}>{option}</Option> | |
| ))} | |
| </Select> | |
| ); | |
| } else { | |
| return ( | |
| <TextInput | |
| value={editedValues[key] !== undefined ? String(editedValues[key]) : ""} | |
| onChange={(e) => handleTextInputChange(key, e.target.value)} | |
| placeholder={property.description || ""} | |
| className="mt-2" | |
| /> | |
| ); | |
| } | |
| }; | |
| const renderValue = (key: string, value: any): JSX.Element => { | |
| if (value === null || value === undefined) return <span className="text-gray-400">Not set</span>; | |
| if (key === "budget_duration") { | |
| return <span>{getBudgetDurationLabel(value)}</span>; | |
| } | |
| if (typeof value === "boolean") { | |
| return <span>{value ? "Enabled" : "Disabled"}</span>; | |
| } | |
| if (key === "models" && Array.isArray(value)) { | |
| if (value.length === 0) return <span className="text-gray-400">None</span>; | |
| return ( | |
| <div className="flex flex-wrap gap-2 mt-1"> | |
| {value.map((model, index) => ( | |
| <span key={index} className="px-2 py-1 bg-blue-100 rounded text-xs"> | |
| {getModelDisplayName(model)} | |
| </span> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| if (typeof value === "object") { | |
| if (Array.isArray(value)) { | |
| if (value.length === 0) return <span className="text-gray-400">None</span>; | |
| return ( | |
| <div className="flex flex-wrap gap-2 mt-1"> | |
| {value.map((item, index) => ( | |
| <span key={index} className="px-2 py-1 bg-blue-100 rounded text-xs"> | |
| {typeof item === "object" ? JSON.stringify(item) : String(item)} | |
| </span> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <pre className="bg-gray-100 p-2 rounded text-xs overflow-auto mt-1"> | |
| {JSON.stringify(value, null, 2)} | |
| </pre> | |
| ); | |
| } | |
| return <span>{String(value)}</span>; | |
| }; | |
| if (loading) { | |
| return ( | |
| <div className="flex justify-center items-center h-64"> | |
| <Spin size="large" /> | |
| </div> | |
| ); | |
| } | |
| if (!settings) { | |
| return ( | |
| <Card> | |
| <Text>No team settings available or you do not have permission to view them.</Text> | |
| </Card> | |
| ); | |
| } | |
| // Dynamically render settings based on the schema | |
| const renderSettings = () => { | |
| const { values, schema } = settings; | |
| if (!schema || !schema.properties) { | |
| return <Text>No schema information available</Text>; | |
| } | |
| return Object.entries(schema.properties).map(([key, property]: [string, any]) => { | |
| const value = values[key]; | |
| const displayName = key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); | |
| return ( | |
| <div key={key} className="mb-6 pb-6 border-b border-gray-200 last:border-0"> | |
| <Text className="font-medium text-lg">{displayName}</Text> | |
| <Paragraph className="text-sm text-gray-500 mt-1"> | |
| {property.description || "No description available"} | |
| </Paragraph> | |
| {isEditing ? ( | |
| <div className="mt-2"> | |
| {renderEditableField(key, property, value)} | |
| </div> | |
| ) : ( | |
| <div className="mt-1 p-2 bg-gray-50 rounded"> | |
| {renderValue(key, value)} | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| }); | |
| }; | |
| return ( | |
| <Card> | |
| <div className="flex justify-between items-center mb-4"> | |
| <Title className="text-xl">Default Team Settings</Title> | |
| {!loading && settings && ( | |
| isEditing ? ( | |
| <div className="flex gap-2"> | |
| <Button | |
| variant="secondary" | |
| onClick={() => { | |
| setIsEditing(false); | |
| setEditedValues(settings.values || {}); | |
| }} | |
| disabled={saving} | |
| > | |
| Cancel | |
| </Button> | |
| <Button | |
| onClick={handleSaveSettings} | |
| loading={saving} | |
| > | |
| Save Changes | |
| </Button> | |
| </div> | |
| ) : ( | |
| <Button | |
| onClick={() => setIsEditing(true)} | |
| > | |
| Edit Settings | |
| </Button> | |
| ) | |
| )} | |
| </div> | |
| <Text> | |
| These settings will be applied by default when creating new teams. | |
| </Text> | |
| {settings?.schema?.description && ( | |
| <Paragraph className="mb-4 mt-2">{settings.schema.description}</Paragraph> | |
| )} | |
| <Divider /> | |
| <div className="mt-4 space-y-4"> | |
| {renderSettings()} | |
| </div> | |
| </Card> | |
| ); | |
| }; | |
| export default TeamSSOSettings; |