111 lines
3.6 KiB
TypeScript
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}
|
|
/>
|
|
);
|
|
}
|