'use client'; import { useState, useEffect } from 'react'; import { Team } from '@/types'; import { useToast } from '@/lib/useToast'; import { ToastContainer } from '@/components/Toast'; export default function Teams() { const [teams, setTeams] = useState([]); const [isLoading, setIsLoading] = useState(true); const [newTeamName, setNewTeamName] = useState(''); const [editingTeam, setEditingTeam] = useState(null); const [editingName, setEditingName] = useState(''); const [creatingGroupForTeam, setCreatingGroupForTeam] = useState(null); const [isSyncing, setIsSyncing] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const [updatingTeamId, setUpdatingTeamId] = useState(null); const [deletingTeamId, setDeletingTeamId] = useState(null); const [validationErrors, setValidationErrors] = useState<{[key: string]: string}>({}); const { toasts, removeToast, showSuccess, showError } = useToast(); useEffect(() => { fetchTeams(); }, []); const fetchTeams = async () => { setIsLoading(true); try { const res = await fetch('/api/teams'); const data = await res.json(); // Handle both old and new API response formats const teams = data.success ? data.data : data; setTeams(teams); } catch (error) { console.error('Error fetching teams:', error); } finally { setIsLoading(false); } }; const handleAddTeam = async (e: React.FormEvent) => { e.preventDefault(); // Client-side validation const errors: {[key: string]: string} = {}; if (!newTeamName.trim()) { errors.newTeamName = 'Team name is required'; } else if (newTeamName.trim().length < 2) { errors.newTeamName = 'Team name must be at least 2 characters'; } else if (newTeamName.trim().length > 50) { errors.newTeamName = 'Team name must be less than 50 characters'; } setValidationErrors(errors); if (Object.keys(errors).length > 0) { showError('Validation Error', 'Please fix the form errors'); return; } setIsSubmitting(true); try { const res = await fetch('/api/teams', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ team_name: newTeamName }), }); if (res.ok) { setNewTeamName(''); fetchTeams(); showSuccess('Team Added', `"${newTeamName}" has been added successfully`); } else { const error = await res.json(); showError('Failed to Add Team', error.error || 'Unknown error occurred'); } } catch (error) { console.error('Error adding team:', error); showError('Failed to Add Team', 'Network error or server unavailable'); } finally { setIsSubmitting(false); } }; const handleUpdateTeam = async (teamId: number) => { // Client-side validation if (!editingName.trim()) { showError('Validation Error', 'Team name cannot be empty'); return; } if (editingName.trim().length < 2) { showError('Validation Error', 'Team name must be at least 2 characters'); return; } if (editingName.trim().length > 50) { showError('Validation Error', 'Team name must be less than 50 characters'); return; } setUpdatingTeamId(teamId); try { const res = await fetch(`/api/teams/${teamId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ team_name: editingName }), }); if (res.ok) { setEditingTeam(null); setEditingName(''); fetchTeams(); showSuccess('Team Updated', `Team name changed to "${editingName}"`); } else { const error = await res.json(); showError('Failed to Update Team', error.error || 'Unknown error occurred'); } } catch (error) { console.error('Error updating team:', error); showError('Failed to Update Team', 'Network error or server unavailable'); } finally { setUpdatingTeamId(null); } }; const handleDeleteTeam = async (teamId: number) => { const teamToDelete = teams.find(t => t.team_id === teamId); if (!confirm('Are you sure you want to delete this team? This will also delete all associated streams.')) { return; } setDeletingTeamId(teamId); try { const res = await fetch(`/api/teams/${teamId}`, { method: 'DELETE', }); if (res.ok) { fetchTeams(); showSuccess('Team Deleted', `"${teamToDelete?.team_name || 'Team'}" has been deleted`); } else { const error = await res.json(); showError('Failed to Delete Team', error.error || 'Unknown error occurred'); } } catch (error) { console.error('Error deleting team:', error); showError('Failed to Delete Team', 'Network error or server unavailable'); } finally { setDeletingTeamId(null); } }; const handleSyncAllGroups = async () => { if (!confirm('This will create OBS groups for all teams that don\'t have one. Continue?')) { return; } setIsSyncing(true); try { const res = await fetch('/api/syncGroups', { method: 'POST', headers: { 'Content-Type': 'application/json' }, }); if (res.ok) { const result = await res.json(); fetchTeams(); showSuccess('Groups Synced', `${result.summary.successful} groups created successfully`); if (result.summary.failed > 0) { showError('Some Failures', `${result.summary.failed} groups failed to create`); } } else { const error = await res.json(); showError('Failed to Sync Groups', error.error || 'Unknown error occurred'); } } catch (error) { console.error('Error syncing groups:', error); showError('Failed to Sync Groups', 'Network error or server unavailable'); } finally { setIsSyncing(false); } }; const handleCreateGroup = async (teamId: number, teamName: string) => { const groupName = prompt(`Enter group name for team "${teamName}":`, teamName); if (!groupName) return; setCreatingGroupForTeam(teamId); try { const res = await fetch('/api/createGroup', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ teamId, groupName }), }); if (res.ok) { fetchTeams(); showSuccess('Group Created', `OBS group "${groupName}" created for team "${teamName}"`); } else { const error = await res.json(); showError('Failed to Create Group', error.error || 'Unknown error occurred'); } } catch (error) { console.error('Error creating group:', error); showError('Failed to Create Group', 'Network error or server unavailable'); } finally { setCreatingGroupForTeam(null); } }; const startEditing = (team: Team) => { setEditingTeam(team); setEditingName(team.team_name); }; const cancelEditing = () => { setEditingTeam(null); setEditingName(''); }; return (
{/* Title */}

Team Management

Organize your streams by creating and managing teams

{/* Add New Team */}

Add New Team

{ setNewTeamName(e.target.value); // Clear validation error when user starts typing if (validationErrors.newTeamName) { setValidationErrors(prev => ({ ...prev, newTeamName: '' })); } }} placeholder="Enter team name" className={`input ${ validationErrors.newTeamName ? 'border-red-500/60 bg-red-500/10' : '' }`} style={{ flex: 1 }} required />
{validationErrors.newTeamName && (
{validationErrors.newTeamName}
)}
{/* Teams List */}

Existing Teams

{isLoading ? (
Loading teams...
) : teams.length === 0 ? (
No teams found
Create your first team above!
) : (
{teams.map((team) => (
{editingTeam?.team_id === team.team_id ? (
setEditingName(e.target.value)} className="input" style={{ flex: 1 }} autoFocus />
) : (
{team.team_name.charAt(0).toUpperCase()}
{team.team_name}
ID: {team.team_id}
{team.group_name ? (
OBS Group: {team.group_name}
) : (
No OBS Group
)}
{!team.group_name && ( )}
)}
))}
)}
{/* Toast Notifications */}
); }