website refactor
This commit is contained in:
@@ -1,15 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
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 { Box } from '@/ui/Box';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Surface } from '@/ui/Surface';
|
||||
import type { MembershipRole } from '@/lib/types/MembershipRole';
|
||||
import type { LeagueRosterJoinRequestDTO } from '@/lib/types/generated/LeagueRosterJoinRequestDTO';
|
||||
import type { LeagueRosterMemberDTO } from '@/lib/types/generated/LeagueRosterMemberDTO';
|
||||
import type { LeagueRosterAdminViewData } from '@/lib/view-data/LeagueRosterAdminViewData';
|
||||
|
||||
interface RosterAdminTemplateProps {
|
||||
joinRequests: LeagueRosterJoinRequestDTO[];
|
||||
members: LeagueRosterMemberDTO[];
|
||||
viewData: LeagueRosterAdminViewData;
|
||||
loading: boolean;
|
||||
pendingCountLabel: string;
|
||||
onApprove: (requestId: string) => Promise<void>;
|
||||
@@ -20,8 +24,7 @@ interface RosterAdminTemplateProps {
|
||||
}
|
||||
|
||||
export function RosterAdminTemplate({
|
||||
joinRequests,
|
||||
members,
|
||||
viewData,
|
||||
loading,
|
||||
pendingCountLabel,
|
||||
onApprove,
|
||||
@@ -30,136 +33,122 @@ export function RosterAdminTemplate({
|
||||
onRemove,
|
||||
roleOptions,
|
||||
}: RosterAdminTemplateProps) {
|
||||
const { joinRequests, members } = viewData;
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<Stack gap={6}>
|
||||
<Card>
|
||||
<Section>
|
||||
<Section>
|
||||
<Text size="2xl" weight="bold" className="text-white">
|
||||
Roster Admin
|
||||
</Text>
|
||||
<Text size="sm" className="text-gray-400">
|
||||
<Stack gap={6}>
|
||||
<Box>
|
||||
<Heading level={1}>Roster Admin</Heading>
|
||||
<Text size="sm" color="text-gray-400" block mt={1}>
|
||||
Manage join requests and member roles.
|
||||
</Text>
|
||||
</Section>
|
||||
</Box>
|
||||
|
||||
<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">
|
||||
<Box>
|
||||
<Stack direction="row" align="center" justify="between" mb={4}>
|
||||
<Heading level={2}>Pending join requests</Heading>
|
||||
<Text size="xs" color="text-gray-500">
|
||||
{pendingCountLabel}
|
||||
</Text>
|
||||
</div>
|
||||
</Stack>
|
||||
|
||||
{loading ? (
|
||||
<Text size="sm" className="text-gray-400">
|
||||
Loading…
|
||||
</Text>
|
||||
) : joinRequests.length ? (
|
||||
<div className="space-y-2">
|
||||
<Text size="sm" color="text-gray-400">Loading…</Text>
|
||||
) : joinRequests.length > 0 ? (
|
||||
<Stack gap={3}>
|
||||
{joinRequests.map((req) => (
|
||||
<div
|
||||
<Surface
|
||||
key={req.id}
|
||||
className="flex items-center justify-between gap-3 bg-deep-graphite border border-charcoal-outline rounded p-3"
|
||||
variant="muted"
|
||||
rounded="lg"
|
||||
border
|
||||
padding={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>
|
||||
<Stack direction="row" align="center" justify="between">
|
||||
<Box>
|
||||
<Text weight="medium" color="text-white" block>{req.driver.name}</Text>
|
||||
<Text size="xs" color="text-gray-400" block mt={1}>{req.requestedAt}</Text>
|
||||
{req.message && (
|
||||
<Text size="xs" color="text-gray-500" block mt={1} truncate>{req.message}</Text>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<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>
|
||||
<Stack direction="row" gap={2}>
|
||||
<Button
|
||||
onClick={() => onApprove(req.id)}
|
||||
variant="primary"
|
||||
size="sm"
|
||||
>
|
||||
Approve
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => onReject(req.id)}
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Surface>
|
||||
))}
|
||||
</div>
|
||||
</Stack>
|
||||
) : (
|
||||
<Text size="sm" className="text-gray-500">
|
||||
No pending join requests.
|
||||
</Text>
|
||||
<Text size="sm" color="text-gray-500">No pending join requests.</Text>
|
||||
)}
|
||||
</Section>
|
||||
</Box>
|
||||
|
||||
<Section>
|
||||
<Text size="lg" weight="semibold" className="text-white">
|
||||
Members
|
||||
</Text>
|
||||
<Box pt={6} style={{ borderTop: '1px solid #262626' }}>
|
||||
<Box mb={4}>
|
||||
<Heading level={2}>Members</Heading>
|
||||
</Box>
|
||||
|
||||
{loading ? (
|
||||
<Text size="sm" className="text-gray-400">
|
||||
Loading…
|
||||
</Text>
|
||||
) : members.length ? (
|
||||
<div className="space-y-2">
|
||||
<Text size="sm" color="text-gray-400">Loading…</Text>
|
||||
) : members.length > 0 ? (
|
||||
<Stack gap={3}>
|
||||
{members.map((member) => (
|
||||
<div
|
||||
<Surface
|
||||
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"
|
||||
variant="muted"
|
||||
rounded="lg"
|
||||
border
|
||||
padding={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>
|
||||
<Stack direction="row" align="center" justify="between" wrap gap={4}>
|
||||
<Box>
|
||||
<Text weight="medium" color="text-white" block>{member.driver.name}</Text>
|
||||
<Text size="xs" color="text-gray-400" block mt={1}>{member.joinedAt}</Text>
|
||||
</Box>
|
||||
|
||||
<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>
|
||||
<Stack direction="row" align="center" gap={3}>
|
||||
<Box>
|
||||
<Select
|
||||
value={member.role}
|
||||
onChange={(e) => onRoleChange(member.driverId, e.target.value as MembershipRole)}
|
||||
options={roleOptions.map((role) => ({ value: role, label: role }))}
|
||||
/>
|
||||
</Box>
|
||||
<Button
|
||||
onClick={() => onRemove(member.driverId)}
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Surface>
|
||||
))}
|
||||
</div>
|
||||
</Stack>
|
||||
) : (
|
||||
<Text size="sm" className="text-gray-500">
|
||||
No members found.
|
||||
</Text>
|
||||
<Text size="sm" color="text-gray-500">No members found.</Text>
|
||||
)}
|
||||
</Section>
|
||||
</Section>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Card>
|
||||
</Section>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user