From 81e5b1d0b85afe7d9e0058c87e0480dce8306df3 Mon Sep 17 00:00:00 2001 From: Jarkko Pesonen <435495+jrkkp@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:58:31 +0200 Subject: [PATCH] VKT(Frontend & Backend): Grade UI fixes --- .../clerk/ExaminerEnrollmentGradesDTO.java | 3 + .../fi/oph/vkt/model/EnrollmentGrade.java | 2 +- .../vkt/model/type/EnrollmentGradeType.java | 5 +- .../service/ExaminerEnrollmentService.java | 3 + .../vkt/public/i18n/fi-FI/common.json | 5 + ...lerkEnrollmentAppointmentDetailsFields.tsx | 108 ++++++++++-------- frontend/packages/vkt/src/enums/app.ts | 6 + .../vkt/src/interfaces/clerkEnrollment.ts | 5 +- .../vkt/src/interfaces/common/enrollment.ts | 9 +- .../reducers/clerkEnrollmentAppointment.ts | 1 + .../redux/sagas/clerkEnrollmentAppointment.ts | 1 + 11 files changed, 95 insertions(+), 53 deletions(-) diff --git a/backend/vkt/src/main/java/fi/oph/vkt/api/dto/clerk/ExaminerEnrollmentGradesDTO.java b/backend/vkt/src/main/java/fi/oph/vkt/api/dto/clerk/ExaminerEnrollmentGradesDTO.java index a2d91e85a..4ec1e706a 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/api/dto/clerk/ExaminerEnrollmentGradesDTO.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/api/dto/clerk/ExaminerEnrollmentGradesDTO.java @@ -1,10 +1,13 @@ package fi.oph.vkt.api.dto.clerk; import fi.oph.vkt.api.dto.EnrollmentGradeDTO; +import jakarta.validation.constraints.NotNull; import lombok.Builder; +import lombok.NonNull; @Builder public record ExaminerEnrollmentGradesDTO( + @NonNull @NotNull Integer version, EnrollmentGradeDTO speakingPartialExam, EnrollmentGradeDTO speechComprehensionPartialExam, EnrollmentGradeDTO writingPartialExam, diff --git a/backend/vkt/src/main/java/fi/oph/vkt/model/EnrollmentGrade.java b/backend/vkt/src/main/java/fi/oph/vkt/model/EnrollmentGrade.java index da07e472a..b01db18cb 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/model/EnrollmentGrade.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/model/EnrollmentGrade.java @@ -15,7 +15,7 @@ @Setter @Entity @Table(name = "enrollment_grade") -public class EnrollmentGrade { +public class EnrollmentGrade extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/backend/vkt/src/main/java/fi/oph/vkt/model/type/EnrollmentGradeType.java b/backend/vkt/src/main/java/fi/oph/vkt/model/type/EnrollmentGradeType.java index 5cc8fa290..2165a23dc 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/model/type/EnrollmentGradeType.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/model/type/EnrollmentGradeType.java @@ -1,6 +1,7 @@ package fi.oph.vkt.model.type; public enum EnrollmentGradeType { - ACCEPTED, - FAILED, + GOOD, + SATISFACTORY, + FAILED } diff --git a/backend/vkt/src/main/java/fi/oph/vkt/service/ExaminerEnrollmentService.java b/backend/vkt/src/main/java/fi/oph/vkt/service/ExaminerEnrollmentService.java index bd939627d..f0e2462ba 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/service/ExaminerEnrollmentService.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/service/ExaminerEnrollmentService.java @@ -137,6 +137,7 @@ public ExaminerEnrollmentGradesDTO upsertAppointmentGrades( final EnrollmentAppointment enrollmentAppointment = enrollmentAppointmentRepository.getReferenceById( enrollmentAppointmentId ); + checkExaminerOid(enrollmentAppointment, oid); final Optional enrollmentGradeOptional = enrollmentGradesRepository.findByEnrollmentAppointment( @@ -144,6 +145,7 @@ public ExaminerEnrollmentGradesDTO upsertAppointmentGrades( ); final EnrollmentGrade enrollmentGrade = enrollmentGradeOptional.orElseGet(EnrollmentGrade::new); + enrollmentGrade.assertVersion(dto.version()); if (dto.speakingPartialExam() != null) { enrollmentGrade.setSpeakingPartialExamGrade(dto.speakingPartialExam().grade()); enrollmentGrade.setSpeakingPartialExamComment(dto.speakingPartialExam().comment()); @@ -187,6 +189,7 @@ public ExaminerEnrollmentGradesDTO upsertAppointmentGrades( private ExaminerEnrollmentGradesDTO createGradesDTO(final EnrollmentGrade enrollmentGrade) { return ExaminerEnrollmentGradesDTO .builder() + .version(enrollmentGrade.getVersion()) .writingPartialExam( createGradeDTO(enrollmentGrade.getWritingPartialExamGrade(), enrollmentGrade.getWritingPartialExamComment()) ) diff --git a/frontend/packages/vkt/public/i18n/fi-FI/common.json b/frontend/packages/vkt/public/i18n/fi-FI/common.json index 82f097d72..37e725e5d 100644 --- a/frontend/packages/vkt/public/i18n/fi-FI/common.json +++ b/frontend/packages/vkt/public/i18n/fi-FI/common.json @@ -32,6 +32,11 @@ "understandingSkill": "Ymmärtämisen taito", "writingPartialExam": "Kirjoittaminen" }, + "grades": { + "GOOD": "Hyvä", + "SATISFACTORY": "Tyydyttävä", + "FAILED": "Hylätty" + }, "textFields": { "country": "Maa", "email": "Sähköpostiosoite", diff --git a/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetailsFields.tsx b/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetailsFields.tsx index 27b2d7ccc..2e55ae58f 100644 --- a/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetailsFields.tsx +++ b/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetailsFields.tsx @@ -14,7 +14,6 @@ import { H3, InfoText, Text, - valueAsOption, } from 'shared/components'; import { APIResponseStatus, @@ -34,12 +33,17 @@ import { useKoodistoMunicipalitiesTranslation, } from 'configs/i18n'; import { useAppDispatch, useAppSelector } from 'configs/redux'; -import { EnrollmentAppointmentStatus, PaymentStatus } from 'enums/app'; +import { + EnrollmentAppointmentStatus, + ExamGrades, + PaymentStatus, +} from 'enums/app'; import { ClerkEnrollmentTextFieldEnum } from 'enums/clerkEnrollment'; import { ClerkEnrollmentAppointment, ClerkEnrollmentAppointmentGrades, ClerkPayment, + GradedExams, } from 'interfaces/clerkEnrollment'; import { ClerkEnrollmentTextFieldProps } from 'interfaces/clerkEnrollmentTextField'; import { PartialExamsAndSkills } from 'interfaces/common/enrollment'; @@ -105,14 +109,14 @@ const GradeModal = ({ }) => { const translateCommon = useCommonTranslation(); const dispatch = useAppDispatch(); - const exams: Array = [ + const exams: Array = [ 'writingPartialExam', 'readingComprehensionPartialExam', 'speakingPartialExam', 'speechComprehensionPartialExam', ]; const selectedSkills = exams.filter( - (skill: keyof ClerkEnrollmentAppointmentGrades) => skills[skill], + (skill: keyof GradedExams) => skills[skill], ); const { grades, gradesSaveStatus } = useAppSelector( clerkEnrollmentAppointmentSelector, @@ -138,25 +142,38 @@ const GradeModal = ({ }, [gradesSaveStatus, dispatch, closeModal]); const onSetComment = - (exam: keyof ClerkEnrollmentAppointmentGrades) => - (event: ChangeEvent) => + (exam: keyof GradedExams) => (event: ChangeEvent) => setGrades((prev) => ({ ...prev, [exam]: { - ...prev[exam], + grade: prev[exam]?.grade ?? '', comment: event.target.value, }, })); - const onSetGrade = - (exam: keyof ClerkEnrollmentAppointmentGrades) => (grade?: string) => - setGrades((prev) => ({ - ...prev, - [exam]: { - ...prev[exam], - grade, - }, - })); + const onSetGrade = (exam: keyof GradedExams) => (grade?: string) => + setGrades((prev) => ({ + ...prev, + [exam]: { + comment: prev[exam]?.comment ?? '', + grade, + }, + })); + + const gradeValues = [ + { + label: translateCommon(`enrollment.grades.${ExamGrades.GOOD}`), + value: ExamGrades.GOOD, + }, + { + label: translateCommon(`enrollment.grades.${ExamGrades.SATISFACTORY}`), + value: ExamGrades.SATISFACTORY, + }, + { + label: translateCommon(`enrollment.grades.${ExamGrades.FAILED}`), + value: ExamGrades.FAILED, + }, + ]; return ( Osakoe Arvosana Huomautuksia - {selectedSkills.map( - (skill: keyof ClerkEnrollmentAppointmentGrades, index) => ( - - - {translateCommon( - `enrollment.partialExamsAndSkills.${skill}`, - )} - - - - - ), - )} + {selectedSkills.map((skill: keyof GradedExams, index) => ( + + + {translateCommon(`enrollment.partialExamsAndSkills.${skill}`)} + + + + + ))}
(grade && grade !== '' ? grade : '-'); + const renderGrade = (grade: string) => + grade && grade !== '' ? translateCommon(`enrollment.grades.${grade}`) : '-'; + const renderComment = (comment: string) => + comment && comment !== '' ? comment : '-'; const partialExamsRow = (exams: Array) => { return exams.map((exam) => ( @@ -415,7 +431,7 @@ const ClerkEnrollmentSkillsListTable = ({
{renderGrade(grades[exam]?.grade)} - {renderGrade(grades[exam]?.comment)} + {renderComment(grades[exam]?.comment)} )); }; diff --git a/frontend/packages/vkt/src/enums/app.ts b/frontend/packages/vkt/src/enums/app.ts index beb68c500..f87ae7f44 100644 --- a/frontend/packages/vkt/src/enums/app.ts +++ b/frontend/packages/vkt/src/enums/app.ts @@ -105,6 +105,12 @@ export enum ExamEventToggleFilter { Passed = 'passed', } +export enum ExamGrades { + GOOD = 'GOOD', + SATISFACTORY = 'SATISFACTORY', + FAILED = 'FAILED', +} + export enum UIMode { Edit = 'edit', View = 'view', diff --git a/frontend/packages/vkt/src/interfaces/clerkEnrollment.ts b/frontend/packages/vkt/src/interfaces/clerkEnrollment.ts index 9acc8a6e7..a58cbb862 100644 --- a/frontend/packages/vkt/src/interfaces/clerkEnrollment.ts +++ b/frontend/packages/vkt/src/interfaces/clerkEnrollment.ts @@ -9,6 +9,7 @@ import { import { ClerkFreeEnrollmentBasis } from 'interfaces/clerkEducation'; import { CertificateShippingData, + PartialExams, PartialExamsAndSkills, } from 'interfaces/common/enrollment'; import { @@ -126,7 +127,9 @@ interface Grade { comment: string; } -export interface ClerkEnrollmentAppointmentGrades { +export interface GradedExams extends Omit {} + +export interface ClerkEnrollmentAppointmentGrades extends WithVersion { speakingPartialExam: Grade; speechComprehensionPartialExam: Grade; writingPartialExam: Grade; diff --git a/frontend/packages/vkt/src/interfaces/common/enrollment.ts b/frontend/packages/vkt/src/interfaces/common/enrollment.ts index 78b391d42..195fd4037 100644 --- a/frontend/packages/vkt/src/interfaces/common/enrollment.ts +++ b/frontend/packages/vkt/src/interfaces/common/enrollment.ts @@ -1,6 +1,4 @@ -export interface PartialExamsAndSkills { - oralSkill: boolean; - textualSkill: boolean; +export interface PartialExams { understandingSkill: boolean; speakingPartialExam: boolean; speechComprehensionPartialExam: boolean; @@ -8,6 +6,11 @@ export interface PartialExamsAndSkills { readingComprehensionPartialExam: boolean; } +export interface PartialExamsAndSkills extends PartialExams { + oralSkill: boolean; + textualSkill: boolean; +} + export interface CertificateShippingTextFields { street?: string; postalCode?: string; diff --git a/frontend/packages/vkt/src/redux/reducers/clerkEnrollmentAppointment.ts b/frontend/packages/vkt/src/redux/reducers/clerkEnrollmentAppointment.ts index 046483e25..0a6c5a0db 100644 --- a/frontend/packages/vkt/src/redux/reducers/clerkEnrollmentAppointment.ts +++ b/frontend/packages/vkt/src/redux/reducers/clerkEnrollmentAppointment.ts @@ -26,6 +26,7 @@ const initialState: ClerkEnrollmentAppointmentState = { examEventsStatus: APIResponseStatus.NotStarted, examEvents: [], grades: { + version: 0, speakingPartialExam: { grade: '', comment: '', diff --git a/frontend/packages/vkt/src/redux/sagas/clerkEnrollmentAppointment.ts b/frontend/packages/vkt/src/redux/sagas/clerkEnrollmentAppointment.ts index 054cc5c30..b4512aa66 100644 --- a/frontend/packages/vkt/src/redux/sagas/clerkEnrollmentAppointment.ts +++ b/frontend/packages/vkt/src/redux/sagas/clerkEnrollmentAppointment.ts @@ -36,6 +36,7 @@ function* upsertClerkEnrollmentAppointmentGradesSaga( ) { const { enrollment, grades, oid } = action.payload; const nonEmptyGrades = { + version: grades.version ?? 0, speakingPartialExam: grades.speakingPartialExam?.grade !== '' ? grades.speakingPartialExam