From 5ec9924af70981765770b354afffec460695c651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= Date: Fri, 13 Sep 2024 11:36:25 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9E=95=20Frontend=20validation=20for=20sched?= =?UTF-8?q?ule=20creation/modification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/ScheduleCreation.vue | 26 +++++++++++++++++++- frontend/src/locales/de.json | 4 +++ frontend/src/locales/en.json | 4 +++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/ScheduleCreation.vue b/frontend/src/components/ScheduleCreation.vue index 2636803c..8a5bb53e 100644 --- a/frontend/src/components/ScheduleCreation.vue +++ b/frontend/src/components/ScheduleCreation.vue @@ -12,7 +12,7 @@ import { import { Dayjs } from 'dayjs'; import { useI18n } from 'vue-i18n'; import { useUserStore } from '@/stores/user-store'; -import { dayjsKey, callKey, isoWeekdaysKey } from '@/keys'; +import { dayjsKey, callKey, isoWeekdaysKey, hasProfanityKey } from '@/keys'; import AppointmentCreatedModal from '@/components/AppointmentCreatedModal.vue'; import PrimaryButton from '@/tbpro/elements/PrimaryButton.vue'; @@ -46,6 +46,7 @@ const { t } = useI18n(); const dj = inject(dayjsKey); const call = inject(callKey); const isoWeekdays = inject(isoWeekdaysKey); +const hasProfanity = inject(hasProfanityKey); const dateFormat = DateFormatStrings.QalendarFullDay; const firstStep = ScheduleCreationState.Availability; @@ -253,6 +254,20 @@ const revertForm = (resetData = true) => { } }; +// Form validation +const scheduleValidationError = (schedule: Schedule): string|null => { + // Schedule name is empty + if (schedule.name === '') { + return t('validation.fieldIsRequired', { field: t('ftue.scheduleName') }); + } + // Schedule name contains profanity + if (hasProfanity(schedule.name)) { + return t('validation.fieldContainsProfanity', { field: t('ftue.scheduleName') }); + } + // All good + return null; +}; + // handle actual schedule creation/update const savingInProgress = ref(false); const saveSchedule = async (withConfirmation = true) => { @@ -275,6 +290,15 @@ const saveSchedule = async (withConfirmation = true) => { delete obj.time_updated; delete obj.id; + // validate schedule data + const validationError = scheduleValidationError(obj); + if (validationError) { + scheduleCreationError.value = validationError; + savingInProgress.value = false; + window.scrollTo(0, 0); + return; + } + // save schedule data const response = props.schedule ? await scheduleStore.updateSchedule(call, props.schedule.id, obj) diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 079b33e6..4200a775 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -455,6 +455,10 @@ "minutes": "{value} Minuten", "none": "{value}" }, + "validation": { + "fieldContainsProfanity": "Bitte verwende keine beleidigenden Ausdrücke (entdeckt in {field}).", + "fieldIsRequired": "{field} darf nicht leer sein." + }, "waitingList": { "adminInviteNotice": "Hinweis: Der Senden-Button wird bereits akzeptierte Personen nicht erneut einladen, sie können aber dennoch ausgewählt werden. Verwende die Filter für eine bessere Übersicht!", "confirmHeading": "Du bist auf der Warteliste", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 05054e94..44e249d3 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -456,6 +456,10 @@ "minutes": "{value} minutes", "none": "{value}" }, + "validation": { + "fieldContainsProfanity": "Please don't use offensive language (detected in {field}).", + "fieldIsRequired": "{field} cannot be empty." + }, "waitingList": { "adminInviteNotice": "Notice: The Send button will not re-invite people already accepted, but you can still select them. Use the filters for clarity!", "confirmHeading": "You’re on the waitlist",