mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-29 14:08:12 +00:00
Two follow-ups on the dropdown-actions pass: - All four action menus (exams, results, questions, candidates panel) now trigger off a plain three-dot icon button instead of a text 'Actions' button with a chevron. - Exam status change no longer lists all six statuses flat in the dropdown (bad UX, looked like six always-visible options). 'Status' is now one menu item that opens a small picker modal (current status + a Select + Update) rather than the full edit form. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
133 lines
4.0 KiB
TypeScript
133 lines
4.0 KiB
TypeScript
import { ActionIcon, Badge, Menu, Text } from '@mantine/core';
|
|
import { IconDotsVertical, IconRefresh, IconUserCheck } from '@tabler/icons-react';
|
|
import type { TFunction } from 'i18next';
|
|
import type { AdvancedColumn } from '@ema-platform/ui';
|
|
import { LICENSE_PERMISSIONS, RequirePermission } from '@ema-platform/auth';
|
|
import type { AttendanceStatus, ExamRegistration } from '../../types/exam';
|
|
|
|
const ATTENDANCE_COLOR: Record<AttendanceStatus, string> = {
|
|
REGISTERED: 'gray',
|
|
PRESENT: 'teal',
|
|
LATE: 'yellow',
|
|
ABSENT: 'red',
|
|
WITHDRAWN: 'orange',
|
|
DISQUALIFIED: 'red',
|
|
};
|
|
|
|
export const candidateName = (registration: ExamRegistration) =>
|
|
[
|
|
registration.profile?.firstName,
|
|
registration.profile?.middleName,
|
|
registration.profile?.lastName,
|
|
]
|
|
.filter(Boolean)
|
|
.join(' ') || registration.profileId.slice(0, 8);
|
|
|
|
export function examCandidateColumns(
|
|
t: TFunction,
|
|
handlers: {
|
|
onRecord: (registration: ExamRegistration) => void;
|
|
onRegrade: (registration: ExamRegistration) => void;
|
|
regrading?: string | null;
|
|
},
|
|
): AdvancedColumn<ExamRegistration>[] {
|
|
return [
|
|
{
|
|
header: t('exam.candidates.admission'),
|
|
cell: ({ row }) => (
|
|
<Text fz="sm" ff="monospace" fw={600}>
|
|
{row.original.admissionNumber}
|
|
</Text>
|
|
),
|
|
},
|
|
{
|
|
header: t('exam.candidates.name'),
|
|
cell: ({ row }) => <Text fz="sm">{candidateName(row.original)}</Text>,
|
|
},
|
|
{
|
|
header: t('exam.candidates.attempt'),
|
|
cell: ({ row }) => (
|
|
<Badge
|
|
size="sm"
|
|
variant="light"
|
|
color={row.original.kind === 'RETAKE' ? 'orange' : 'blue'}
|
|
>
|
|
{row.original.kind === 'RETAKE'
|
|
? t('exam.candidates.retake', { n: row.original.attemptNumber })
|
|
: t('exam.candidates.firstSitting')}
|
|
</Badge>
|
|
),
|
|
},
|
|
{
|
|
header: t('exam.candidates.attendance'),
|
|
cell: ({ row }) => (
|
|
<Badge
|
|
size="sm"
|
|
variant="light"
|
|
color={ATTENDANCE_COLOR[row.original.attendanceStatus] ?? 'gray'}
|
|
>
|
|
{t(`exam.attendance.${row.original.attendanceStatus}`)}
|
|
</Badge>
|
|
),
|
|
},
|
|
{
|
|
header: t('exam.candidates.remark'),
|
|
cell: ({ row }) => (
|
|
<Text fz="xs" c="dimmed" maw={220} lineClamp={2}>
|
|
{row.original.attendanceRemark ?? '—'}
|
|
</Text>
|
|
),
|
|
},
|
|
{
|
|
header: '',
|
|
label: t('exam.candidates.record'),
|
|
align: 'right',
|
|
cell: ({ row }) => {
|
|
const attemptStatus = row.original.attempt?.status;
|
|
const canRegrade = attemptStatus === 'SUBMITTED' || attemptStatus === 'EXPIRED';
|
|
return (
|
|
<Menu shadow="md" width={180} position="bottom-end">
|
|
<Menu.Target>
|
|
<ActionIcon
|
|
variant="subtle"
|
|
color="gray"
|
|
size="sm"
|
|
loading={handlers.regrading === row.original.attempt?.id}
|
|
>
|
|
<IconDotsVertical size={16} />
|
|
</ActionIcon>
|
|
</Menu.Target>
|
|
<Menu.Dropdown>
|
|
<RequirePermission
|
|
anyOf={[LICENSE_PERMISSIONS.RECORD_EXAM_ATTENDANCE]}
|
|
hideOnly
|
|
>
|
|
<Menu.Item
|
|
leftSection={<IconUserCheck size={14} />}
|
|
onClick={() => handlers.onRecord(row.original)}
|
|
>
|
|
{t('exam.candidates.record')}
|
|
</Menu.Item>
|
|
</RequirePermission>
|
|
{canRegrade && (
|
|
<RequirePermission
|
|
anyOf={[LICENSE_PERMISSIONS.RECORD_EXAM_RESULT]}
|
|
hideOnly
|
|
>
|
|
<Menu.Item
|
|
color="grape"
|
|
leftSection={<IconRefresh size={14} />}
|
|
onClick={() => handlers.onRegrade(row.original)}
|
|
>
|
|
{t('exam.candidates.regrade')}
|
|
</Menu.Item>
|
|
</RequirePermission>
|
|
)}
|
|
</Menu.Dropdown>
|
|
</Menu>
|
|
);
|
|
},
|
|
},
|
|
];
|
|
}
|