'use client'; import { useState, useEffect, useCallback } from 'react'; import Button from '../ui/Button'; import Input from '../ui/Input'; import { DollarSign, Star, Award, Plus, X, Bell } from 'lucide-react'; import PendingSponsorshipRequests, { type PendingRequestDTO } from '../sponsors/PendingSponsorshipRequests'; import { PendingSponsorshipRequestsPresenter } from '@/lib/presenters/PendingSponsorshipRequestsPresenter'; import { useEffectiveDriverId } from '@/lib/currentDriver'; interface SponsorshipSlot { tier: 'main' | 'secondary'; sponsorName?: string; logoUrl?: string; price: number; isOccupied: boolean; } interface LeagueSponsorshipsSectionProps { leagueId: string; seasonId?: string; readOnly?: boolean; } export function LeagueSponsorshipsSection({ leagueId, seasonId: propSeasonId, readOnly = false }: LeagueSponsorshipsSectionProps) { const currentDriverId = useEffectiveDriverId(); const [slots, setSlots] = useState([ { tier: 'main', price: 500, isOccupied: false }, { tier: 'secondary', price: 200, isOccupied: false }, { tier: 'secondary', price: 200, isOccupied: false }, ]); const [editingIndex, setEditingIndex] = useState(null); const [tempPrice, setTempPrice] = useState(''); const [pendingRequests, setPendingRequests] = useState([]); const [requestsLoading, setRequestsLoading] = useState(false); const [seasonId, setSeasonId] = useState(propSeasonId); // Load season ID if not provided useEffect(() => { async function loadSeasonId() { if (propSeasonId) { setSeasonId(propSeasonId); return; } try { const seasonRepo = getSeasonRepository(); const seasons = await seasonRepo.findByLeagueId(leagueId); const activeSeason = seasons.find(s => s.status === 'active') ?? seasons[0]; if (activeSeason) { setSeasonId(activeSeason.id); } } catch (err) { console.error('Failed to load season:', err); } } loadSeasonId(); }, [leagueId, propSeasonId]); // Load pending sponsorship requests const loadPendingRequests = useCallback(async () => { if (!seasonId) return; setRequestsLoading(true); try { const useCase = getGetPendingSponsorshipRequestsUseCase(); const presenter = new PendingSponsorshipRequestsPresenter(); await useCase.execute( { entityType: 'season', entityId: seasonId, }, presenter, ); const viewModel = presenter.getViewModel(); setPendingRequests(viewModel?.requests ?? []); } catch (err) { console.error('Failed to load pending requests:', err); } finally { setRequestsLoading(false); } }, [seasonId]); useEffect(() => { loadPendingRequests(); }, [loadPendingRequests]); const handleAcceptRequest = async (requestId: string) => { try { const useCase = getAcceptSponsorshipRequestUseCase(); await useCase.execute({ requestId, respondedBy: currentDriverId, }); await loadPendingRequests(); } catch (err) { console.error('Failed to accept request:', err); alert(err instanceof Error ? err.message : 'Failed to accept request'); } }; const handleRejectRequest = async (requestId: string, reason?: string) => { try { const useCase = getRejectSponsorshipRequestUseCase(); await useCase.execute({ requestId, respondedBy: currentDriverId, ...(reason ? { reason } : {}), }); await loadPendingRequests(); } catch (err) { console.error('Failed to reject request:', err); alert(err instanceof Error ? err.message : 'Failed to reject request'); } }; const handleEditPrice = (index: number) => { const slot = slots[index]; if (!slot) return; setEditingIndex(index); setTempPrice(slot.price.toString()); }; const handleSavePrice = (index: number) => { const price = parseFloat(tempPrice); if (!isNaN(price) && price > 0) { const updated = [...slots]; const slot = updated[index]; if (slot) { slot.price = price; setSlots(updated); } } setEditingIndex(null); setTempPrice(''); }; const handleCancelEdit = () => { setEditingIndex(null); setTempPrice(''); }; const totalRevenue = slots.reduce((sum, slot) => slot.isOccupied ? sum + slot.price : sum, 0 ); const platformFee = totalRevenue * 0.10; const netRevenue = totalRevenue - platformFee; const availableSlots = slots.filter(s => !s.isOccupied).length; const occupiedSlots = slots.filter(s => s.isOccupied).length; return (
{/* Header */}

Sponsorships

Define pricing for sponsor slots in this league. Sponsors pay per season.

These sponsors are attached to seasons in this league, so you can change partners from season to season.

{!readOnly && (
{availableSlots} slot{availableSlots !== 1 ? 's' : ''} available
)}
{/* Revenue Summary */} {totalRevenue > 0 && (
Total Revenue
${totalRevenue.toFixed(2)}
Platform Fee (10%)
-${platformFee.toFixed(2)}
Net Revenue
${netRevenue.toFixed(2)}
)} {/* Sponsorship Slots */}
{slots.map((slot, index) => { const isEditing = editingIndex === index; const Icon = slot.tier === 'main' ? Star : Award; return (

{slot.tier === 'main' ? 'Main Sponsor' : 'Secondary Sponsor'}

{slot.isOccupied && ( Occupied )}

{slot.tier === 'main' ? 'Big livery slot • League page logo • Name in league title' : 'Small livery slot • League page logo'}

{isEditing ? (
setTempPrice(e.target.value)} placeholder="Price" className="w-32" min="0" step="0.01" />
) : ( <>
${slot.price.toFixed(2)}
per season
{!readOnly && !slot.isOccupied && ( )} )}
); })}
{/* Pending Sponsorship Requests */} {!readOnly && (pendingRequests.length > 0 || requestsLoading) && (
)} {/* Alpha Notice */}

Alpha Note: Sponsorship management is demonstration-only. In production, sponsors can browse leagues, select slots, and complete payment integration.

); }