Skip to content

Commit

Permalink
VKT(Frontend & Backend): Delete examiner contact request functionalit…
Browse files Browse the repository at this point in the history
…y [deploy]
  • Loading branch information
jrkkp committed Nov 21, 2024
1 parent 98b4e29 commit 64b8f0e
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,21 @@ public ExaminerEnrollmentAppointmentDTO updateEnrollmentAppointment(
@GetMapping(path = "/contact/{enrollmentContactId:\\d+}", consumes = ALL_VALUE)
@Operation(tags = TAG_ENROLLMENT, summary = "Get enrollment contact request")
public ClerkEnrollmentContactRequestDTO getEnrollmentContactRequest(
@PathVariable String oid,
@PathVariable final String oid,
@PathVariable final long enrollmentContactId
) {
return examinerEnrollmentService.getEnrollmentContactRequest(oid, enrollmentContactId);
}

@DeleteMapping(path = "/contact/{enrollmentContactId:\\d+}", consumes = ALL_VALUE)
@Operation(tags = TAG_ENROLLMENT, summary = "Delete enrollment contact request")
public void deleteEnrollmentContactRequest(
@PathVariable final String oid,
@PathVariable final long enrollmentContactId
) {
examinerEnrollmentService.deleteEnrollmentContactRequest(oid, enrollmentContactId);
}

@PostMapping(path = "/contact/{enrollmentContactId:\\d+}/convertToAppointment", consumes = ALL_VALUE)
@Operation(tags = TAG_ENROLLMENT, summary = "Convert enrollment contact request to enrollment appointment")
public ExaminerEnrollmentAppointmentDTO enrollmentContactRequestToAppointment(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
package fi.oph.vkt.repository;

import fi.oph.vkt.api.dto.FreeEnrollmentDetails;
import fi.oph.vkt.model.Enrollment;
import fi.oph.vkt.model.EnrollmentAppointment;
import fi.oph.vkt.model.ExamEvent;
import fi.oph.vkt.model.Examiner;
import fi.oph.vkt.model.Person;
import fi.oph.vkt.model.type.EnrollmentAppointmentStatus;
import fi.oph.vkt.model.type.EnrollmentStatus;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

@Repository
public interface EnrollmentAppointmentRepository extends BaseRepository<EnrollmentAppointment> {
Optional<EnrollmentAppointment> findByIdAndAuthHash(final long id, final String paymentLinkHash);
List<EnrollmentAppointment> findByExaminerAndStatus(
Optional<EnrollmentAppointment> findByIdAndAuthHashAndDeletedAtIsNull(final long id, final String paymentLinkHash);
List<EnrollmentAppointment> findByExaminerAndStatusAndDeletedAtIsNull(
final Examiner examiner,
final EnrollmentAppointmentStatus status
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public ExaminerDetailsDTO getExaminer(final String oid) {
throw new APIException(APIExceptionType.EXAMINER_NOT_FOUND);
}
final String baseUrlAPI = environment.getRequiredProperty("app.base-url.api");
final List<EnrollmentAppointment> enrollmentAppointments = enrollmentAppointmentRepository.findByExaminerAndStatus(
final List<EnrollmentAppointment> enrollmentAppointments = enrollmentAppointmentRepository.findByExaminerAndStatusAndDeletedAtIsNull(
examiner,
EnrollmentAppointmentStatus.CONTACT_CREATED
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,17 @@ public ExaminerEnrollmentAppointmentDTO sendEnrollmentAppointmentLink(

return ClerkEnrollmentUtil.createClerkEnrollmentAppointmentDTO(enrollmentAppointment, baseUrlAPI);
}

@Transactional
public void deleteEnrollmentContactRequest(final String oid, final long enrollmentContactId) {
final EnrollmentAppointment enrollmentAppointment = enrollmentAppointmentRepository.getReferenceById(
enrollmentContactId
);

checkExaminerOid(enrollmentAppointment, oid);

enrollmentAppointment.setDeletedAt(LocalDateTime.now());

enrollmentAppointmentRepository.flush();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ public EnrollmentAppointment getEnrollmentAppointmentByHash(
final long enrollmentAppointmentId,
final String authHash
) {
return enrollmentAppointmentRepository.findByIdAndAuthHash(enrollmentAppointmentId, authHash).orElseThrow();
return enrollmentAppointmentRepository
.findByIdAndAuthHashAndDeletedAtIsNull(enrollmentAppointmentId, authHash)
.orElseThrow();
}

public void savePersonInfo(final long targetId, final Long appointmentId, final Person person) {
Expand Down
10 changes: 10 additions & 0 deletions frontend/packages/vkt/public/i18n/fi-FI/clerk.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,16 @@
"getAllKoskiEducations": "Hae kaikki KOSKI-koulutustiedot"
}
},
"clerkcontactRequest": {
"createEnrollment": "Luo ilmoittautuminen",
"contactDetails": "Yhteydenoton tiedot",
"message": "Viesti",
"wantFullExam": "Haluan suorittaa koko tutkinnon?",
"deleteAreYouSure": "Haluatko varmasti poistaa yhteydenoton?",
"deleteDescription": "Toimintoa ei voida peruuttaa.",
"deleteContactRequest": "Poista yhteydenotto",
"deleteContactRequestSuccess": "Yhteydenotto poistettu"
},
"clerkEnrollmentListing": {
"header": {
"examEventCoverage": "Tutkintokokonaisuus",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,65 @@ import {
LoadingProgressIndicator,
Text,
} from 'shared/components';
import { APIResponseStatus, Color, Variant } from 'shared/enums';
import {
APIResponseStatus,
Color,
Duration,
Severity,
Variant,
} from 'shared/enums';
import { useDialog, useToast } from 'shared/hooks';

import { TopControls } from 'components/clerkExamEvent/overview/TopControls';
import { useCommonTranslation } from 'configs/i18n';
import { useClerkTranslation, useCommonTranslation } from 'configs/i18n';
import { useAppDispatch, useAppSelector } from 'configs/redux';
import { AppRoutes } from 'enums/app';
import { PartialExamsAndSkills } from 'interfaces/common/enrollment';
import {
createClerkEnrollmentAppointment,
deleteClerkEnrollmentContactRequest,
loadClerkEnrollmentContactRequest,
resetClerkEnrollmentContactRequestToInitialState,
} from 'redux/reducers/clerkEnrollmentContactRequest';
import { resetExaminerDetailsToInitialState } from 'redux/reducers/examinerDetails';
import { clerkEnrollmentContactRequestSelector } from 'redux/selectors/clerkEnrollmentContactRequest';
import { EnrollmentUtils } from 'utils/enrollment';

export const ClerkEnrollmentContactRequestPage: FC = () => {
const { status, createStatus, enrollment } = useAppSelector(
const { status, deleteStatus, createStatus, enrollment } = useAppSelector(
clerkEnrollmentContactRequestSelector,
);
const { t } = useClerkTranslation({
keyPrefix: 'vkt.component.clerkcontactRequest',
});
const translateCommon = useCommonTranslation();
const params = useParams();
const navigate = useNavigate();
const { showDialog } = useDialog();
const { showToast } = useToast();

const dispatch = useAppDispatch();

useEffect(() => {
if (deleteStatus === APIResponseStatus.Success && params.oid) {
dispatch(resetExaminerDetailsToInitialState());
navigate(AppRoutes.ExaminerHomePage.replace(':oid', params.oid));
showToast({
severity: Severity.Success,
description: t('deleteContactRequestSuccess'),
timeOut: Duration.Short,
});
}
}, [dispatch, params.oid, deleteStatus, navigate, t, showToast]);

// Cleanup on unmount
useEffect(
() => () => {
dispatch(resetClerkEnrollmentContactRequestToInitialState());
},
[dispatch],
);

useEffect(() => {
if (
enrollment?.id &&
Expand Down Expand Up @@ -84,7 +118,8 @@ export const ClerkEnrollmentContactRequestPage: FC = () => {
]);

const isLoading = status === APIResponseStatus.InProgress;
const isSavingDisabled = isLoading;
const isDeleteLoading = deleteStatus === APIResponseStatus.InProgress;
const isSavingDisabled = isDeleteLoading || isLoading;

if (!enrollment) {
return <></>;
Expand Down Expand Up @@ -123,6 +158,32 @@ export const ClerkEnrollmentContactRequestPage: FC = () => {
}),
);
};

const openDeleteDialog = () => {
showDialog({
title: t('deleteAreYouSure'),
severity: Severity.Warning,
description: t('deleteDescription'),
actions: [
{
title: translateCommon('back'),
variant: Variant.Outlined,
},
{
title: translateCommon('yes'),
variant: Variant.Contained,
action: () =>
dispatch(
deleteClerkEnrollmentContactRequest({
id: enrollment.id,
oid: params.oid || '',
}),
),
},
],
});
};

const backTo = AppRoutes.ExaminerHomePage.replace(':oid', params.oid || '');

return (
Expand Down Expand Up @@ -164,9 +225,9 @@ export const ClerkEnrollmentContactRequestPage: FC = () => {
</div>
</div>
<Divider />
<H2>Yhteydenoton tiedot</H2>
<H2>{t('contactDetails')}</H2>
<div className="rows gapped">
<H3>Haluan suorittaa koko tutkinnon?</H3>
<H3>{t('wantFullExam')}</H3>
<Text>
{EnrollmentUtils.isFullExam(enrollment)
? translateCommon('yes')
Expand All @@ -186,10 +247,21 @@ export const ClerkEnrollmentContactRequestPage: FC = () => {
</Text>
</div>
<div className="rows gapped">
<H3>Viesti</H3>
<H3>{t('message')}</H3>
<Text>{enrollment.message}</Text>
</div>
<div className="columns flex-end">
<div className="columns gapped-sm flex-end">
<LoadingProgressIndicator isLoading={isDeleteLoading}>
<CustomButton
data-testid="clerk-translator-overview__translator-details__save-btn"
variant={Variant.Outlined}
color={Color.Secondary}
disabled={isSavingDisabled}
onClick={openDeleteDialog}
>
{t('deleteContactRequest')}
</CustomButton>
</LoadingProgressIndicator>
<LoadingProgressIndicator isLoading={isLoading}>
<CustomButton
data-testid="clerk-translator-overview__translator-details__save-btn"
Expand All @@ -198,7 +270,7 @@ export const ClerkEnrollmentContactRequestPage: FC = () => {
disabled={isSavingDisabled}
onClick={onSubmit}
>
{translateCommon('save')}
{t('createEnrollment')}
</CustomButton>
</LoadingProgressIndicator>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import { ClerkEnrollmentContact } from 'interfaces/clerkEnrollment';

interface ClerkEnrollmentContactRequestState {
status: APIResponseStatus;
deleteStatus: APIResponseStatus;
enrollment?: ClerkEnrollmentContact;
createStatus: APIResponseStatus;
}

const initialState: ClerkEnrollmentContactRequestState = {
status: APIResponseStatus.NotStarted,
createStatus: APIResponseStatus.NotStarted,
deleteStatus: APIResponseStatus.NotStarted,
};

const clerkEnrollmentContactRequestSlice = createSlice({
Expand Down Expand Up @@ -58,6 +60,18 @@ const clerkEnrollmentContactRequestSlice = createSlice({
resetClerkEnrollmentContactRequestToInitialState(_state) {
return initialState;
},
deleteClerkEnrollmentContactRequest(
state,
_action: PayloadAction<{
id: number;
oid: string;
}>,
) {
state.deleteStatus = APIResponseStatus.InProgress;
},
storeDeleteClerkEnrollmentContactRequest(state) {
state.deleteStatus = APIResponseStatus.Success;
},
},
});

Expand All @@ -71,4 +85,6 @@ export const {
storeCreateClerkEnrollmentAppointment,
rejectCreateClerkEnrollmentAppointment,
resetClerkEnrollmentContactRequestToInitialState,
deleteClerkEnrollmentContactRequest,
storeDeleteClerkEnrollmentContactRequest,
} = clerkEnrollmentContactRequestSlice.actions;
4 changes: 4 additions & 0 deletions frontend/packages/vkt/src/redux/reducers/examinerDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ const examinerDetailsSlice = createSlice({
) {
state.examEventFilters.toggleFilter = action.payload;
},
resetExaminerDetailsToInitialState(_state) {
return initialState;
},
},
});

Expand All @@ -58,4 +61,5 @@ export const {
setExaminerOid,
setExaminerExamEventLanguageFilter,
setExaminerExamEventToggleFilter,
resetExaminerDetailsToInitialState,
} = examinerDetailsSlice.actions;
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import { APIEndpoints } from 'enums/api';
import { ClerkEnrollmentContactResponse } from 'interfaces/clerkEnrollment';
import {
createClerkEnrollmentAppointment,
deleteClerkEnrollmentContactRequest,
loadClerkEnrollmentContactRequest,
rejectClerkEnrollmentContactRequest,
rejectCreateClerkEnrollmentAppointment,
storeClerkEnrollmentContactRequest,
storeCreateClerkEnrollmentAppointment,
storeDeleteClerkEnrollmentContactRequest,
} from 'redux/reducers/clerkEnrollmentContactRequest';
import { SerializationUtils } from 'utils/serialization';

Expand Down Expand Up @@ -43,6 +45,27 @@ function* createClerkEnrollmentAppointmentSaga(
}
}

function* deleteClerkEnrollmentContactRequestSaga(
action: PayloadAction<{
id: number;
oid: string;
}>,
) {
try {
const { id, oid } = action.payload;
const deleteUrl = `${APIEndpoints.ExaminerEnrollmentContactRequest.replace(
/:oid/,
oid,
)}/${id}`;

yield call(axiosInstance.delete, deleteUrl);

yield put(storeDeleteClerkEnrollmentContactRequest());
} catch (error) {
yield put(rejectClerkEnrollmentContactRequest());
}
}

function* loadClerkEnrollmentContactRequestSaga(
action: PayloadAction<{
id: number;
Expand Down Expand Up @@ -76,6 +99,10 @@ export function* watchClerkEnrollmentContactRequest() {
loadClerkEnrollmentContactRequest.type,
loadClerkEnrollmentContactRequestSaga,
);
yield takeLatest(
deleteClerkEnrollmentContactRequest.type,
deleteClerkEnrollmentContactRequestSaga,
);
yield takeLatest(
createClerkEnrollmentAppointment.type,
createClerkEnrollmentAppointmentSaga,
Expand Down

0 comments on commit 64b8f0e

Please sign in to comment.