wip
This commit is contained in:
160
apps/website/components/leagues/JoinLeagueButton.tsx
Normal file
160
apps/website/components/leagues/JoinLeagueButton.tsx
Normal file
@@ -0,0 +1,160 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import Button from '../ui/Button';
|
||||
import {
|
||||
getMembership,
|
||||
joinLeague,
|
||||
leaveLeague,
|
||||
requestToJoin,
|
||||
getCurrentDriverId,
|
||||
type MembershipStatus,
|
||||
} from '@gridpilot/racing/application';
|
||||
|
||||
interface JoinLeagueButtonProps {
|
||||
leagueId: string;
|
||||
isInviteOnly?: boolean;
|
||||
onMembershipChange?: () => void;
|
||||
}
|
||||
|
||||
export default function JoinLeagueButton({
|
||||
leagueId,
|
||||
isInviteOnly = false,
|
||||
onMembershipChange,
|
||||
}: JoinLeagueButtonProps) {
|
||||
const currentDriverId = getCurrentDriverId();
|
||||
const membership = getMembership(leagueId, currentDriverId);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
||||
const [dialogAction, setDialogAction] = useState<'join' | 'leave' | 'request'>('join');
|
||||
|
||||
const handleJoin = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
if (isInviteOnly) {
|
||||
requestToJoin(leagueId, currentDriverId);
|
||||
} else {
|
||||
joinLeague(leagueId, currentDriverId);
|
||||
}
|
||||
onMembershipChange?.();
|
||||
setShowConfirmDialog(false);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to join league');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLeave = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
leaveLeague(leagueId, currentDriverId);
|
||||
onMembershipChange?.();
|
||||
setShowConfirmDialog(false);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to leave league');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
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' || loading;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
variant={getButtonVariant()}
|
||||
onClick={() => {
|
||||
if (membership) {
|
||||
openDialog('leave');
|
||||
} else {
|
||||
openDialog(isInviteOnly ? 'request' : 'join');
|
||||
}
|
||||
}}
|
||||
disabled={isDisabled}
|
||||
className="w-full"
|
||||
>
|
||||
{loading ? 'Processing...' : getButtonText()}
|
||||
</Button>
|
||||
|
||||
{error && (
|
||||
<p className="mt-2 text-sm text-red-400">{error}</p>
|
||||
)}
|
||||
|
||||
{/* Confirmation Dialog */}
|
||||
{showConfirmDialog && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-iron-gray border border-charcoal-outline rounded-lg max-w-md w-full p-6">
|
||||
<h3 className="text-xl font-semibold text-white mb-4">
|
||||
{dialogAction === 'leave' ? 'Leave League' : dialogAction === 'request' ? 'Request to Join' : 'Join League'}
|
||||
</h3>
|
||||
|
||||
<p className="text-gray-400 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?'}
|
||||
</p>
|
||||
|
||||
{error && (
|
||||
<div className="mb-4 p-3 rounded bg-red-500/10 border border-red-500/30 text-red-400 text-sm">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
variant={dialogAction === 'leave' ? 'danger' : 'primary'}
|
||||
onClick={dialogAction === 'leave' ? handleLeave : handleJoin}
|
||||
disabled={loading}
|
||||
className="flex-1"
|
||||
>
|
||||
{loading ? 'Processing...' : 'Confirm'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={closeDialog}
|
||||
disabled={loading}
|
||||
className="flex-1"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user