Skip to content

Commit

Permalink
VKT(Frontend & Backend): Examiner exam event overview page fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
jrkkp committed Nov 14, 2024
1 parent 8c5d632 commit 798579e
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 144 deletions.
28 changes: 28 additions & 0 deletions backend/vkt/db/4_init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -446,3 +446,31 @@ VALUES (1, 1, 1,
'[email protected]', '0404040404', null, null, null, null,
'Teppo', 'Testinen',
'922c2089-83a8-4163-8180-d8b675ff5337', NOW() + INTERVAL '3 days', NOW());

-- Insert enrollment appointment
INSERT INTO enrollment_appointment(person_id, examiner_id, examiner_exam_event_id,
skill_oral, skill_textual, skill_understanding,
partial_exam_speaking, partial_exam_speech_comprehension, partial_exam_writing, partial_exam_reading_comprehension,
status, digital_certificate_consent, email, phone_number, street, postal_code, town, country, first_name, last_name,
auth_hash, auth_hash_expires, auth_hash_sent)
VALUES (2, 1, 1,
false, true, false,
false, false, true, true,
'COMPLETED', false,
'[email protected]', '0401234504', null, null, null, null,
'Anneli', 'Annikkinen',
'123c2089-83a8-4163-8180-d8b675ff5337', NOW() - INTERVAL '3 days', NOW() - INTERVAL '6 days');

-- Insert enrollment appointment
INSERT INTO enrollment_appointment(person_id, examiner_id, examiner_exam_event_id,
skill_oral, skill_textual, skill_understanding,
partial_exam_speaking, partial_exam_speech_comprehension, partial_exam_writing, partial_exam_reading_comprehension,
status, digital_certificate_consent, email, phone_number, street, postal_code, town, country, first_name, last_name,
auth_hash, auth_hash_expires, auth_hash_sent)
VALUES (3, 1, 1,
true, true, true,
true, true, true, true,
'CANCELED', false,
'[email protected]', '0501234504', null, null, null, null,
'Marja-Liisa', 'Testaaja',
'233c2129-83a8-4163-8180-d8b675ff5337', NOW() + INTERVAL '3 days', NOW());
1 change: 1 addition & 0 deletions frontend/packages/vkt/public/i18n/fi-FI/clerk.json
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
"CANCELED": "Peruutetut",
"CANCELED_UNFINISHED_ENROLLMENT": "Keskeytetyt (keskeneräinen)",
"EXPECTING_PAYMENT_UNFINISHED_ENROLLMENT": "Maksua odottavat (keskeneräinen)",
"WAITING_AUTHENTICATION": "Odottaa tunnistautumista",
"COMPLETED": "Ilmoittautuneet",
"QUEUED": "Jonoon ilmoittautuneet",
"AWAITING_APPROVAL": "Odottaa tarkastusta",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
import { Dayjs } from 'dayjs';
import { ChangeEvent, FC, useCallback, useState } from 'react';
import { H2 } from 'shared/components';
import { Severity, Variant } from 'shared/enums';
import { useDialog } from 'shared/hooks';
import EditIcon from '@mui/icons-material/Edit';
import { FC } from 'react';
import { CustomButton, H2, H3, Text } from 'shared/components';
import { Color, Variant } from 'shared/enums';

import { ClerkExamEventDetailsFields } from 'components/clerkExamEvent/overview/ClerkExamEventDetailsFields';
import { ControlButtons } from 'components/clerkExamEvent/overview/ControlButtons';
import { ExaminerEnrollmentListing } from 'components/examinerEnrollment/listing/ExaminerEnrollmentListing';
import { useClerkTranslation, useCommonTranslation } from 'configs/i18n';
import { useAppDispatch, useAppSelector } from 'configs/redux';
import { EnrollmentAppointmentStatus, UIMode } from 'enums/app';
import { useNavigationProtection } from 'hooks/useNavigationProtection';
import { ClerkEnrollmentAppointment } from 'interfaces/clerkEnrollment';
import {
ClerkExamEvent,
ClerkExamEventBasicInformation,
} from 'interfaces/clerkExamEvent';
import {
resetClerkExamEventDetailsUpdate,
updateClerkExamEventDetails,
} from 'redux/reducers/clerkExamEventOverview';
useClerkTranslation,
useCommonTranslation,
useKoodistoMunicipalitiesTranslation,
} from 'configs/i18n';
import { useAppSelector } from 'configs/redux';
import { EnrollmentAppointmentStatus } from 'enums/app';
import { ClerkEnrollmentAppointment } from 'interfaces/clerkEnrollment';
import { examinerExamEventOverviewSelector } from 'redux/selectors/examinerExamEventOverview';
import { ExamCreateEventUtils } from 'utils/examCreateEvent';
import { DateTimeUtils } from 'utils/dateTime';

interface EnrollmentListProps {
enrollments: Array<ClerkEnrollmentAppointment>;
Expand Down Expand Up @@ -67,168 +59,94 @@ const EnrollmentList: FC<EnrollmentListProps> = ({

export const ExaminerExamEventDetails = () => {
// Redux
const dispatch = useAppDispatch();
const { examEvent } = useAppSelector(examinerExamEventOverviewSelector);

const { showDialog } = useDialog();

// Local state
const [examEventDetails, setExamEventDetails] = useState(examEvent);
const [hasLocalChanges, setHasLocalChanges] = useState(false);
const [currentUIMode, setCurrentUIMode] = useState(UIMode.View);
const isViewMode = currentUIMode === UIMode.View;

const resetLocalExamEventDetails = useCallback(() => {
setExamEventDetails(examEvent);
}, [examEvent]);
const translateCommon = useCommonTranslation();
const translateMunicipality = useKoodistoMunicipalitiesTranslation();

const resetToInitialState = useCallback(() => {
dispatch(resetClerkExamEventDetailsUpdate());
resetLocalExamEventDetails();
setHasLocalChanges(false);
setCurrentUIMode(UIMode.View);
}, [dispatch, resetLocalExamEventDetails]);

useNavigationProtection(hasLocalChanges);

if (!examEventDetails) {
if (!examEvent) {
return null;
}

const { enrollments } = examEventDetails;

const handleComboBoxChange =
(field: keyof ClerkExamEventBasicInformation) => (value?: string) => {
handleFieldChange(field, value);
};

const handleCheckBoxChange =
(field: keyof ClerkExamEventBasicInformation) =>
(_event: ChangeEvent<HTMLInputElement>, checked: boolean) => {
handleFieldChange(field, checked);
};

const handleDateChange =
(
field: keyof Pick<
ClerkExamEventBasicInformation,
'date' | 'registrationCloses' | 'registrationOpens'
>,
) =>
(date: Dayjs | null) => {
handleFieldChange(field, date);
};

const handleMaxParticipantsChange = (
event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
handleFieldChange('maxParticipants', event.target.value);
};

const handleFieldChange = (
field: keyof ClerkExamEventBasicInformation,
fieldValue: string | number | boolean | undefined | Dayjs | null,
) => {
const updatedExamEventDetails = {
...examEventDetails,
[field]: fieldValue,
};
setHasLocalChanges(true);
setExamEventDetails(updatedExamEventDetails as ClerkExamEvent);
};

const onSave = () => {
dispatch(updateClerkExamEventDetails(examEventDetails as ClerkExamEvent));
};
const { enrollments } = examEvent;

const onEdit = () => {
resetLocalExamEventDetails();
setCurrentUIMode(UIMode.Edit);
};

const openCancelDialog = () => {
showDialog({
title: translateCommon('cancelUpdateDialog.header'),
severity: Severity.Info,
description: translateCommon('cancelUpdateDialog.description'),
actions: [
{
title: translateCommon('back'),
variant: Variant.Outlined,
},
{
title: translateCommon('yes'),
variant: Variant.Contained,
action: () => resetToInitialState(),
},
],
});
};

const onCancel = () => {
if (!hasLocalChanges) {
resetToInitialState();
} else {
openCancelDialog();
}
// TODO navigate to edit view
};

return (
<>
<div className="columns margin-top-lg flex-end">
<ControlButtons
onCancel={onCancel}
onEdit={onEdit}
onSave={onSave}
isViewMode={isViewMode}
isValidExamEvent={
examEventDetails &&
ExamCreateEventUtils.isValidExamEvent(examEventDetails)
}
/>
<CustomButton
data-testid="clerk-exam-event-overview__exam-event-details__edit-button"
variant={Variant.Contained}
color={Color.Secondary}
startIcon={<EditIcon />}
onClick={onEdit}
>
{translateCommon('edit')}
</CustomButton>
</div>
<div className="clerk-homepage__exam-events clerk-homepage-create-exam-events">
<ClerkExamEventDetailsFields
examEvent={examEventDetails}
onComboBoxChange={handleComboBoxChange}
onDateChange={handleDateChange}
onCheckBoxChange={handleCheckBoxChange}
onMaxParticipantsChange={handleMaxParticipantsChange}
editDisabled={isViewMode}
/>
<div className="rows">
<div className="grid-3-columns gapped">
<div className="rows grow gapped-sm margin-top-lg">
<H3>Kieli ja taso</H3>
<Text>{translateCommon(`examLanguage.${examEvent.language}`)}</Text>
</div>
<div className="rows grow gapped-sm margin-top-lg">
<H3>Tutkintopäivä</H3>
<Text>{DateTimeUtils.renderDateTime(examEvent.date)}</Text>
</div>
<div className="rows grow gapped-sm margin-top-lg">
<H3>Tutkintopaikka</H3>
<Text>{translateMunicipality(examEvent.municipality.code)}</Text>
</div>
<div className="rows grow gapped-sm margin-top-lg">
<H3>Osoitetiedot</H3>
<Text>{examEvent.location}</Text>
</div>
<div className="rows grow gapped-sm margin-top-lg">
<H3>Paikkojen lukumäärä</H3>
<Text>{examEvent.maxParticipants}</Text>
</div>
<div className="rows grow gapped-sm margin-top-lg">
<H3>Ilmoittautuminen sulkeutuu</H3>
<Text>
{DateTimeUtils.renderDateTime(examEvent.registrationCloses)}
</Text>
</div>
</div>
</div>
<EnrollmentList
enrollments={enrollments}
status={EnrollmentAppointmentStatus.COMPLETED}
examEventId={examEventDetails.id}
examEventId={examEvent.id}
/>
<EnrollmentList
enrollments={enrollments}
status={EnrollmentAppointmentStatus.AWAITING_PAYMENT}
examEventId={examEventDetails.id}
examEventId={examEvent.id}
/>
<EnrollmentList
enrollments={enrollments}
status={
EnrollmentAppointmentStatus.EXPECTING_PAYMENT_UNFINISHED_ENROLLMENT
}
examEventId={examEventDetails.id}
examEventId={examEvent.id}
/>
<EnrollmentList
enrollments={enrollments}
status={EnrollmentAppointmentStatus.WAITING_AUTHENTICATION}
examEventId={examEventDetails.id}
examEventId={examEvent.id}
/>
<EnrollmentList
enrollments={enrollments}
status={EnrollmentAppointmentStatus.CANCELED}
examEventId={examEventDetails.id}
examEventId={examEvent.id}
/>
<EnrollmentList
enrollments={enrollments}
status={EnrollmentAppointmentStatus.CANCELED_UNFINISHED_ENROLLMENT}
examEventId={examEventDetails.id}
examEventId={examEvent.id}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ExaminerExamEventDetails } from 'components/examinerExamEvent/overview/
import { ClerkExamEventOverviewPageSkeleton } from 'components/skeletons/ClerkExamEventOverviewPageSkeleton';
import { useClerkTranslation, useCommonTranslation } from 'configs/i18n';
import { useAppDispatch, useAppSelector } from 'configs/redux';
import { AppRoutes } from 'enums/app';
import { AppRoutes, ExamLevel } from 'enums/app';
import { loadExaminerExamEventOverview } from 'redux/reducers/examinerExamEventOverview';
import { examinerExamEventOverviewSelector } from 'redux/selectors/examinerExamEventOverview';
import { ExamEventUtils } from 'utils/examEvent';
Expand Down Expand Up @@ -77,7 +77,7 @@ export const ExaminerExamEventOverviewPage: FC = () => {
const pageHeader = examEvent
? `${ExamEventUtils.languageAndLevelText(
examEvent.language,
examEvent.level,
ExamLevel.GOOD_AND_SATISFACTORY,
translateCommon,
)} ${DateUtils.formatOptionalDate(examEvent.date)}`
: '';
Expand Down

0 comments on commit 798579e

Please sign in to comment.