From bb20fb46e4858c6efb5837775babc0b3fbb45c7e Mon Sep 17 00:00:00 2001 From: Jarkko Pesonen <435495+jrkkp@users.noreply.github.com> Date: Tue, 29 Oct 2024 23:25:28 +0200 Subject: [PATCH] VKT(Frontend & Backend): Enrollment appointment clerk interface --- .../api/clerk/ClerkEnrollmentController.java | 6 ++ .../ClerkEnrollmentAppointmentDetails.tsx | 53 +++-------- ...lerkEnrollmentAppointmentDetailsFields.tsx | 94 +++++++------------ .../vkt/src/interfaces/clerkEnrollment.ts | 3 +- ...ClerkEnrollmentAppointmentOverviewPage.tsx | 1 - .../reducers/clerkEnrollmentAppointment.ts | 23 +++++ .../redux/sagas/clerkEnrollmentAppointment.ts | 43 ++++++++- .../packages/vkt/src/utils/serialization.ts | 10 ++ 8 files changed, 128 insertions(+), 105 deletions(-) diff --git a/backend/vkt/src/main/java/fi/oph/vkt/api/clerk/ClerkEnrollmentController.java b/backend/vkt/src/main/java/fi/oph/vkt/api/clerk/ClerkEnrollmentController.java index ea8e7f30b..c0d99fe88 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/api/clerk/ClerkEnrollmentController.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/api/clerk/ClerkEnrollmentController.java @@ -95,6 +95,12 @@ public ClerkEnrollmentAppointmentDTO getEnrollmentAppointment(@PathVariable fina return clerkEnrollmentService.getEnrollmentAppointment(enrollmentAppointmentId); } + @PutMapping(path = "/appointment/{enrollmentAppointmentId:\\d+}") + @Operation(tags = TAG_ENROLLMENT, summary = "Update enrollment appointment") + public ClerkEnrollmentDTO updateEnrollmentAppointment(@RequestBody @Valid final ClerkEnrollmentUpdateDTO dto) { + return clerkEnrollmentService.update(dto); + } + @GetMapping(path = "/attachment", consumes = ALL_VALUE) @Operation(tags = TAG_ENROLLMENT, summary = "Download enrollment attachment") public void attachmentRedirect(final HttpServletResponse response, @RequestParam final String key) diff --git a/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetails.tsx b/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetails.tsx index 3d68b56df..1437f2a1b 100644 --- a/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetails.tsx +++ b/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetails.tsx @@ -1,12 +1,11 @@ import { ChangeEvent, useCallback, useEffect, useState } from 'react'; -import { CustomButton, CustomModal } from 'shared/components'; +import { CustomButton } from 'shared/components'; import { APIResponseStatus, Color, Severity, Variant } from 'shared/enums'; import { useDialog, useToast } from 'shared/hooks'; import { StringUtils } from 'shared/utils'; import { ClerkEnrollmentAppointmentDetailsFields } from 'components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetailsFields'; import { ControlButtons } from 'components/clerkEnrollment/overview/ControlButtons'; -import { MoveModal } from 'components/clerkEnrollment/overview/MoveModal'; import { useClerkTranslation, useCommonTranslation } from 'configs/i18n'; import { useAppDispatch, useAppSelector } from 'configs/redux'; import { EnrollmentStatus, UIMode } from 'enums/app'; @@ -16,8 +15,8 @@ import { ClerkEnrollmentAppointment } from 'interfaces/clerkEnrollment'; import { PartialExamsAndSkills } from 'interfaces/common/enrollment'; import { resetClerkEnrollmentDetailsUpdate, - updateClerkEnrollmentDetails, -} from 'redux/reducers/clerkEnrollmentDetails'; + updateClerkEnrollmentAppointment, +} from 'redux/reducers/clerkEnrollmentAppointment'; import { changeClerkEnrollmentStatus, resetClerkEnrollmentStatusChange, @@ -47,14 +46,10 @@ export const ClerkEnrollmentAppointmentDetails = ({ const [enrollmentDetails, setEnrollmentDetails] = useState< ClerkEnrollmentAppointment | undefined >(enrollment); - const [isMoveModalOpen, setIsOpenModalOpen] = useState(false); const [hasLocalChanges, setHasLocalChanges] = useState(false); const [currentUIMode, setCurrentUIMode] = useState(UIMode.View); const isViewMode = currentUIMode === UIMode.View; - const handleMoveButtonCLick = () => setIsOpenModalOpen(true); - const closeMoveModal = () => setIsOpenModalOpen(false); - const resetLocalEnrollmentDetails = useCallback(() => { setEnrollmentDetails(enrollment); }, [enrollment]); @@ -140,43 +135,30 @@ export const ClerkEnrollmentAppointmentDetails = ({ return undefined; } - let updatedEnrollmentDetails; - if ( - field === ClerkEnrollmentTextFieldEnum.FirstName || - field === ClerkEnrollmentTextFieldEnum.LastName - ) { - updatedEnrollmentDetails = { - ...prevState, - person: { - ...prevState.person, - [field]: fieldValue, - }, - }; - } else { - updatedEnrollmentDetails = { - ...prevState, - [field]: fieldValue, - }; - } - - return updatedEnrollmentDetails; + return { + ...prevState, + [field]: fieldValue, + }; }); }; const handleSaveButtonClick = () => { dispatch( - updateClerkEnrollmentDetails({ + updateClerkEnrollmentAppointment({ enrollment: { ...enrollmentDetails, understandingSkill: enrollmentDetails.speechComprehensionPartialExam && enrollmentDetails.readingComprehensionPartialExam, }, - examEvent, }), ); }; + const handleMoveButtonClick = () => { + // TODO + }; + const handleEditButtonClick = () => { resetLocalEnrollmentDetails(); setCurrentUIMode(UIMode.Edit); @@ -237,15 +219,6 @@ export const ClerkEnrollmentAppointmentDetails = ({ return ( <> - - - diff --git a/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetailsFields.tsx b/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetailsFields.tsx index 8e5c25a06..c5dcd7483 100644 --- a/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetailsFields.tsx +++ b/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetailsFields.tsx @@ -19,7 +19,6 @@ import { import { useDialog } from 'shared/hooks'; import { InputFieldUtils } from 'shared/utils'; -import { ExamEventDetails } from 'components/publicEnrollment/steps/ExamEventDetails'; import { translateOutsideComponent, useClerkTranslation, @@ -330,8 +329,6 @@ export const ClerkEnrollmentAppointmentDetailsFields = ({ const displayPaymentHistory = enrollment.payments.length > 1; // TODO Remove this flag once digital certificates are available - const isDigitalCertificateAvailable = false; - return (
@@ -375,6 +372,39 @@ export const ClerkEnrollmentAppointmentDetailsFields = ({ )} />
+ {!enrollment.digitalCertificateConsent && ( +
+

+ {translateCommon('enrollment.certificateShipping.addressTitle')} +

+
+ + + + +
+
+ )}

{t('header.previousEnrollment')}

@@ -461,7 +491,6 @@ export const ClerkEnrollmentAppointmentDetailsFields = ({
)} -

{t('status')}

{t(`enrollmentStatus.${enrollment.status}`)} @@ -501,63 +530,6 @@ export const ClerkEnrollmentAppointmentDetailsFields = ({ )}
)} - {isDigitalCertificateAvailable && ( -
-

{t('header.digitalCertificateConsent')}

- - onCheckboxFieldChange( - 'digitalCertificateConsent', - !enrollment.digitalCertificateConsent, - ) - } - color={Color.Secondary} - checked={enrollment.digitalCertificateConsent} - disabled={editDisabled} - /> - } - label={translateCommon('enrollment.certificateShipping.consent')} - /> -
- )} - - {!enrollment.digitalCertificateConsent && ( -
-

- {translateCommon('enrollment.certificateShipping.addressTitle')} -

-
- - - - -
-
- )} { + extends Omit { enrollmentTime: string; + payments: Array; } diff --git a/frontend/packages/vkt/src/pages/ClerkEnrollmentAppointmentOverviewPage.tsx b/frontend/packages/vkt/src/pages/ClerkEnrollmentAppointmentOverviewPage.tsx index de5445756..afcfdf762 100644 --- a/frontend/packages/vkt/src/pages/ClerkEnrollmentAppointmentOverviewPage.tsx +++ b/frontend/packages/vkt/src/pages/ClerkEnrollmentAppointmentOverviewPage.tsx @@ -27,7 +27,6 @@ export const ClerkEnrollmentAppointmentOverviewPage: FC = () => { } }, [dispatch, status, params.enrollmentAppointmentId]); - console.log('enrollment', enrollment); return (

diff --git a/frontend/packages/vkt/src/redux/reducers/clerkEnrollmentAppointment.ts b/frontend/packages/vkt/src/redux/reducers/clerkEnrollmentAppointment.ts index 4d5136c78..d95348a96 100644 --- a/frontend/packages/vkt/src/redux/reducers/clerkEnrollmentAppointment.ts +++ b/frontend/packages/vkt/src/redux/reducers/clerkEnrollmentAppointment.ts @@ -2,6 +2,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { APIResponseStatus } from 'shared/enums'; import { ClerkEnrollmentAppointment } from 'interfaces/clerkEnrollment'; +import { ClerkExamEvent } from 'interfaces/clerkExamEvent'; interface ClerkEnrollmentAppointmentState { status: APIResponseStatus; @@ -28,16 +29,38 @@ const clerkEnrollmentAppointmentSlice = createSlice({ state.enrollment = action.payload; state.status = APIResponseStatus.Success; }, + storeClerkEnrollmentAppointmentUpdate( + state, + action: PayloadAction, + ) { + state.status = APIResponseStatus.Success; + state.enrollment = action.payload; + }, rejectClerkEnrollmentAppointment(state) { state.status = APIResponseStatus.Error; }, + updateClerkEnrollmentAppointment( + state, + _action: PayloadAction<{ + enrollment: ClerkEnrollmentAppointment; + examEvent?: ClerkExamEvent; + }>, + ) { + state.status = APIResponseStatus.InProgress; + }, + resetClerkEnrollmentDetailsUpdate(state) { + state.status = initialState.status; + }, }, }); export const clerkEnrollmentAppointmentReducer = clerkEnrollmentAppointmentSlice.reducer; export const { + storeClerkEnrollmentAppointmentUpdate, rejectClerkEnrollmentAppointment, storeClerkEnrollmentAppointment, loadClerkEnrollmentAppointment, + updateClerkEnrollmentAppointment, + resetClerkEnrollmentDetailsUpdate, } = clerkEnrollmentAppointmentSlice.actions; diff --git a/frontend/packages/vkt/src/redux/sagas/clerkEnrollmentAppointment.ts b/frontend/packages/vkt/src/redux/sagas/clerkEnrollmentAppointment.ts index 55406e6be..9a9243e0f 100644 --- a/frontend/packages/vkt/src/redux/sagas/clerkEnrollmentAppointment.ts +++ b/frontend/packages/vkt/src/redux/sagas/clerkEnrollmentAppointment.ts @@ -1,17 +1,52 @@ import { PayloadAction } from '@reduxjs/toolkit'; -import { AxiosResponse } from 'axios'; +import { AxiosError, AxiosResponse } from 'axios'; import { call, put, takeLatest } from 'redux-saga/effects'; import axiosInstance from 'configs/axios'; import { APIEndpoints } from 'enums/api'; -import { ClerkEnrollmentAppointmentResponse } from 'interfaces/clerkEnrollment'; +import { + ClerkEnrollmentAppointment, + ClerkEnrollmentAppointmentResponse, +} from 'interfaces/clerkEnrollment'; +import { setAPIError } from 'redux/reducers/APIError'; import { loadClerkEnrollmentAppointment, rejectClerkEnrollmentAppointment, storeClerkEnrollmentAppointment, + storeClerkEnrollmentAppointmentUpdate, + updateClerkEnrollmentAppointment, } from 'redux/reducers/clerkEnrollmentAppointment'; +import { NotifierUtils } from 'utils/notifier'; import { SerializationUtils } from 'utils/serialization'; +function* updateClerkEnrollmentAppointmentSaga( + action: PayloadAction<{ + enrollment: ClerkEnrollmentAppointment; + }>, +) { + const { enrollment } = action.payload; + + try { + const apiResponse: AxiosResponse = + yield call( + axiosInstance.put, + APIEndpoints.ClerkEnrollment, + SerializationUtils.serializeClerkEnrollmentAppointment(enrollment), + ); + const updatedEnrollment = + SerializationUtils.deserializeClerkEnrollmentAppointment( + apiResponse.data, + ); + + yield put(storeClerkEnrollmentAppointmentUpdate(updatedEnrollment)); + //yield put(storeClerkExamEventOverview(updatedExamEvent)); + } catch (error) { + const errorMessage = NotifierUtils.getAPIErrorMessage(error as AxiosError); + yield put(setAPIError(errorMessage)); + //yield put(rejectClerkEnrollmentDetailsUpdate()); + } +} + function* loadClerkEnrollmentAppointmentSaga(action: PayloadAction) { try { const appointmentId = action.payload; @@ -30,6 +65,10 @@ function* loadClerkEnrollmentAppointmentSaga(action: PayloadAction) { } export function* watchClerkEnrollmentAppointment() { + yield takeLatest( + updateClerkEnrollmentAppointment.type, + updateClerkEnrollmentAppointmentSaga, + ); yield takeLatest( loadClerkEnrollmentAppointment.type, loadClerkEnrollmentAppointmentSaga, diff --git a/frontend/packages/vkt/src/utils/serialization.ts b/frontend/packages/vkt/src/utils/serialization.ts index 467ddb4be..53fee0121 100644 --- a/frontend/packages/vkt/src/utils/serialization.ts +++ b/frontend/packages/vkt/src/utils/serialization.ts @@ -4,6 +4,7 @@ import { DateUtils } from 'shared/utils'; import { ExamLanguage } from 'enums/app'; import { ClerkEnrollment, + ClerkEnrollmentAppointment, ClerkEnrollmentAppointmentResponse, ClerkEnrollmentContactResponse, ClerkEnrollmentResponse, @@ -103,6 +104,15 @@ export class SerializationUtils { }; } + static serializeClerkEnrollmentAppointment( + enrollment: ClerkEnrollmentAppointment, + ) { + return { + ...enrollment, + enrollmentTime: DateUtils.serializeDate(enrollment.enrollmentTime), + }; + } + static deserializeClerkEnrollmentAppointment( enrollment: ClerkEnrollmentAppointmentResponse, ) {