From 52414789cdb80ad18f84dc383a0f6e7e56fe106f Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 4 Feb 2025 12:51:06 +0100 Subject: [PATCH 01/15] new approach for validation --- frontend/package.json | 4 +- .../Contributions/ContributionForm.vue | 63 +++++++++---------- frontend/src/components/Inputs/InputHour.vue | 5 +- .../src/components/Inputs/ValidatedInput.vue | 63 +++++++++++++++++++ frontend/src/pages/Community.vue | 2 +- frontend/src/validationSchemas.js | 15 +++++ frontend/yarn.lock | 21 ++++--- 7 files changed, 125 insertions(+), 48 deletions(-) create mode 100644 frontend/src/components/Inputs/ValidatedInput.vue create mode 100644 frontend/src/validationSchemas.js diff --git a/frontend/package.json b/frontend/package.json index 17dd58803..96c2e99fc 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -29,7 +29,7 @@ "@types/leaflet": "^1.9.12", "@vee-validate/i18n": "^4.14.7", "@vee-validate/rules": "^4.14.1", - "@vee-validate/yup": "^4.14.1", + "@vee-validate/yup": "^4.15.0", "@vitejs/plugin-vue": "5.1.4", "@vue-leaflet/vue-leaflet": "^0.10.1", "@vue/apollo-composable": "^4.0.2", @@ -68,7 +68,7 @@ "vue-timer-hook": "^1.0.84", "vuex": "^4.1.0", "vuex-persistedstate": "^4.1.0", - "yup": "^1.4.0" + "yup": "^1.6.1" }, "devDependencies": { "@apollo/client": "^3.10.8", diff --git a/frontend/src/components/Contributions/ContributionForm.vue b/frontend/src/components/Contributions/ContributionForm.vue index 5612ae14b..3098a4dd1 100644 --- a/frontend/src/components/Contributions/ContributionForm.vue +++ b/frontend/src/components/Contributions/ContributionForm.vue @@ -5,26 +5,18 @@ class="form-style p-3 bg-white app-box-shadow gradido-border-radius" @submit.prevent="submit" > - - - - - - + :schema-description="schemaDescription.fields.date" + @update:model-value="updateField" + />
{{ noOpenCreation }}
@@ -39,12 +31,12 @@ @@ -88,12 +80,14 @@ \ No newline at end of file diff --git a/frontend/src/pages/Community.vue b/frontend/src/pages/Community.vue index 6ab7d9875..e728ae6ab 100644 --- a/frontend/src/pages/Community.vue +++ b/frontend/src/pages/Community.vue @@ -94,7 +94,7 @@ const form = ref({ id: null, date: '', memo: '', - hours: 0, + hours: 0.0, amount: '', }) const originalContributionDate = ref('') diff --git a/frontend/src/validationSchemas.js b/frontend/src/validationSchemas.js new file mode 100644 index 000000000..6aa614f67 --- /dev/null +++ b/frontend/src/validationSchemas.js @@ -0,0 +1,15 @@ +import {object, string, date } from 'yup' + +export const createContributionFormValidation = (t) => { + return object({ + // The date field is required and needs to be a valid date + // contribution date + date: + date() + .required(t('contribution.noDateSelected')) + .min((new Date(new Date().setMonth(new Date().getMonth() - 1, 1))).toISOString().slice(0,10)) // min date is first day of last month + .max(new Date().toISOString().slice(0,10)) + .default(''), // date cannot be in the future + memo: string().required(t('')).min(5).max(255) + }) +} diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 7eb244d40..4ebc33d80 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1657,14 +1657,13 @@ dependencies: vee-validate "4.14.7" -"@vee-validate/yup@^4.14.1": - version "4.14.7" - resolved "https://registry.yarnpkg.com/@vee-validate/yup/-/yup-4.14.7.tgz#a029151394ae4fbc7a038dbb49acc86f2ba78ddc" - integrity sha512-sMLkSXbVWIFK0BE8gEp2Gcdd3aqpTggBjbkrYmcdgyHBeYoPmhBHhUpkXDFhmsckie2xv6lNTicGO5oJt71N1Q== +"@vee-validate/yup@^4.15.0": + version "4.15.0" + resolved "https://registry.yarnpkg.com/@vee-validate/yup/-/yup-4.15.0.tgz#409f9b57414fadd5b86bc6ada18cd51a7ccd121c" + integrity sha512-paK2ZdxZJRrUGwqaqf7KMNC+n5C7UGs7DofK7wZCza/zKT/QtFSxVYgopGoYYrbAfd6DpVmNpf/ouBuRdPBthA== dependencies: type-fest "^4.8.3" - vee-validate "4.14.7" - yup "^1.3.2" + vee-validate "4.15.0" "@vitejs/plugin-vue@5.1.4": version "5.1.4" @@ -7133,6 +7132,14 @@ vee-validate@4.14.7, vee-validate@^4.13.2: "@vue/devtools-api" "^7.5.2" type-fest "^4.8.3" +vee-validate@4.15.0: + version "4.15.0" + resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-4.15.0.tgz#eb77a9c867669d34abbc33ca5e16f2a991eb7ad5" + integrity sha512-PGJh1QCFwCBjbHu5aN6vB8macYVWrajbDvgo1Y/8fz9n/RVIkLmZCJDpUgu7+mUmCOPMxeyq7vXUOhbwAqdXcA== + dependencies: + "@vue/devtools-api" "^7.5.2" + type-fest "^4.8.3" + vite-node@2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.8.tgz#9495ca17652f6f7f95ca7c4b568a235e0c8dbac5" @@ -7518,7 +7525,7 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -yup@^1.3.2, yup@^1.4.0: +yup@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/yup/-/yup-1.6.1.tgz#8defcff9daaf9feac178029c0e13b616563ada4b" integrity sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA== From 851db7e8c5f0e8f1f2ae6a02cbec83e08a647046 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Tue, 4 Feb 2025 13:02:10 +0100 Subject: [PATCH 02/15] lint fix --- .../Contributions/ContributionForm.vue | 6 +++--- .../src/components/Inputs/ValidatedInput.vue | 21 +++++++------------ frontend/src/validationSchemas.js | 11 +++++----- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/frontend/src/components/Contributions/ContributionForm.vue b/frontend/src/components/Contributions/ContributionForm.vue index 3098a4dd1..5c00cd54a 100644 --- a/frontend/src/components/Contributions/ContributionForm.vue +++ b/frontend/src/components/Contributions/ContributionForm.vue @@ -119,13 +119,13 @@ const { hours: props.modelValue.hours, amount: props.modelValue.amount, }, - validationSchema + validationSchema, }) const { meta: dataFieldMeta } = useField('date') const updateField = (newValue, name) => { - if(typeof name === 'string' && name.length) { + if (typeof name === 'string' && name.length) { setFieldValue(name, newValue) } } @@ -173,7 +173,7 @@ watch( () => formValues.hours, () => { updateAmount(formValues.hours) - } + }, ) function updateAmount(hours) { diff --git a/frontend/src/components/Inputs/ValidatedInput.vue b/frontend/src/components/Inputs/ValidatedInput.vue index 305f4b8f3..9be51b9c1 100644 --- a/frontend/src/components/Inputs/ValidatedInput.vue +++ b/frontend/src/components/Inputs/ValidatedInput.vue @@ -19,10 +19,10 @@ \ No newline at end of file + diff --git a/frontend/src/validationSchemas.js b/frontend/src/validationSchemas.js index 6aa614f67..ae09a16a8 100644 --- a/frontend/src/validationSchemas.js +++ b/frontend/src/validationSchemas.js @@ -1,15 +1,14 @@ -import {object, string, date } from 'yup' +import { object, string, date } from 'yup' export const createContributionFormValidation = (t) => { return object({ // The date field is required and needs to be a valid date // contribution date - date: - date() + date: date() .required(t('contribution.noDateSelected')) - .min((new Date(new Date().setMonth(new Date().getMonth() - 1, 1))).toISOString().slice(0,10)) // min date is first day of last month - .max(new Date().toISOString().slice(0,10)) + .min(new Date(new Date().setMonth(new Date().getMonth() - 1, 1)).toISOString().slice(0, 10)) // min date is first day of last month + .max(new Date().toISOString().slice(0, 10)) .default(''), // date cannot be in the future - memo: string().required(t('')).min(5).max(255) + memo: string().required(t('')).min(5).max(255), }) } From 6bbb97048ea4cf06e0be547e8ac5e6a960de4f90 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Thu, 6 Feb 2025 16:18:32 +0100 Subject: [PATCH 03/15] try modularizing validated form input(s) --- .../Contributions/ContributionForm.vue | 102 +++++++++--------- .../src/components/Inputs/InputTextarea.vue | 16 ++- .../src/components/Inputs/LabeledInput.vue | 32 ++++++ .../src/components/Inputs/ValidatedInput.vue | 87 ++++++++------- frontend/src/locales/de.json | 11 +- frontend/src/locales/en.json | 5 + frontend/src/validationSchemas.js | 22 ++-- 7 files changed, 174 insertions(+), 101 deletions(-) create mode 100644 frontend/src/components/Inputs/LabeledInput.vue diff --git a/frontend/src/components/Contributions/ContributionForm.vue b/frontend/src/components/Contributions/ContributionForm.vue index 5c00cd54a..84eb7f31c 100644 --- a/frontend/src/components/Contributions/ContributionForm.vue +++ b/frontend/src/components/Contributions/ContributionForm.vue @@ -9,12 +9,11 @@ id="contribution-date" :model-value="formValues.date" name="date" - :state="dataFieldMeta.valid" :label="$t('contribution.selectDate')" :no-flip="true" class="mb-4 bg-248" type="date" - :schema-description="schemaDescription.fields.date" + :rules="validationSchema" @update:model-value="updateField" />
@@ -23,33 +22,33 @@
- - - diff --git a/frontend/src/components/Inputs/InputTextarea.vue b/frontend/src/components/Inputs/InputTextarea.vue index 2d1d2a12e..41e649265 100644 --- a/frontend/src/components/Inputs/InputTextarea.vue +++ b/frontend/src/components/Inputs/InputTextarea.vue @@ -16,7 +16,7 @@ @update:modelValue="currentValue = $event" /> - {{ errorMessage }} + {{ translatedErrorString }}
@@ -25,6 +25,8 @@ diff --git a/frontend/src/components/Inputs/LabeledInput.vue b/frontend/src/components/Inputs/LabeledInput.vue new file mode 100644 index 000000000..7fd4a9efe --- /dev/null +++ b/frontend/src/components/Inputs/LabeledInput.vue @@ -0,0 +1,32 @@ + + + diff --git a/frontend/src/components/Inputs/ValidatedInput.vue b/frontend/src/components/Inputs/ValidatedInput.vue index 9be51b9c1..50d869529 100644 --- a/frontend/src/components/Inputs/ValidatedInput.vue +++ b/frontend/src/components/Inputs/ValidatedInput.vue @@ -1,58 +1,73 @@ diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index dd34fbad4..0c71a5407 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -77,6 +77,7 @@ "myContributions": "Du hast noch keine Beiträge eingereicht." }, "noDateSelected": "Wähle irgendein Datum im Monat", + "noHours": "Bitte trage deine Stunden ein", "noOpenCreation": { "allMonth": "Für alle beiden Monate ist dein Schöpfungslimit erreicht. Den Nächsten Monat kannst du wieder 1000 GDD Schöpfen.", "lastMonth": "Für den ausgewählten Monat ist das Schöpfungslimit erreicht.", @@ -185,9 +186,17 @@ "username": "Benutzername", "username-placeholder": "Wähle deinen Benutzernamen", "validation": { - "gddCreationTime": "Das Feld {_field_} muss eine Zahl zwischen {min} und {max} mit höchstens einer Nachkommastelle sein", + "gddCreationTime": { + "min": "Die Stunden sollten mindestens {min} groß sein", + "max": "Die Stunden sollten höchsten {max} groß sein", + "decimal-places": "Die Stunden sollten maximal zwei Nachkommastellen enthalten" + }, "gddSendAmount": "Das Feld {_field_} muss eine Zahl zwischen {min} und {max} mit höchstens zwei Nachkommastellen sein", "is-not": "Du kannst dir selbst keine Gradidos überweisen", + "memo": { + "min": "Die Tätigkeitsbeschreibung sollte mindestens {min} Zeichen lang sein", + "max": "Die Tätigkeitsbeschreibung sollte höchstens {max} Zeichen lang sein" + }, "requiredField": "{fieldName} ist ein Pflichtfeld", "username-allowed-chars": "Der Nutzername darf nur aus Buchstaben (ohne Umlaute), Zahlen, Binde- oder Unterstrichen bestehen.", "username-hyphens": "Binde- oder Unterstriche müssen zwischen Buchstaben oder Zahlen stehen.", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 9346ea609..8804c7fe1 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -77,6 +77,7 @@ "myContributions": "You have not submitted any entries yet." }, "noDateSelected": "Choose any date in the month", + "noHours": "Please enter your hours", "noOpenCreation": { "allMonth": "For all two months your creation limit is reached. The next month you can create 1000 GDD again.", "lastMonth": "The creation limit is reached for the selected month.", @@ -188,6 +189,10 @@ "gddCreationTime": "The field {_field_} must be a number between {min} and {max} with at most one decimal place.", "gddSendAmount": "The {_field_} field must be a number between {min} and {max} with at most two digits after the decimal point", "is-not": "You cannot send Gradidos to yourself", + "memo": { + "min": "The job description should be at least {min} characters long", + "max": "The job description should not be longer than {max} characters" + }, "requiredField": "The {fieldName} field is required", "username-allowed-chars": "The username may only contain letters, numbers, hyphens or underscores.", "username-hyphens": "Hyphens or underscores must be in between letters or numbers.", diff --git a/frontend/src/validationSchemas.js b/frontend/src/validationSchemas.js index ae09a16a8..fe3ef901e 100644 --- a/frontend/src/validationSchemas.js +++ b/frontend/src/validationSchemas.js @@ -1,14 +1,10 @@ -import { object, string, date } from 'yup' +import { string } from 'yup' + +// TODO: only needed for grace period, before all inputs updated for using veeValidate + yup +export const isLanguageKey = (str) => str.match(/^(?!\.)[a-z][a-zA-Z0-9]*([.][a-z][a-zA-Z0-9]*)*(? ({ key: 'form.validation.memo.min', values: { min } })) + .max(255, ({max}) => ({ key: 'form.validation.memo.max', values: { max } })) -export const createContributionFormValidation = (t) => { - return object({ - // The date field is required and needs to be a valid date - // contribution date - date: date() - .required(t('contribution.noDateSelected')) - .min(new Date(new Date().setMonth(new Date().getMonth() - 1, 1)).toISOString().slice(0, 10)) // min date is first day of last month - .max(new Date().toISOString().slice(0, 10)) - .default(''), // date cannot be in the future - memo: string().required(t('')).min(5).max(255), - }) -} From a41829fe046e6d4a9d56c4dbc8e17ce4a3d9c78e Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Fri, 7 Feb 2025 08:16:07 +0100 Subject: [PATCH 04/15] rollback --- frontend/src/pages/Community.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/Community.vue b/frontend/src/pages/Community.vue index e728ae6ab..6ab7d9875 100644 --- a/frontend/src/pages/Community.vue +++ b/frontend/src/pages/Community.vue @@ -94,7 +94,7 @@ const form = ref({ id: null, date: '', memo: '', - hours: 0.0, + hours: 0, amount: '', }) const originalContributionDate = ref('') From 4f1ea1f5ba48be0d1b6fdbe6a898f4884297c804 Mon Sep 17 00:00:00 2001 From: Einhornimmond Date: Sat, 8 Feb 2025 13:24:09 +0100 Subject: [PATCH 05/15] change to using only yup --- .../Contributions/ContributionForm.vue | 16 +++++++- .../src/components/Inputs/InputTextarea.vue | 2 +- .../src/components/Inputs/ValidatedInput.vue | 37 ++++++++++++++++--- 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/Contributions/ContributionForm.vue b/frontend/src/components/Contributions/ContributionForm.vue index 84eb7f31c..37c7b72ec 100644 --- a/frontend/src/components/Contributions/ContributionForm.vue +++ b/frontend/src/components/Contributions/ContributionForm.vue @@ -99,6 +99,7 @@ const props = defineProps({ const emit = defineEmits(['update-contribution', 'set-contribution']) const { t } = useI18n() +let allFieldsValid = ref(false) const form = ref({ ...props.modelValue }) @@ -152,6 +153,17 @@ const updateField = (newValue, name) => { if (name === 'hours') { setFieldValue('amount', (newValue * 20).toFixed(2).toString()) } + /* + validationSchema.value.validateAt(name, formValues) + .then(() => { + allFieldsValid = true + }) + .catch((e) => { + allFieldsValid = false + console.log('validation error') + console.log(JSON.stringify(e, null, 2)) + //errorMessage = e.message + })*/ } const showMessage = computed(() => { @@ -165,11 +177,13 @@ const showMessage = computed(() => { }) const disabled = computed(() => { + return !allFieldsValid + /* return ( !formMeta.value.valid || (props.isThisMonth && parseFloat(form.value.amount) > parseFloat(props.maxGddThisMonth)) || (!props.isThisMonth && parseFloat(form.value.amount) > parseFloat(props.maxGddLastMonth)) - ) + )*/ }) const noOpenCreation = computed(() => { diff --git a/frontend/src/components/Inputs/InputTextarea.vue b/frontend/src/components/Inputs/InputTextarea.vue index 41e649265..a30a6510a 100644 --- a/frontend/src/components/Inputs/InputTextarea.vue +++ b/frontend/src/components/Inputs/InputTextarea.vue @@ -54,7 +54,7 @@ const props = defineProps({ const { value: currentValue, errorMessage, meta } = useField(props.name, props.rules) const { t } = useI18n() const translatedErrorString = computed(() => { - console.log(errorMessage) + // console.log(errorMessage) if (typeof errorMessage.value === 'object') { return t(errorMessage.value.key, errorMessage.value.values) } else if (isLanguageKey(errorMessage.value)) { diff --git a/frontend/src/components/Inputs/ValidatedInput.vue b/frontend/src/components/Inputs/ValidatedInput.vue index 50d869529..3e25bda6e 100644 --- a/frontend/src/components/Inputs/ValidatedInput.vue +++ b/frontend/src/components/Inputs/ValidatedInput.vue @@ -9,7 +9,7 @@ :required="!isOptional" :label="props.label" :name="props.name" - :state="meta.valid" + :state="valid" @input="updateValue($event.target.value)"> {{ $t(translatedErrorString) }} @@ -18,7 +18,7 @@ From 772901e073318a1c25218c07e206aa5bee075619 Mon Sep 17 00:00:00 2001 From: einhornimmond Date: Wed, 12 Feb 2025 10:46:47 +0100 Subject: [PATCH 06/15] finally a working solution --- .../Contributions/ContributionForm.vue | 157 ++++++++---------- .../src/components/Inputs/LabeledInput.vue | 39 ++++- .../src/components/Inputs/ValidatedInput.vue | 98 +++++------ frontend/src/pages/Community.vue | 21 +-- frontend/src/validationSchemas.js | 13 +- 5 files changed, 163 insertions(+), 165 deletions(-) diff --git a/frontend/src/components/Contributions/ContributionForm.vue b/frontend/src/components/Contributions/ContributionForm.vue index 37c7b72ec..6b36c29fe 100644 --- a/frontend/src/components/Contributions/ContributionForm.vue +++ b/frontend/src/components/Contributions/ContributionForm.vue @@ -7,40 +7,42 @@ > -
+
{{ noOpenCreation }}
-