website refactor

This commit is contained in:
2026-01-14 10:51:05 +01:00
parent 4522d41aef
commit 0d89ad027e
291 changed files with 6887 additions and 3685 deletions

View File

@@ -0,0 +1,165 @@
import { Card } from '@/ui/Card';
import { Section } from '@/ui/Section';
import { Text } from '@/ui/Text';
import { Button } from '@/ui/Button';
import { Select } from '@/ui/Select';
import type { MembershipRole } from '@/lib/types/MembershipRole';
import type { LeagueRosterJoinRequestDTO } from '@/lib/types/generated/LeagueRosterJoinRequestDTO';
import type { LeagueRosterMemberDTO } from '@/lib/types/generated/LeagueRosterMemberDTO';
interface RosterAdminTemplateProps {
joinRequests: LeagueRosterJoinRequestDTO[];
members: LeagueRosterMemberDTO[];
loading: boolean;
pendingCountLabel: string;
onApprove: (requestId: string) => Promise<void>;
onReject: (requestId: string) => Promise<void>;
onRoleChange: (driverId: string, newRole: MembershipRole) => Promise<void>;
onRemove: (driverId: string) => Promise<void>;
roleOptions: MembershipRole[];
}
export function RosterAdminTemplate({
joinRequests,
members,
loading,
pendingCountLabel,
onApprove,
onReject,
onRoleChange,
onRemove,
roleOptions,
}: RosterAdminTemplateProps) {
return (
<Section>
<Card>
<Section>
<Section>
<Text size="2xl" weight="bold" className="text-white">
Roster Admin
</Text>
<Text size="sm" className="text-gray-400">
Manage join requests and member roles.
</Text>
</Section>
<Section>
<div className="flex items-center justify-between gap-3">
<Text size="lg" weight="semibold" className="text-white">
Pending join requests
</Text>
<Text size="xs" className="text-gray-500">
{pendingCountLabel}
</Text>
</div>
{loading ? (
<Text size="sm" className="text-gray-400">
Loading
</Text>
) : joinRequests.length ? (
<div className="space-y-2">
{joinRequests.map((req) => (
<div
key={req.id}
className="flex items-center justify-between gap-3 bg-deep-graphite border border-charcoal-outline rounded p-3"
>
<div className="min-w-0">
<Text weight="medium" className="text-white truncate">
{(req.driver as any)?.name || 'Unknown'}
</Text>
<Text size="xs" className="text-gray-400 truncate">
{req.requestedAt}
</Text>
{req.message && (
<Text size="xs" className="text-gray-500 truncate">
{req.message}
</Text>
)}
</div>
<div className="flex items-center gap-2">
<Button
data-testid={`join-request-${req.id}-approve`}
onClick={() => onApprove(req.id)}
className="bg-primary-blue text-white"
>
Approve
</Button>
<Button
data-testid={`join-request-${req.id}-reject`}
onClick={() => onReject(req.id)}
className="bg-iron-gray text-gray-200"
>
Reject
</Button>
</div>
</div>
))}
</div>
) : (
<Text size="sm" className="text-gray-500">
No pending join requests.
</Text>
)}
</Section>
<Section>
<Text size="lg" weight="semibold" className="text-white">
Members
</Text>
{loading ? (
<Text size="sm" className="text-gray-400">
Loading
</Text>
) : members.length ? (
<div className="space-y-2">
{members.map((member) => (
<div
key={member.driverId}
className="flex flex-col md:flex-row md:items-center md:justify-between gap-3 bg-deep-graphite border border-charcoal-outline rounded p-3"
>
<div className="min-w-0">
<Text weight="medium" className="text-white truncate">
{(member.driver as any)?.name || 'Unknown'}
</Text>
<Text size="xs" className="text-gray-400 truncate">
{member.joinedAt}
</Text>
</div>
<div className="flex flex-col md:flex-row md:items-center gap-2">
<label className="text-xs text-gray-400" htmlFor={`role-${member.driverId}`}>
Role for {(member.driver as any)?.name || 'Unknown'}
</label>
<Select
id={`role-${member.driverId}`}
aria-label={`Role for ${(member.driver as any)?.name || 'Unknown'}`}
value={member.role}
onChange={(e) => onRoleChange(member.driverId, e.target.value as MembershipRole)}
options={roleOptions.map((role) => ({ value: role, label: role }))}
className="bg-iron-gray text-white px-3 py-2 rounded"
/>
<Button
data-testid={`member-${member.driverId}-remove`}
onClick={() => onRemove(member.driverId)}
className="bg-iron-gray text-gray-200"
>
Remove
</Button>
</div>
</div>
))}
</div>
) : (
<Text size="sm" className="text-gray-500">
No members found.
</Text>
)}
</Section>
</Section>
</Card>
</Section>
);
}