'use client'; import { useState, useEffect, useCallback } from 'react'; import Dropdown from '@/components/Dropdown'; import { Team } from '@/types'; import { useToast } from '@/lib/useToast'; import { ToastContainer } from '@/components/Toast'; interface Stream { id: number; name: string; obs_source_name: string; url: string; team_id: number; } export default function AddStream() { const [formData, setFormData] = useState({ name: '', obs_source_name: '', url: '', team_id: null, }); const [teams, setTeams] = useState<{id: number; name: string}[]>([]); const [streams, setStreams] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isSubmitting, setIsSubmitting] = useState(false); const [validationErrors, setValidationErrors] = useState<{[key: string]: string}>({}); const { toasts, removeToast, showSuccess, showError } = useToast(); const fetchData = useCallback(async () => { setIsLoading(true); try { const [teamsResponse, streamsResponse] = await Promise.all([ fetch('/api/teams'), fetch('/api/streams') ]); const teamsData = await teamsResponse.json(); const streamsData = await streamsResponse.json(); // Handle both old and new API response formats const teams = teamsData.success ? teamsData.data : teamsData; const streams = streamsData.success ? streamsData.data : streamsData; // Map the API data to the format required by the Dropdown setTeams( teams.map((team: Team) => ({ id: team.team_id, name: team.team_name, })) ); setStreams(streams); } catch (error) { console.error('Failed to fetch data:', error); showError('Failed to Load Data', 'Could not fetch teams and streams. Please refresh the page.'); } finally { setIsLoading(false); } }, [showError]); // Fetch teams and streams on component mount useEffect(() => { fetchData(); }, [fetchData]); const handleInputChange = (e: React.ChangeEvent) => { const { name, value } = e.target; setFormData((prev) => ({ ...prev, [name]: value })); // Clear validation error when user starts typing if (validationErrors[name]) { setValidationErrors(prev => ({ ...prev, [name]: '' })); } }; const handleTeamSelect = (teamId: number) => { // @ts-expect-error - team_id can be null or number in formData, but TypeScript expects only number setFormData((prev) => ({ ...prev, team_id: teamId })); // Clear validation error when user selects team if (validationErrors.team_id) { setValidationErrors(prev => ({ ...prev, team_id: '' })); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); // Client-side validation const errors: {[key: string]: string} = {}; if (!formData.name.trim()) { errors.name = 'Stream name is required'; } else if (formData.name.trim().length < 2) { errors.name = 'Stream name must be at least 2 characters'; } if (!formData.obs_source_name.trim()) { errors.obs_source_name = 'OBS source name is required'; } if (!formData.url.trim()) { errors.url = 'Stream URL is required'; } else { try { new URL(formData.url); } catch { errors.url = 'Please enter a valid URL'; } } if (!formData.team_id) { errors.team_id = 'Please select a team'; } setValidationErrors(errors); if (Object.keys(errors).length > 0) { showError('Validation Error', 'Please fix the form errors'); return; } setIsSubmitting(true); try { const response = await fetch('/api/addStream', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData), }); const data = await response.json(); if (response.ok) { showSuccess('Stream Added', `"${formData.name}" has been added successfully`); setFormData({ name: '', obs_source_name: '', url: '', team_id: null }); setValidationErrors({}); fetchData(); } else { showError('Failed to Add Stream', data.error || 'Unknown error occurred'); } } catch (error) { console.error('Error adding stream:', error); showError('Failed to Add Stream', 'Network error or server unavailable'); } finally { setIsSubmitting(false); } }; return (
{/* Title */}

Streams

Organize your content by creating and managing stream sources

{/* Add New Stream */}

Add Stream

{/* Stream Name */}
{validationErrors.name && (
{validationErrors.name}
)}
{/* OBS Source Name */}
{validationErrors.obs_source_name && (
{validationErrors.obs_source_name}
)}
{/* URL */}
{validationErrors.url && (
{validationErrors.url}
)}
{/* Team Selection and Submit Button */}
{validationErrors.team_id && (
{validationErrors.team_id}
)}
{/* Streams List */}

Existing Streams

{isLoading ? (
Loading streams...
) : streams.length === 0 ? (
No streams found
Create your first stream above!
) : (
{streams.map((stream) => { const team = teams.find(t => t.id === stream.team_id); return (
{stream.name.charAt(0).toUpperCase()}
{stream.name}
OBS: {stream.obs_source_name}
Team: {team?.name || 'Unknown'}
ID: {stream.id}
View Stream
); })}
)}
{/* Toast Notifications */}
); }