Files
gridpilot.gg/apps/website/components/teams/TeamAdmin.tsx
2026-01-20 15:12:28 +01:00

211 lines
6.3 KiB
TypeScript

'use client';
import { JoinRequestItem } from '@/components/leagues/JoinRequestItem';
import { JoinRequestList } from '@/components/leagues/JoinRequestList';
import { EmptyState } from '@/ui/EmptyState';
import { LoadingWrapper } from '@/ui/LoadingWrapper';
import { useApproveJoinRequest } from "@/hooks/team/useApproveJoinRequest";
import { useRejectJoinRequest } from "@/hooks/team/useRejectJoinRequest";
import { useTeamJoinRequests } from "@/hooks/team/useTeamJoinRequests";
import { useUpdateTeam } from "@/hooks/team/useUpdateTeam";
import type { TeamJoinRequestViewModel } from '@/lib/view-models/TeamJoinRequestViewModel';
import { Button } from '@/ui/Button';
import { Panel } from '@/ui/Panel';
import { DangerZone } from '@/ui/DangerZone';
import { Input } from '@/ui/Input';
import { Text } from '@/ui/Text';
import { TextArea } from '@/ui/TextArea';
import { Box } from '@/ui/Box';
import { Stack } from '@/ui/Stack';
import React, { useState } from 'react';
interface TeamAdminProps {
team: {
id: string;
name: string;
tag: string;
description?: string;
ownerId: string;
};
onUpdate: () => void;
}
export function TeamAdmin({ team, onUpdate }: TeamAdminProps) {
const [editMode, setEditMode] = useState(false);
const [editedTeam, setEditedTeam] = useState({
name: team.name,
tag: team.tag,
description: team.description || '',
});
const { data: joinRequests = [], isLoading: loading } = useTeamJoinRequests(
team.id,
team.ownerId,
true
);
const updateTeamMutation = useUpdateTeam({
onSuccess: () => {
setEditMode(false);
onUpdate();
},
onError: (error) => {
alert(error instanceof Error ? error.message : 'Failed to update team');
},
});
const approveJoinRequestMutation = useApproveJoinRequest({
onSuccess: () => {
onUpdate();
},
onError: (error) => {
alert(error instanceof Error ? error.message : 'Failed to approve request');
},
});
const rejectJoinRequestMutation = useRejectJoinRequest({
onSuccess: () => {
onUpdate();
},
onError: (error) => {
alert(error instanceof Error ? error.message : 'Failed to reject request');
},
});
const handleApprove = () => {
approveJoinRequestMutation.mutate();
};
const handleReject = () => {
rejectJoinRequestMutation.mutate();
};
const handleSaveChanges = () => {
updateTeamMutation.mutate({
teamId: team.id,
input: {
name: editedTeam.name,
tag: editedTeam.tag,
description: editedTeam.description,
},
});
};
return (
<Stack gap="lg">
<Panel
variant="default"
padding="md"
>
<Box display="flex" justifyContent="between" alignItems="center" marginBottom={6}>
<Heading level={3} uppercase>Team Settings</Heading>
{!editMode && (
<Button variant="secondary" size="sm" onClick={() => setEditMode(true)}>
Edit Details
</Button>
)}
</Box>
{editMode ? (
<Stack gap="md">
<Input
label="Team Name"
value={editedTeam.name}
onChange={(e) => setEditedTeam({ ...editedTeam, name: e.target.value })}
/>
<Input
label="Team Tag"
value={editedTeam.tag}
onChange={(e) => setEditedTeam({ ...editedTeam, tag: e.target.value })}
maxLength={4}
hint="Max 4 characters"
/>
<TextArea
label="Description"
rows={4}
value={editedTeam.description}
onChange={(e) => setEditedTeam({ ...editedTeam, description: e.target.value })}
/>
<Box display="flex" gap="sm">
<Button variant="primary" onClick={handleSaveChanges} disabled={updateTeamMutation.isPending}>
{updateTeamMutation.isPending ? 'Saving...' : 'Save Changes'}
</Button>
<Button
variant="secondary"
onClick={() => {
setEditMode(false);
setEditedTeam({
name: team.name,
tag: team.tag,
description: team.description || '',
});
}}
>
Cancel
</Button>
</Box>
</Stack>
) : (
<Stack gap="md">
<Stack gap="xs">
<Text size="xs" variant="low" uppercase>Team Name</Text>
<Text weight="bold">{team.name}</Text>
</Stack>
<Stack gap="xs">
<Text size="xs" variant="low" uppercase>Team Tag</Text>
<Text weight="bold">{team.tag}</Text>
</Stack>
<Stack gap="xs">
<Text size="xs" variant="low" uppercase>Description</Text>
<Text variant="med">{team.description}</Text>
</Stack>
</Stack>
)}
</Panel>
<Panel variant="default" padding="md">
<Box marginBottom={6}>
<Heading level={3} uppercase>Join Requests</Heading>
</Box>
{loading ? (
<LoadingWrapper variant="spinner" message="Loading requests..." />
) : joinRequests.length > 0 ? (
<JoinRequestList>
{joinRequests.map((request: TeamJoinRequestViewModel) => (
<JoinRequestItem
key={request.requestId}
driverId={request.driverId}
requestedAt={request.requestedAt}
onApprove={() => handleApprove()}
onReject={() => handleReject()}
isApproving={approveJoinRequestMutation.isPending}
isRejecting={rejectJoinRequestMutation.isPending}
/>
))}
</JoinRequestList>
) : (
<EmptyState
title="No pending join requests"
description="When drivers request to join your team, they will appear here."
variant="minimal"
/>
)}
</Panel>
<DangerZone
title="Disband Team"
description="Permanently delete this team. This action cannot be undone."
>
<Button variant="danger" disabled>
Disband Team (Coming Soon)
</Button>
</DangerZone>
</Stack>
);
}
import { Heading } from '@/ui/Heading';