Files
gridpilot.gg/apps/website/components/sponsors/PendingSponsorshipRequests.tsx
2026-01-18 23:24:30 +01:00

134 lines
4.2 KiB
TypeScript

'use client';
import { SponsorshipRequestItem } from '@/components/sponsors/SponsorshipRequestItem';
import { Badge } from '@/ui/Badge';
import { Heading } from '@/ui/Heading';
import { Icon } from '@/ui/Icon';
import { Stack } from '@/ui/Stack';
import { Text } from '@/ui/Text';
import { Building } from 'lucide-react';
import { useState } from 'react';
export interface PendingRequestDTO {
id: string;
sponsorId: string;
sponsorName: string;
sponsorLogo?: string | undefined;
tier: string;
offeredAmount: number;
currency: string;
formattedAmount: string;
message?: string | undefined;
createdAt: string | Date;
platformFee: number;
netAmount: number;
}
interface PendingSponsorshipRequestsProps {
entityType: 'driver' | 'team' | 'race' | 'season';
entityId: string;
entityName: string;
requests: PendingRequestDTO[];
onAccept: (requestId: string) => Promise<void>;
onReject: (requestId: string, reason?: string) => Promise<void>;
isLoading?: boolean;
}
export function PendingSponsorshipRequests({
entityType,
requests,
onAccept,
onReject,
isLoading = false,
}: PendingSponsorshipRequestsProps) {
const [processingId, setProcessingId] = useState<string | null>(null);
const [rejectModalId, setRejectModalId] = useState<string | null>(null);
const [rejectReason, setRejectReason] = useState('');
const handleAccept = async (requestId: string) => {
setProcessingId(requestId);
try {
await onAccept(requestId);
} finally {
setProcessingId(null);
}
};
const handleReject = async (requestId: string) => {
setProcessingId(requestId);
try {
await onReject(requestId, rejectReason || undefined);
setRejectModalId(null);
setRejectReason('');
} finally {
setProcessingId(null);
}
};
if (isLoading) {
return (
<Stack textAlign="center" py={8}>
<Text color="text-gray-400" animate="pulse">Loading sponsorship requests...</Text>
</Stack>
);
}
if (requests.length === 0) {
return (
<Stack textAlign="center" py={8}>
<Stack w="12" h="12" mx="auto" mb={3} rounded="full" bg="bg-iron-gray/50" display="flex" alignItems="center" justifyContent="center">
<Icon icon={Building} size={6} color="rgb(115, 115, 115)" />
</Stack>
<Text color="text-gray-400" size="sm" block>No pending sponsorship requests</Text>
<Text color="text-gray-500" size="xs" mt={1} block>
When sponsors apply to sponsor this {entityType}, their requests will appear here. Sponsorships are attached to seasons, so you can change partners from season to season.
</Text>
</Stack>
);
}
return (
<Stack gap={4}>
<Stack display="flex" alignItems="center" justifyContent="between">
<Heading level={3}>Sponsorship Requests</Heading>
<Badge variant="primary">
{requests.length} pending
</Badge>
</Stack>
<Stack gap={3}>
{requests.map((request) => (
<SponsorshipRequestItem
key={request.id}
sponsorName={request.sponsorName}
sponsorLogo={request.sponsorLogo}
tier={request.tier}
formattedAmount={request.formattedAmount}
netAmount={request.netAmount}
createdAt={typeof request.createdAt === 'string' ? new Date(request.createdAt) : request.createdAt}
message={request.message}
isProcessing={processingId === request.id}
isRejecting={rejectModalId === request.id}
rejectReason={rejectReason}
onAccept={() => handleAccept(request.id)}
onRejectClick={() => setRejectModalId(request.id)}
onRejectConfirm={() => handleReject(request.id)}
onRejectCancel={() => {
setRejectModalId(null);
setRejectReason('');
}}
onRejectReasonChange={setRejectReason}
/>
))}
</Stack>
<Stack mt={4}>
<Text size="xs" color="text-gray-500" block>
<Text weight="bold" color="text-gray-400">Note:</Text> Accepting a request will activate the sponsorship.
The sponsor will be charged per season and you&apos;ll receive the payment minus 10% platform fee.
</Text>
</Stack>
</Stack>
);
}