'use client'; import { Award, DollarSign, Star, X } from 'lucide-react'; import { useCallback, useEffect, useState } from 'react'; import PendingSponsorshipRequests, { type PendingRequestDTO } from '../sponsors/PendingSponsorshipRequests'; import Button from '../ui/Button'; import Input from '../ui/Input'; import { useEffectiveDriverId } from '@/hooks/useEffectiveDriverId'; import { useServices } from '@/lib/services/ServiceProvider'; 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 { sponsorshipService, leagueService } = useServices(); 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 seasons = await leagueService.getLeagueSeasons(leagueId); const activeSeason = seasons.find((s) => s.status === 'active') ?? seasons[0]; if (activeSeason) setSeasonId(activeSeason.seasonId); } catch (err) { console.error('Failed to load season:', err); } } loadSeasonId(); }, [leagueId, propSeasonId, leagueService]); // Load pending sponsorship requests const loadPendingRequests = useCallback(async () => { if (!seasonId) return; setRequestsLoading(true); try { const requests = await sponsorshipService.getPendingSponsorshipRequests({ entityType: 'season', entityId: seasonId, }); // Convert service view-models to component DTO type (UI-only) setPendingRequests( requests.map( (r): PendingRequestDTO => ({ id: r.id, sponsorId: r.sponsorId, sponsorName: r.sponsorName, sponsorLogo: r.sponsorLogo, tier: r.tier, offeredAmount: r.offeredAmount, currency: r.currency, formattedAmount: r.formattedAmount, message: r.message, createdAt: r.createdAt, platformFee: r.platformFee, netAmount: r.netAmount, }), ), ); } catch (err) { console.error('Failed to load pending requests:', err); } finally { setRequestsLoading(false); } }, [seasonId, sponsorshipService]); useEffect(() => { loadPendingRequests(); }, [loadPendingRequests]); const handleAcceptRequest = async (requestId: string) => { try { await sponsorshipService.acceptSponsorshipRequest(requestId, 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 { await sponsorshipService.rejectSponsorshipRequest(requestId, currentDriverId, 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.

); }