From 4a16a49c4319e56d3ecb6b54a279e53a38d72bf2 Mon Sep 17 00:00:00 2001 From: Jarkko Pesonen <435495+jrkkp@users.noreply.github.com> Date: Mon, 21 Oct 2024 09:53:22 +0300 Subject: [PATCH] VKT(Frontend & Backend): Enrollment appointment contact form continues --- .../java/fi/oph/vkt/api/PublicController.java | 7 +++++ .../dto/PublicEnrollmentContactCreateDTO.java | 30 +++++++++++++++++++ .../service/AbstractEnrollmentService.java | 16 ++++++++++ .../vkt/service/PublicEnrollmentService.java | 14 +++++++++ .../PublicEnrollmentContactControlButtons.tsx | 13 ++++---- .../PublicEnrollmentContactDesktopGrid.tsx | 3 ++ .../PublicEnrollmentContactGrid.tsx | 16 ++++++---- .../PublicEnrollmentContactStepContents.tsx | 1 + .../steps/FillContactDetails.tsx | 5 +++- frontend/packages/vkt/src/enums/api.ts | 2 +- .../redux/reducers/publicEnrollmentContact.ts | 5 +++- .../redux/sagas/publicEnrollmentContact.ts | 10 +++++-- frontend/packages/vkt/src/utils/routes.ts | 22 +++++++------- 13 files changed, 115 insertions(+), 29 deletions(-) create mode 100644 backend/vkt/src/main/java/fi/oph/vkt/api/dto/PublicEnrollmentContactCreateDTO.java diff --git a/backend/vkt/src/main/java/fi/oph/vkt/api/PublicController.java b/backend/vkt/src/main/java/fi/oph/vkt/api/PublicController.java index 4747e829c..ab76e524e 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/api/PublicController.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/api/PublicController.java @@ -4,6 +4,7 @@ import fi.oph.vkt.api.dto.PublicEducationDTO; import fi.oph.vkt.api.dto.PublicEnrollmentAppointmentDTO; import fi.oph.vkt.api.dto.PublicEnrollmentAppointmentUpdateDTO; +import fi.oph.vkt.api.dto.PublicEnrollmentContactCreateDTO; import fi.oph.vkt.api.dto.PublicEnrollmentCreateDTO; import fi.oph.vkt.api.dto.PublicEnrollmentDTO; import fi.oph.vkt.api.dto.PublicEnrollmentInitialisationDTO; @@ -110,6 +111,12 @@ public List listExaminers() { return publicExaminerService.listExaminers(); } + @PostMapping(path = "/enrollment/examiner/{examinerId:\\d+}") + @ResponseStatus(HttpStatus.CREATED) + public void createEnrollmentContact(@RequestBody @Valid final PublicEnrollmentContactCreateDTO dto) { + publicEnrollmentService.createEnrollmentContact(dto); + } + @PostMapping(path = "/enrollment/reservation/{reservationId:\\d+}") @ResponseStatus(HttpStatus.CREATED) public PublicEnrollmentDTO createEnrollment( diff --git a/backend/vkt/src/main/java/fi/oph/vkt/api/dto/PublicEnrollmentContactCreateDTO.java b/backend/vkt/src/main/java/fi/oph/vkt/api/dto/PublicEnrollmentContactCreateDTO.java new file mode 100644 index 000000000..65994eaf7 --- /dev/null +++ b/backend/vkt/src/main/java/fi/oph/vkt/api/dto/PublicEnrollmentContactCreateDTO.java @@ -0,0 +1,30 @@ +package fi.oph.vkt.api.dto; + +import fi.oph.vkt.util.StringUtil; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Builder; +import lombok.NonNull; + +@Builder +public record PublicEnrollmentContactCreateDTO( + @NonNull @NotNull Boolean oralSkill, + @NonNull @NotNull Boolean textualSkill, + @NonNull @NotNull Boolean understandingSkill, + @NonNull @NotNull Boolean speakingPartialExam, + @NonNull @NotNull Boolean speechComprehensionPartialExam, + @NonNull @NotNull Boolean writingPartialExam, + @NonNull @NotNull Boolean readingComprehensionPartialExam, + @Size(max = 1024) String previousEnrollment, + @Size(max = 255) @NonNull @NotBlank String email, + @Size(max = 255) @NonNull @NotBlank String firstName, + @Size(max = 255) @NonNull @NotBlank String lastName +) { + public PublicEnrollmentContactCreateDTO { + previousEnrollment = StringUtil.sanitize(previousEnrollment); + email = StringUtil.sanitize(email); + firstName = StringUtil.sanitize(firstName); + lastName = StringUtil.sanitize(lastName); + } +} diff --git a/backend/vkt/src/main/java/fi/oph/vkt/service/AbstractEnrollmentService.java b/backend/vkt/src/main/java/fi/oph/vkt/service/AbstractEnrollmentService.java index 683a35699..0619d5e09 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/service/AbstractEnrollmentService.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/service/AbstractEnrollmentService.java @@ -1,7 +1,9 @@ package fi.oph.vkt.service; import fi.oph.vkt.api.dto.EnrollmentDTOCommonFields; +import fi.oph.vkt.api.dto.PublicEnrollmentContactCreateDTO; import fi.oph.vkt.model.Enrollment; +import fi.oph.vkt.model.EnrollmentAppointment; import fi.oph.vkt.model.ExamEvent; import fi.oph.vkt.model.Person; import fi.oph.vkt.repository.EnrollmentRepository; @@ -9,6 +11,20 @@ public abstract class AbstractEnrollmentService { + protected void copyDtoFieldsToEnrollment( + final EnrollmentAppointment enrollment, + final PublicEnrollmentContactCreateDTO dto + ) { + enrollment.setOralSkill(dto.oralSkill()); + enrollment.setTextualSkill(dto.textualSkill()); + enrollment.setUnderstandingSkill(dto.understandingSkill()); + enrollment.setSpeakingPartialExam(dto.speakingPartialExam()); + enrollment.setSpeechComprehensionPartialExam(dto.speechComprehensionPartialExam()); + enrollment.setWritingPartialExam(dto.writingPartialExam()); + enrollment.setReadingComprehensionPartialExam(dto.readingComprehensionPartialExam()); + enrollment.setEmail(dto.email()); + } + protected void copyDtoFieldsToEnrollment(final Enrollment enrollment, final EnrollmentDTOCommonFields dto) { enrollment.setOralSkill(dto.oralSkill()); enrollment.setTextualSkill(dto.textualSkill()); diff --git a/backend/vkt/src/main/java/fi/oph/vkt/service/PublicEnrollmentService.java b/backend/vkt/src/main/java/fi/oph/vkt/service/PublicEnrollmentService.java index 0e086cb00..ff64c1c86 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/service/PublicEnrollmentService.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/service/PublicEnrollmentService.java @@ -6,6 +6,7 @@ import fi.oph.vkt.api.dto.PublicEducationDTO; import fi.oph.vkt.api.dto.PublicEnrollmentAppointmentDTO; import fi.oph.vkt.api.dto.PublicEnrollmentAppointmentUpdateDTO; +import fi.oph.vkt.api.dto.PublicEnrollmentContactCreateDTO; import fi.oph.vkt.api.dto.PublicEnrollmentCreateDTO; import fi.oph.vkt.api.dto.PublicEnrollmentDTO; import fi.oph.vkt.api.dto.PublicEnrollmentInitialisationDTO; @@ -671,4 +672,17 @@ public PublicEnrollmentAppointmentDTO saveEnrollmentAppointment( return createEnrollmentAppointmentDTO(enrollmentAppointment); } + + public void createEnrollmentContact(final PublicEnrollmentContactCreateDTO dto) { + final EnrollmentAppointment enrollmentAppointment = new EnrollmentAppointment(); + + enrollmentAppointment.setStatus(EnrollmentStatus.EXPECTING_PAYMENT_UNFINISHED_ENROLLMENT); + copyDtoFieldsToEnrollment(enrollmentAppointment, dto); + + // TODO: remove + enrollmentAppointment.setAuthHash("asd"); + + + enrollmentAppointmentRepository.saveAndFlush(enrollmentAppointment); + } } diff --git a/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactControlButtons.tsx b/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactControlButtons.tsx index cd07a9a0d..fd4179f90 100644 --- a/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactControlButtons.tsx +++ b/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactControlButtons.tsx @@ -9,12 +9,11 @@ import { APIResponseStatus, Color, Variant } from 'shared/enums'; import { useCommonTranslation, usePublicTranslation } from 'configs/i18n'; import { useAppDispatch } from 'configs/redux'; +import { AppRoutes } from 'enums/app'; import { PublicEnrollmentContactFormStep } from 'enums/publicEnrollment'; import { PublicEnrollmentContact } from 'interfaces/publicEnrollment'; -import { setLoadingPayment } from 'redux/reducers/publicEnrollmentAppointment'; import { loadPublicEnrollmentSave } from 'redux/reducers/publicEnrollmentContact'; import { RouteUtils } from 'utils/routes'; -import { AppRoutes } from 'enums/app'; export const PublicEnrollmentContactControlButtons = ({ activeStep, @@ -22,12 +21,14 @@ export const PublicEnrollmentContactControlButtons = ({ isStepValid, setShowValidation, submitStatus, + examinerId, }: { activeStep: PublicEnrollmentContactFormStep; enrollment: PublicEnrollmentContact; isStepValid: boolean; setShowValidation: (showValidation: boolean) => void; submitStatus: APIResponseStatus; + examinerId: number; }) => { const { t } = usePublicTranslation({ keyPrefix: 'vkt.component.publicEnrollment.controlButtons', @@ -46,18 +47,18 @@ export const PublicEnrollmentContactControlButtons = ({ if (submitStatus === APIResponseStatus.Success) { navigate(AppRoutes.PublicGoodAndSatisfactoryLevelLanding); } - }, [submitStatus, enrollment.id, dispatch]); + }, [submitStatus, navigate]); const handleBackBtnClick = () => { const nextStep: PublicEnrollmentContactFormStep = activeStep - 1; - navigate(RouteUtils.contactStepToRoute(nextStep, enrollment.id)); + navigate(RouteUtils.contactStepToRoute(nextStep, examinerId)); }; const handleNextBtnClick = () => { if (isStepValid) { setShowValidation(false); const nextStep: PublicEnrollmentContactFormStep = activeStep + 1; - navigate(RouteUtils.contactStepToRoute(nextStep, enrollment.id)); + navigate(RouteUtils.contactStepToRoute(nextStep, examinerId)); } else { setShowValidation(true); } @@ -67,7 +68,7 @@ export const PublicEnrollmentContactControlButtons = ({ if (isStepValid) { setIsSubmitLoading(true); setShowValidation(false); - dispatch(loadPublicEnrollmentSave(enrollment)); + dispatch(loadPublicEnrollmentSave({ enrollment, examinerId })); } else { setShowValidation(true); } diff --git a/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactDesktopGrid.tsx b/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactDesktopGrid.tsx index 3e2a04fc0..4226f71ff 100644 --- a/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactDesktopGrid.tsx +++ b/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactDesktopGrid.tsx @@ -18,6 +18,7 @@ export const PublicEnrollmentContactDesktopGrid = ({ showValidation, setIsStepValid, setShowValidation, + examinerId, }: { activeStep: PublicEnrollmentContactFormStep; isStepValid: boolean; @@ -25,6 +26,7 @@ export const PublicEnrollmentContactDesktopGrid = ({ showValidation: boolean; setIsStepValid: (isValid: boolean) => void; setShowValidation: (showValidation: boolean) => void; + examinerId: number; }) => { const translateCommon = useCommonTranslation(); @@ -59,6 +61,7 @@ export const PublicEnrollmentContactDesktopGrid = ({ setShowValidation={setShowValidation} isStepValid={isStepValid} submitStatus={enrollmentSubmitStatus} + examinerId={examinerId} /> )} diff --git a/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactGrid.tsx b/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactGrid.tsx index 56b5bb060..25be344d6 100644 --- a/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactGrid.tsx +++ b/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactGrid.tsx @@ -15,6 +15,8 @@ export const PublicEnrollmentContactGrid = ({ activeStep: PublicEnrollmentContactFormStep; }) => { const params = useParams(); + const examinerId = + params.examinerId !== undefined ? +params.examinerId : null; const dispatch = useAppDispatch(); const { enrollment, loadExamEventStatus } = useAppSelector( publicEnrollmentContactSelector, @@ -23,13 +25,14 @@ export const PublicEnrollmentContactGrid = ({ const [showValidation, setShowValidation] = useState(false); useEffect(() => { - if ( - loadExamEventStatus === APIResponseStatus.NotStarted && - params.enrollmentId - ) { - dispatch(loadPublicExamEvent(+params.enrollmentId)); + if (loadExamEventStatus === APIResponseStatus.NotStarted && examinerId) { + dispatch(loadPublicExamEvent(examinerId)); } - }, [dispatch, loadExamEventStatus, params.enrollmentId]); + }, [dispatch, loadExamEventStatus, examinerId]); + + if (!examinerId) { + return <>; + } return ( ); diff --git a/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactStepContents.tsx b/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactStepContents.tsx index bba02b1e0..f744c147f 100644 --- a/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactStepContents.tsx +++ b/frontend/packages/vkt/src/components/publicEnrollmentContact/PublicEnrollmentContactStepContents.tsx @@ -23,6 +23,7 @@ export const PublicEnrollmentContactStepContents = ({ enrollment={enrollment} isLoading={false} setIsStepValid={setIsStepValid} + updatePublicEnrollment={updatePublicEnrollment} /> ); case PublicEnrollmentContactFormStep.SelectExam: diff --git a/frontend/packages/vkt/src/components/publicEnrollmentContact/steps/FillContactDetails.tsx b/frontend/packages/vkt/src/components/publicEnrollmentContact/steps/FillContactDetails.tsx index e6038073b..1d84471eb 100644 --- a/frontend/packages/vkt/src/components/publicEnrollmentContact/steps/FillContactDetails.tsx +++ b/frontend/packages/vkt/src/components/publicEnrollmentContact/steps/FillContactDetails.tsx @@ -5,16 +5,19 @@ import { InputAutoComplete, TextFieldTypes } from 'shared/enums'; import { usePublicTranslation } from 'configs/i18n'; import { useAppDispatch } from 'configs/redux'; import { PublicEnrollmentContact } from 'interfaces/publicEnrollment'; -import { updatePublicEnrollment } from 'redux/reducers/publicEnrollmentContact'; export const FillContactDetails = ({ isLoading, enrollment, setIsStepValid, + updatePublicEnrollment, }: { isLoading: boolean; enrollment: PublicEnrollmentContact; setIsStepValid: (isValid: boolean) => void; + updatePublicEnrollment: ( + enrollment: Partial, + ) => AnyAction; }) => { const { t } = usePublicTranslation({ keyPrefix: 'vkt.component.publicEnrollment.steps.fillContactDetails', diff --git a/frontend/packages/vkt/src/enums/api.ts b/frontend/packages/vkt/src/enums/api.ts index d64f74551..ade548f59 100644 --- a/frontend/packages/vkt/src/enums/api.ts +++ b/frontend/packages/vkt/src/enums/api.ts @@ -3,7 +3,7 @@ export enum APIEndpoints { PublicAuthLogout = '/vkt/api/v1/auth/logout', PublicExamEvent = '/vkt/api/v1/examEvent', PublicEnrollmentAppointment = '/vkt/api/v1/enrollment/appointment', - PublicEnrollmentContact = '/vkt/api/v1/enrollment/contact-request', + PublicEnrollmentContact = '/vkt/api/v1/enrollment/examiner', PublicExaminer = '/vkt/api/v1/examiner', PublicEnrollment = '/vkt/api/v1/enrollment', PublicReservation = '/vkt/api/v1/reservation', diff --git a/frontend/packages/vkt/src/redux/reducers/publicEnrollmentContact.ts b/frontend/packages/vkt/src/redux/reducers/publicEnrollmentContact.ts index d47d8f1b7..a9c55b0b8 100644 --- a/frontend/packages/vkt/src/redux/reducers/publicEnrollmentContact.ts +++ b/frontend/packages/vkt/src/redux/reducers/publicEnrollmentContact.ts @@ -62,7 +62,10 @@ const publicEnrollmentContactSlice = createSlice({ }, loadPublicEnrollmentSave( state, - _action: PayloadAction, + _action: PayloadAction<{ + enrollment: PublicEnrollmentContact; + examinerId: number; + }>, ) { state.enrollmentSubmitStatus = APIResponseStatus.InProgress; }, diff --git a/frontend/packages/vkt/src/redux/sagas/publicEnrollmentContact.ts b/frontend/packages/vkt/src/redux/sagas/publicEnrollmentContact.ts index f90a078b3..120cf6e5f 100644 --- a/frontend/packages/vkt/src/redux/sagas/publicEnrollmentContact.ts +++ b/frontend/packages/vkt/src/redux/sagas/publicEnrollmentContact.ts @@ -41,14 +41,18 @@ function* loadPublicExamEventSaga(action: PayloadAction) { } function* loadPublicEnrollmentSaveSaga( - action: PayloadAction, + action: PayloadAction<{ + enrollment: PublicEnrollmentContact; + examinerId: number; + }>, ) { - const enrollment = action.payload; + const enrollment = action.payload.enrollment; + const examinerId = action.payload.examinerId; try { const { id: _unused2, status: _unused5, ...body } = enrollment; - const saveUrl = `${APIEndpoints.PublicEnrollmentContact}/${enrollment.id}`; + const saveUrl = `${APIEndpoints.PublicEnrollmentContact}/${examinerId}`; yield call(axiosInstance.post, saveUrl, body); diff --git a/frontend/packages/vkt/src/utils/routes.ts b/frontend/packages/vkt/src/utils/routes.ts index d11bd3ee3..83daf9d07 100644 --- a/frontend/packages/vkt/src/utils/routes.ts +++ b/frontend/packages/vkt/src/utils/routes.ts @@ -139,31 +139,31 @@ export class RouteUtils { return route.replace(':enrollmentId', enrollmentId.toString()); } + static replaceExaminerId(route: string, examinerId: number) { + return route.replace(':examinerId', examinerId.toString()); + } + static contactStepToRoute( step: PublicEnrollmentContactFormStep, - enrollmentId?: number, + examinerId: number, ) { - if (!enrollmentId) { - return ''; - } - switch (step) { case PublicEnrollmentContactFormStep.FillContactDetails: - return RouteUtils.replaceEnrollmentId( + return RouteUtils.replaceExaminerId( AppRoutes.PublicEnrollmentContactContactDetails, - enrollmentId, + examinerId, ); case PublicEnrollmentContactFormStep.SelectExam: - return RouteUtils.replaceEnrollmentId( + return RouteUtils.replaceExaminerId( AppRoutes.PublicEnrollmentContactSelectExam, - enrollmentId, + examinerId, ); case PublicEnrollmentContactFormStep.Done: - return RouteUtils.replaceEnrollmentId( + return RouteUtils.replaceExaminerId( AppRoutes.PublicEnrollmentContactDone, - enrollmentId, + examinerId, ); } }