148 lines
4.5 KiB
TypeScript
148 lines
4.5 KiB
TypeScript
'use client';
|
|
|
|
import { useLeagueMembershipMutation } from "@/hooks/league/useLeagueMembershipMutation";
|
|
import { useEffectiveDriverId } from "@/hooks/useEffectiveDriverId";
|
|
import { getMembership } from '@/lib/leagueMembership';
|
|
import { Box } from '@/ui/Box';
|
|
import { Button } from '@/ui/Button';
|
|
import { Modal } from '@/components/shared/Modal';
|
|
import { Text } from '@/ui/Text';
|
|
import { useState } from 'react';
|
|
|
|
interface JoinLeagueButtonProps {
|
|
leagueId: string;
|
|
isInviteOnly?: boolean;
|
|
onMembershipChange?: () => void;
|
|
}
|
|
|
|
export function JoinLeagueButton({
|
|
leagueId,
|
|
isInviteOnly = false,
|
|
onMembershipChange,
|
|
}: JoinLeagueButtonProps) {
|
|
const currentDriverId = useEffectiveDriverId();
|
|
const membership = currentDriverId ? getMembership(leagueId, currentDriverId) : null;
|
|
const { joinLeague, leaveLeague } = useLeagueMembershipMutation();
|
|
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
|
const [dialogAction, setDialogAction] = useState<'join' | 'leave' | 'request'>('join');
|
|
|
|
const handleJoin = async () => {
|
|
if (!currentDriverId) return;
|
|
|
|
setError(null);
|
|
try {
|
|
if (isInviteOnly) {
|
|
throw new Error(
|
|
'Requesting to join invite-only leagues is not available in this alpha build.',
|
|
);
|
|
}
|
|
|
|
await joinLeague.mutateAsync({ leagueId, driverId: currentDriverId });
|
|
|
|
onMembershipChange?.();
|
|
setShowConfirmDialog(false);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Failed to join league');
|
|
}
|
|
};
|
|
|
|
const handleLeave = async () => {
|
|
if (!currentDriverId) return;
|
|
|
|
setError(null);
|
|
try {
|
|
if (membership?.role === 'owner') {
|
|
throw new Error('League owner cannot leave the league');
|
|
}
|
|
|
|
await leaveLeague.mutateAsync({ leagueId, driverId: currentDriverId });
|
|
|
|
onMembershipChange?.();
|
|
setShowConfirmDialog(false);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Failed to leave league');
|
|
}
|
|
};
|
|
|
|
const openDialog = (action: 'join' | 'leave' | 'request') => {
|
|
setDialogAction(action);
|
|
setShowConfirmDialog(true);
|
|
setError(null);
|
|
};
|
|
|
|
const closeDialog = () => {
|
|
setShowConfirmDialog(false);
|
|
setError(null);
|
|
};
|
|
|
|
const getButtonText = (): string => {
|
|
if (!membership) {
|
|
return isInviteOnly ? 'Request to Join' : 'Join League';
|
|
}
|
|
if (membership.role === 'owner') {
|
|
return 'League Owner';
|
|
}
|
|
return 'Leave League';
|
|
};
|
|
|
|
const getButtonVariant = (): 'primary' | 'secondary' | 'danger' => {
|
|
if (!membership) return 'primary';
|
|
if (membership.role === 'owner') return 'secondary';
|
|
return 'danger';
|
|
};
|
|
|
|
const isDisabled = membership?.role === 'owner' || joinLeague.isPending || leaveLeague.isPending;
|
|
|
|
return (
|
|
<Box>
|
|
<Button
|
|
variant={getButtonVariant()}
|
|
onClick={() => {
|
|
if (membership) {
|
|
openDialog('leave');
|
|
} else {
|
|
openDialog(isInviteOnly ? 'request' : 'join');
|
|
}
|
|
}}
|
|
disabled={isDisabled}
|
|
fullWidth
|
|
>
|
|
{(joinLeague.isPending || leaveLeague.isPending) ? 'Processing...' : getButtonText()}
|
|
</Button>
|
|
|
|
{error && (
|
|
<Text size="sm" color="text-red-400" mt={2} block>{error}</Text>
|
|
)}
|
|
|
|
{/* Confirmation Dialog */}
|
|
<Modal
|
|
isOpen={showConfirmDialog}
|
|
onOpenChange={setShowConfirmDialog}
|
|
title={dialogAction === 'leave' ? 'Leave League' : dialogAction === 'request' ? 'Request to Join' : 'Join League'}
|
|
primaryActionLabel={(joinLeague.isPending || leaveLeague.isPending) ? 'Processing...' : 'Confirm'}
|
|
onPrimaryAction={dialogAction === 'leave' ? handleLeave : handleJoin}
|
|
secondaryActionLabel="Cancel"
|
|
onSecondaryAction={closeDialog}
|
|
>
|
|
<Box>
|
|
<Text color="text-gray-400" block mb={6}>
|
|
{dialogAction === 'leave'
|
|
? 'Are you sure you want to leave this league? You can rejoin later.'
|
|
: dialogAction === 'request'
|
|
? 'Your join request will be sent to the league admins for approval.'
|
|
: 'Are you sure you want to join this league?'}
|
|
</Text>
|
|
|
|
{error && (
|
|
<Box mb={4} p={3} rounded="md" bg="bg-red-500/10" border borderColor="border-red-500/30">
|
|
<Text size="sm" color="text-red-400">{error}</Text>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
</Modal>
|
|
</Box>
|
|
);
|
|
}
|