Files
gridpilot.gg/apps/website/client-wrapper/RosterAdminPage.tsx
2026-01-19 14:07:49 +01:00

111 lines
3.6 KiB
TypeScript

'use client';
import type { MembershipRole } from '@/lib/types/MembershipRole';
import { useParams } from 'next/navigation';
import { useMemo } from 'react';
import {
useLeagueJoinRequests,
useLeagueRosterAdmin,
useApproveJoinRequest,
useRejectJoinRequest,
useUpdateMemberRole,
useRemoveMember,
} from "@/hooks/league/useLeagueRosterAdmin";
import { RosterAdminTemplate } from '@/templates/RosterAdminTemplate';
import type { JoinRequestData, RosterMemberData, LeagueRosterAdminViewData } from '@/lib/view-data/LeagueRosterAdminViewData';
import type { LeagueRosterJoinRequestDTO } from '@/lib/types/generated/LeagueRosterJoinRequestDTO';
import type { LeagueRosterMemberDTO } from '@/lib/types/generated/LeagueRosterMemberDTO';
import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts';
import { DateDisplay } from '@/lib/display-objects/DateDisplay';
const ROLE_OPTIONS: MembershipRole[] = ['owner', 'admin', 'steward', 'member'];
export function RosterAdminPage({ viewData: initialViewData }: Partial<ClientWrapperProps<LeagueRosterAdminViewData>>) {
const params = useParams();
const leagueId = params.id as string;
// Fetch data using React-Query + DI
const {
data: joinRequests = [],
isLoading: loadingJoinRequests,
refetch: refetchJoinRequests,
} = useLeagueJoinRequests(leagueId);
const {
data: members = [],
isLoading: loadingMembers,
refetch: refetchMembers,
} = useLeagueRosterAdmin(leagueId);
const loading = loadingJoinRequests || loadingMembers;
// Mutations
const approveMutation = useApproveJoinRequest({
onSuccess: () => refetchJoinRequests(),
});
const rejectMutation = useRejectJoinRequest({
onSuccess: () => refetchJoinRequests(),
});
const updateRoleMutation = useUpdateMemberRole({
onError: () => refetchMembers(), // Refetch on error to restore state
});
const removeMemberMutation = useRemoveMember({
onSuccess: () => refetchMembers(),
});
const pendingCountLabel = useMemo(() => {
return joinRequests.length === 1 ? '1 request' : `${joinRequests.length} requests`;
}, [joinRequests.length]);
const handleApprove = async (requestId: string) => {
await approveMutation.mutateAsync({ leagueId, requestId });
};
const handleReject = async (requestId: string) => {
await rejectMutation.mutateAsync({ leagueId, requestId });
};
const handleRoleChange = async (driverId: string, newRole: MembershipRole) => {
await updateRoleMutation.mutateAsync({ leagueId, driverId, newRole });
};
const handleRemove = async (driverId: string) => {
await removeMemberMutation.mutateAsync({ leagueId, driverId });
};
const viewData = useMemo(() => ({
leagueId,
joinRequests: joinRequests.map((req: LeagueRosterJoinRequestDTO): JoinRequestData => ({
id: req.id,
driver: req.driver as { id: string; name: string },
requestedAt: req.requestedAt,
formattedRequestedAt: DateDisplay.formatShort(req.requestedAt),
message: req.message || undefined,
})),
members: members.map((m: LeagueRosterMemberDTO): RosterMemberData => ({
driverId: m.driverId,
driver: m.driver as { id: string; name: string },
role: m.role,
joinedAt: m.joinedAt,
formattedJoinedAt: DateDisplay.formatShort(m.joinedAt),
})),
}), [leagueId, joinRequests, members]);
return (
<RosterAdminTemplate
viewData={viewData}
loading={loading}
pendingCountLabel={pendingCountLabel}
onApprove={handleApprove}
onReject={handleReject}
onRoleChange={handleRoleChange}
onRemove={handleRemove}
roleOptions={ROLE_OPTIONS}
/>
);
}