From 43738b2768c7eb4631781dbc55182953a5badfd7 Mon Sep 17 00:00:00 2001 From: Joonatan Kuosa Date: Tue, 30 Apr 2024 10:33:26 +0300 Subject: [PATCH] fix: edit missing interval validations --- apps/admin-ui/src/i18n/messages.ts | 6 ++ apps/admin-ui/src/schemas/utils.ts | 18 ++++- .../src/spa/ReservationUnit/edit/form.ts | 66 ++++++++++++++++--- .../src/spa/ReservationUnit/edit/index.tsx | 29 +++++++- 4 files changed, 105 insertions(+), 14 deletions(-) diff --git a/apps/admin-ui/src/i18n/messages.ts b/apps/admin-ui/src/i18n/messages.ts index 96f6c27df0..e90626b7c7 100644 --- a/apps/admin-ui/src/i18n/messages.ts +++ b/apps/admin-ui/src/i18n/messages.ts @@ -816,6 +816,12 @@ const translations: ITranslations = { "reservationBeginsDate must be before end": [ "Varausajan tulee alkaa ennen kuin se päättyy.", ], + "duration can't be less than reservation start interval": [ + "Kesto ei voi olla pienempi kuin varauksen alkamisväli.", + ], + "duration must be a multiple of the reservation start interval": [ + "Keston on oltava varauksen alkamisvälin kerrannainen", + ], }, }, level: { diff --git a/apps/admin-ui/src/schemas/utils.ts b/apps/admin-ui/src/schemas/utils.ts index 05b0764bb9..c2d62d6a2e 100644 --- a/apps/admin-ui/src/schemas/utils.ts +++ b/apps/admin-ui/src/schemas/utils.ts @@ -1,6 +1,6 @@ import { ReservationStartInterval } from "common/types/gql-types"; -export const intervalToNumber = (i: ReservationStartInterval) => { +export function intervalToNumber(i: ReservationStartInterval) { switch (i) { case ReservationStartInterval.Interval_15Mins: return 15; @@ -10,7 +10,19 @@ export const intervalToNumber = (i: ReservationStartInterval) => { return 60; case ReservationStartInterval.Interval_90Mins: return 90; + case ReservationStartInterval.Interval_120Mins: + return 120; + case ReservationStartInterval.Interval_180Mins: + return 180; + case ReservationStartInterval.Interval_240Mins: + return 240; + case ReservationStartInterval.Interval_300Mins: + return 300; + case ReservationStartInterval.Interval_360Mins: + return 360; + case ReservationStartInterval.Interval_420Mins: + return 420; default: - return 0; + throw new Error(`Unknown interval: ${i}`); } -}; +} diff --git a/apps/admin-ui/src/spa/ReservationUnit/edit/form.ts b/apps/admin-ui/src/spa/ReservationUnit/edit/form.ts index 041e1fcae2..bf6decff04 100644 --- a/apps/admin-ui/src/spa/ReservationUnit/edit/form.ts +++ b/apps/admin-ui/src/spa/ReservationUnit/edit/form.ts @@ -24,6 +24,7 @@ import { checkTimeStringFormat, } from "common/src/schemas/schemaCommon"; import { constructApiDate } from "@/helpers"; +import { intervalToNumber } from "@/schemas/utils"; export const PaymentTypes = ["ONLINE", "INVOICE", "ON_SITE"] as const; @@ -442,6 +443,61 @@ export const ReservationUnitEditSchema = z validateSeasonalTimes(v.seasons, ctx); } + // Drafts require this validation + if (v.minReservationDuration != null && v.maxReservationDuration != null) { + if (v.minReservationDuration > v.maxReservationDuration) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Min reservation duration must be less than max duration", + path: ["maxReservationDuration"], + }); + } + } + + if (v.minReservationDuration != null) { + const minDurationMinutes = Math.floor(v.minReservationDuration / 60); + if (minDurationMinutes < intervalToNumber(v.reservationStartInterval)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "duration can't be less than reservation start interval", + path: ["minReservationDuration"], + }); + } + if ( + minDurationMinutes % intervalToNumber(v.reservationStartInterval) !== + 0 + ) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: + "duration must be a multiple of the reservation start interval", + path: ["minReservationDuration"], + }); + } + } + + if (v.maxReservationDuration != null) { + const maxDurationMinutes = Math.floor(v.maxReservationDuration / 60); + if ( + maxDurationMinutes % intervalToNumber(v.reservationStartInterval) !== + 0 + ) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: + "duration must be a multiple of the reservation start interval", + path: ["maxReservationDuration"], + }); + } + if (maxDurationMinutes < intervalToNumber(v.reservationStartInterval)) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "duration can't be less than reservation start interval", + path: ["maxReservationDuration"], + }); + } + } + if (v.isDraft) { return; } @@ -498,16 +554,6 @@ export const ReservationUnitEditSchema = z } } - if (v.minReservationDuration != null && v.maxReservationDuration != null) { - if (v.minReservationDuration > v.maxReservationDuration) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "Min reservation duration must be less than max duration", - path: ["maxReservationDuration"], - }); - } - } - if (v.hasScheduledPublish) { validateDateTimeInterval({ beginDate: v.hasPublishBegins ? v.publishBeginsDate : "", diff --git a/apps/admin-ui/src/spa/ReservationUnit/edit/index.tsx b/apps/admin-ui/src/spa/ReservationUnit/edit/index.tsx index d019021f33..c79ca9f58f 100644 --- a/apps/admin-ui/src/spa/ReservationUnit/edit/index.tsx +++ b/apps/admin-ui/src/spa/ReservationUnit/edit/index.tsx @@ -2021,7 +2021,34 @@ const ReservationUnitEditor = ({ refetch(); return upPk; } catch (error) { - notifyError(t("ReservationUnitEditor.saveFailed", { error })); + if ( + error != null && + typeof error === "object" && + "graphQLErrors" in error + ) { + const { graphQLErrors } = error; + if (Array.isArray(graphQLErrors) && graphQLErrors.length > 0) { + if ("extensions" in graphQLErrors[0]) { + const { extensions } = graphQLErrors[0]; + if ("errors" in extensions) { + const { errors } = extensions; + if (Array.isArray(errors) && errors.length > 0) { + let str = ""; + for (const e of errors) { + if ("message" in e) { + str += `${e.message}\n`; + } + } + notifyError( + t("ReservationUnitEditor.saveFailed", { error: str }) + ); + return undefined; + } + } + } + } + } + notifyError(t("ReservationUnitEditor.saveFailed", { error: "" })); } return undefined; };