'use client'; import { useState, useEffect } from 'react'; import { Loader2, AlertCircle, Plus, Edit, Trash2, AlertTriangle, X } from 'lucide-react'; import { getCourts, createCourt, updateCourt, deleteCourt, getCourtDependencies, } from '@/src/lib/api/courts'; import type { Court, CourtRequest, CourtDependencies } from '@/src/types/courts'; import { formatTimestamp } from '@/src/types/courts'; interface ClubCourtsTabProps { clubId: number; } export default function ClubCourtsTab({ clubId }: ClubCourtsTabProps) { const [courts, setCourts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Modal state const [showAddModal, setShowAddModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); const [showDependenciesModal, setShowDependenciesModal] = useState(false); const [selectedCourt, setSelectedCourt] = useState(null); const [dependencies, setDependencies] = useState(null); useEffect(() => { loadCourts(); }, [clubId]); async function loadCourts() { setLoading(true); const result = await getCourts(clubId); if (result.success) { setCourts(result.data); setError(null); } else { setError(result.error.detail); } setLoading(false); } function handleAdd() { setShowAddModal(true); } function handleEdit(court: Court) { setSelectedCourt(court); setShowEditModal(true); } async function handleDeleteClick(court: Court) { setSelectedCourt(court); // Check dependencies first const result = await getCourtDependencies(clubId, court.court_id); if (result.success) { setDependencies(result.data); if (result.data.can_delete) { // No dependencies, show delete confirmation setShowDeleteModal(true); } else { // Has dependencies, show blocking modal setShowDependenciesModal(true); } } else { setError(result.error.detail); } } function closeModals() { setShowAddModal(false); setShowEditModal(false); setShowDeleteModal(false); setShowDependenciesModal(false); setSelectedCourt(null); setDependencies(null); } async function handleSuccess() { await loadCourts(); closeModals(); } // Loading state if (loading) { return (
); } // Error state if (error && courts.length === 0) { return (

{error}

); } return (
{/* Header with Add button */}

Courts

Manage court inventory for this club

{/* Empty state */} {courts.length === 0 ? (
🎾

No Courts Yet

Add your first court to start managing slot definitions and bookings for this club.

) : ( /* Court list */
{courts.map((court) => (

{court.name}

ID: {court.court_id} · Created {formatTimestamp(court.created_at)}

))}
)} {/* Add Modal */} {showAddModal && ( )} {/* Edit Modal */} {showEditModal && selectedCourt && ( )} {/* Delete Confirmation Modal */} {showDeleteModal && selectedCourt && ( )} {/* Dependencies Blocking Modal */} {showDependenciesModal && selectedCourt && dependencies && ( )}
); } /** * Court Form Modal (Add/Edit) */ interface CourtFormModalProps { clubId: number; court?: Court; onClose: () => void; onSuccess: () => void; } function CourtFormModal({ clubId, court, onClose, onSuccess }: CourtFormModalProps) { const isEditing = !!court; const [name, setName] = useState(court?.name || ''); const [saving, setSaving] = useState(false); const [error, setError] = useState(''); const [fieldError, setFieldError] = useState(''); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); if (!name || name.trim().length === 0) { setFieldError('Court name is required'); return; } setSaving(true); setError(''); setFieldError(''); const request: CourtRequest = { name: name.trim(), }; const result = isEditing ? await updateCourt(clubId, court!.court_id, request) : await createCourt(clubId, request); if (result.success) { onSuccess(); } else { if (result.error.code === 'validation_error' && result.error.errors) { const nameError = result.error.errors.find(e => e.field === 'name'); if (nameError) { setFieldError(nameError.message); } } else if (result.error.code === 'court_name_duplicate') { setFieldError(result.error.detail); } else { setError(result.error.detail); } } setSaving(false); } return (
{/* Header */}

{isEditing ? 'Edit Court' : 'Add Court'}

{/* Error */} {error && (

{error}

)} {/* Form */}
setName(e.target.value)} placeholder='e.g., "Court 1", "North Court", "VIP Court"' maxLength={100} className={`w-full px-4 py-3 border-2 rounded-lg font-medium transition-colors ${ fieldError ? 'border-red-300 focus:border-red-500' : 'border-slate-200 focus:border-slate-900' } focus:outline-none`} disabled={saving} autoFocus /> {fieldError && (

{fieldError}

)}

Examples: "Court 1", "North Court", "VIP Court"

{/* Actions */}
); } /** * Delete Confirmation Modal */ interface DeleteConfirmationModalProps { clubId: number; court: Court; onClose: () => void; onSuccess: () => void; } function DeleteConfirmationModal({ clubId, court, onClose, onSuccess }: DeleteConfirmationModalProps) { const [deleting, setDeleting] = useState(false); const [error, setError] = useState(''); async function handleDelete() { setDeleting(true); setError(''); const result = await deleteCourt(clubId, court.court_id); if (result.success) { onSuccess(); } else { setError(result.error.detail); } setDeleting(false); } return (
{/* Header */}

Delete Court?

{/* Error */} {error && (

{error}

)} {/* Content */}

Are you sure you want to delete "{court.name}"?

This action cannot be undone.

{/* Actions */}
); } /** * Dependencies Blocking Modal */ interface DependenciesBlockingModalProps { court: Court; dependencies: CourtDependencies; onClose: () => void; } function DependenciesBlockingModal({ court, dependencies, onClose }: DependenciesBlockingModalProps) { return (
{/* Header */}

Cannot Delete Court

{/* Content */}

Court "{court.name}" cannot be deleted because it is referenced by:

{dependencies.dependencies.slot_definitions > 0 && (
Slot definitions: {dependencies.dependencies.slot_definitions}
)} {dependencies.dependencies.slot_instances_future > 0 && (
Future slot instances: {dependencies.dependencies.slot_instances_future}
)} {dependencies.dependencies.slot_instances_booked > 0 && (
Booked slot instances: {dependencies.dependencies.slot_instances_booked}
)}

To delete this court, you must first:

    {dependencies.dependencies.slot_definitions > 0 && (
  1. Delete or reassign all slot definitions
  2. )} {dependencies.dependencies.slot_instances_future > 0 && (
  3. Delete future slot instances
  4. )} {dependencies.dependencies.slot_instances_booked > 0 && (
  5. Cancel or move all booked slot instances
  6. )}
{/* Actions */}
); }