mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
- Implemented ExamStageActions component to manage actions related to exam booking and payment based on application status. - Added mock-base-query for development, providing a partial mock backend for various API endpoints. - Introduced mock-data for simulating responses in the mock-base-query, covering profiles, vessels, applications, licenses, exams, and notifications.
113 lines
3.5 KiB
TypeScript
113 lines
3.5 KiB
TypeScript
import { useState } from 'react';
|
|
import { Alert, Button, Modal, Select, Stack, Text, TextInput } from '@mantine/core';
|
|
import { IconCalendarEvent } from '@tabler/icons-react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { ModalFooter } from '@ema-platform/ui';
|
|
import { useGetExamsQuery } from '../../exam/api/exam-api';
|
|
|
|
interface Props {
|
|
opened: boolean;
|
|
applicantName: string;
|
|
loading: boolean;
|
|
onClose: () => void;
|
|
onConfirm: (payload: {
|
|
examId: string;
|
|
admissionNumber?: string;
|
|
examDate?: string;
|
|
}) => void;
|
|
}
|
|
|
|
/**
|
|
* Places a candidate who has paid the examination fee into an existing sitting.
|
|
*
|
|
* Sessions are picked from the exam calendar rather than typed, because the
|
|
* candidate joins a scheduled sitting — this is an assignment, not the creation
|
|
* of a per-candidate appointment.
|
|
*/
|
|
export function ScheduleExamModal({
|
|
opened,
|
|
applicantName,
|
|
loading,
|
|
onClose,
|
|
onConfirm,
|
|
}: Props) {
|
|
const { t } = useTranslation();
|
|
const { data: exams, isLoading } = useGetExamsQuery(undefined, { skip: !opened });
|
|
const [examId, setExamId] = useState<string | null>(null);
|
|
const [admissionNumber, setAdmissionNumber] = useState('');
|
|
|
|
const options = (exams?.items ?? []).map((exam) => ({
|
|
value: exam.id,
|
|
label: [exam.title?.en ?? exam.title?.am ?? t('exam.untitled', 'Untitled exam'), exam.date]
|
|
.filter(Boolean)
|
|
.join(' — '),
|
|
}));
|
|
const selected = exams?.items?.find((exam) => exam.id === examId);
|
|
|
|
function confirm() {
|
|
if (!examId) return;
|
|
onConfirm({
|
|
examId,
|
|
admissionNumber: admissionNumber.trim() || undefined,
|
|
examDate: selected?.date ? String(selected.date) : undefined,
|
|
});
|
|
}
|
|
|
|
return (
|
|
<Modal
|
|
opened={opened}
|
|
onClose={onClose}
|
|
title={t('review.actions.scheduleExam', 'Schedule exam')}
|
|
>
|
|
<Stack>
|
|
<Text size="sm" c="dimmed">
|
|
{t('review.scheduleExam.intro', {
|
|
defaultValue:
|
|
'Assign {{applicant}} to a scheduled sitting. They will be notified with the date and their admission number.',
|
|
applicant: applicantName,
|
|
})}
|
|
</Text>
|
|
|
|
{!isLoading && options.length === 0 ? (
|
|
<Alert color="orange" icon={<IconCalendarEvent size={16} />}>
|
|
{t(
|
|
'review.scheduleExam.noSessions',
|
|
'No exam sessions exist yet. Create one in the Exams area first.',
|
|
)}
|
|
</Alert>
|
|
) : (
|
|
<Select
|
|
label={t('review.scheduleExam.session', 'Exam session')}
|
|
placeholder={t('review.scheduleExam.pick', 'Choose a sitting')}
|
|
data={options}
|
|
value={examId}
|
|
onChange={setExamId}
|
|
disabled={isLoading}
|
|
searchable
|
|
withAsterisk
|
|
/>
|
|
)}
|
|
|
|
<TextInput
|
|
label={t('review.scheduleExam.admissionNumber', 'Admission number')}
|
|
description={t(
|
|
'review.scheduleExam.admissionHint',
|
|
'Leave blank to let the system issue one.',
|
|
)}
|
|
value={admissionNumber}
|
|
onChange={(e) => setAdmissionNumber(e.currentTarget.value)}
|
|
/>
|
|
|
|
<ModalFooter>
|
|
<Button variant="default" onClick={onClose}>
|
|
{t('common.cancel', 'Cancel')}
|
|
</Button>
|
|
<Button loading={loading} disabled={!examId} onClick={confirm}>
|
|
{t('review.scheduleExam.confirm', 'Schedule')}
|
|
</Button>
|
|
</ModalFooter>
|
|
</Stack>
|
|
</Modal>
|
|
);
|
|
}
|