diff --git a/src/components/base/BaseInput.vue b/src/components/base/BaseInput.vue index 70b157b..8f57aa5 100644 --- a/src/components/base/BaseInput.vue +++ b/src/components/base/BaseInput.vue @@ -11,6 +11,7 @@ :type="type", :readonly="readonly", :disable="disabled", + :name="name" :filled="filled", :bg-color="filled || standout ? '' : 'white'", :rules="rule", @@ -88,6 +89,7 @@ export default { label: String, readonly: Boolean, iconLeft: Boolean, + name: String, }, emits: ["update:modelValue"], computed: { diff --git a/src/pages/newMedicalCard/components/AllergiesForms/AllergiesForm.vue b/src/pages/newMedicalCard/components/AllergiesForms/AllergiesForm.vue index 74a5db2..2d98d19 100644 --- a/src/pages/newMedicalCard/components/AllergiesForms/AllergiesForm.vue +++ b/src/pages/newMedicalCard/components/AllergiesForms/AllergiesForm.vue @@ -8,15 +8,21 @@ :cancel="cancelEdit" :open-edit="openEdit" ) - .flex.flex-col.gap-19px.w-full + q-form.flex.flex-col.gap-19px.w-full(ref="formAllergies") .flex.flex-col.gap-4.w-full - .flex.gap-4.items-center.w-full.justify-between(v-for="(allergy, index) in allergies") - .label-field.font-sm.text-sm.whitespace-nowrap {{ `Аллерген ${index+1}:` }} - .flex.gap-2.w-full.items-center + .flex.items-center.w-full.justify-between(v-for="(allergy, index) in allergies") + .w-32.justify-start + .label-field.font-sm.text-sm.whitespace-nowrap {{ `Аллерген ${index+1}:` }} + .flex.min-w-64.gap-2.w-full.items-center base-input( :disabled="!isEdit" v-model="allergy.name" @update:model-value="checkChangeInput" + name="name" + :rule="configData.name.rules" + no-error-icon + :width="278" + placeholder="Аллерген" type="text" :standout="!isEdit" :outlined="isEdit" @@ -26,6 +32,10 @@ :disabled="!isEdit" v-model="allergy.title" @update:model-value="checkChangeInput" + name="title" + :rule="configData.title.rules" + no-error-icon + placeholder="Проявление аллергии" type="text" :standout="!isEdit" :outlined="isEdit" @@ -49,18 +59,28 @@ import MedicalFormWrapper from "@/pages/newMedicalCard/components/MedicalFormWrapper.vue"; import BaseButton from "@/components/base/BaseButton.vue"; import BaseInput from "@/components/base/BaseInput.vue"; +import TheNotificationProvider from "@/components/Notifications/TheNotificationProvider.vue"; +import { getFieldsNameUnvalidated } from "@/shared/utils/changesObjects"; +import { addNotification } from "@/components/Notifications/notificationContext"; import { checkChangeData } from "@/shared/utils/changesObjects"; +import { allergiesConfig } from "@/pages/newMedicalCard/utils/medicalConfig"; import { mapState, mapGetters } from "vuex"; import { getRequestArrayData } from "@/shared/utils/wrapperRequestChangeData"; export default { name: "AllergiesForm", - components: { MedicalFormWrapper, BaseButton, BaseInput }, + components: { + MedicalFormWrapper, + BaseButton, + BaseInput, + TheNotificationProvider, + }, data() { return { isEdit: false, isLoadingData: false, isCheckChange: false, + configData: allergiesConfig, }; }, computed: { @@ -87,14 +107,29 @@ export default { ); }, saveChange() { - this.isLoadingData = true; - Promise.allSettled(this.updateAllergies()) - .then(() => this.$store.dispatch("getMedicalCardData")) - .then(() => { - this.isLoadingData = false; - this.isEdit = false; - this.isCheckChange = false; - }); + const form = this.$refs.formAllergies; + form.validate().then((res) => { + if (res) { + this.isLoadingData = true; + Promise.allSettled(this.updateAllergies()) + .then(() => this.$store.dispatch("getMedicalCardData")) + .then(() => { + this.isLoadingData = false; + this.isEdit = false; + this.isCheckChange = false; + }); + } else { + getFieldsNameUnvalidated(form).forEach((errorKey) => { + addNotification( + this.configData[errorKey].title, + this.configData[errorKey].title, + this.configData[errorKey].error, + "error", + 5000 + ); + }); + } + }); }, cancelEdit() { this.$store.dispatch("returnInitData"); diff --git a/src/pages/newMedicalCard/components/BasicDataForms/BasicDataForm.vue b/src/pages/newMedicalCard/components/BasicDataForms/BasicDataForm.vue index 5a2630f..d188775 100644 --- a/src/pages/newMedicalCard/components/BasicDataForms/BasicDataForm.vue +++ b/src/pages/newMedicalCard/components/BasicDataForms/BasicDataForm.vue @@ -8,7 +8,7 @@ :cancel="cancelEdit" :open-edit="openEdit" ) - .form-wrap.gap-24.w-full + q-form.form-wrap.gap-24.w-full(ref="formBasicData") .flex.flex-col.gap-4(v-for="data in configData") .font-semibold.text-sm.whitespace-nowrap {{data.dataLabel}} .flex.w-full.justify-between.items-center.gap-4(v-for="field in data.fields") @@ -43,10 +43,12 @@ :disabled="!isEdit" v-model="basic[data.dataKey][field.key]" @update:model-value="checkChangeInput" - :type="field.type" + :type="field.type" + :name="field.key" :width="278" :standout="!isEdit" - :outlined="isEdit" + :outlined="isEdit" + no-error-icon :rule="field.rules" text-color="black" ) @@ -59,6 +61,9 @@ import BaseAvatar from "@/components/base/BaseAvatar.vue"; import BaseModal from "@/components/base/BaseModal.vue"; import BaseUploadPhoto from "@/components/base/BaseUploadPhoto.vue"; import MedicalFormWrapper from "@/pages/newMedicalCard/components/MedicalFormWrapper.vue"; +import TheNotificationProvider from "@/components/Notifications/TheNotificationProvider.vue"; +import { getFieldsNameUnvalidated } from "@/shared/utils/changesObjects"; +import { addNotification } from "@/components/Notifications/notificationContext"; import { getRequestChangeData } from "@/shared/utils/wrapperRequestChangeData"; import { checkChangeData } from "@/shared/utils/changesObjects"; import { fetchWrapper } from "@/shared/fetchWrapper"; @@ -66,6 +71,7 @@ import moment from "moment"; import { baseDataForm, genderOptions, + errorMap, } from "@/pages/newMedicalCard/utils/medicalConfig.js"; import { mapGetters, mapState } from "vuex"; @@ -78,6 +84,7 @@ export default { BaseSelect, BaseModal, BaseUploadPhoto, + TheNotificationProvider, }, data() { return { @@ -87,6 +94,7 @@ export default { isCheckChange: false, genderOptions: genderOptions, configData: baseDataForm, + errorMapBasic: errorMap, }; }, computed: { @@ -176,15 +184,30 @@ export default { return request; }, saveChange() { - this.isLoadingData = true; - let updateBasic = this.updateBasicData(); - Promise.allSettled(updateBasic) - .then(() => this.$store.dispatch("getMedicalCardData")) - .then(() => { - this.isLoadingData = false; - this.isEdit = false; - this.isCheckChange = false; - }); + const form = this.$refs.formBasicData; + form.validate().then((validate) => { + if (validate) { + this.isLoadingData = true; + let updateBasic = this.updateBasicData(); + Promise.allSettled(updateBasic) + .then(() => this.$store.dispatch("getMedicalCardData")) + .then(() => { + this.isLoadingData = false; + this.isEdit = false; + this.isCheckChange = false; + }); + } else { + getFieldsNameUnvalidated(form).forEach((errorKey) => { + addNotification( + this.errorMapBasic[errorKey].title, + this.errorMapBasic[errorKey].title, + this.errorMapBasic[errorKey].error, + "error", + 5000 + ); + }); + } + }); }, openEdit() { this.isEdit = true; diff --git a/src/pages/newMedicalCard/components/BasicDataForms/ContactsForm.vue b/src/pages/newMedicalCard/components/BasicDataForms/ContactsForm.vue index d0f61c7..b2a10c7 100644 --- a/src/pages/newMedicalCard/components/BasicDataForms/ContactsForm.vue +++ b/src/pages/newMedicalCard/components/BasicDataForms/ContactsForm.vue @@ -31,10 +31,11 @@ :size-input="50" ) base-input.w-full( - :disabled="!isEdit" v-model="data.username" - :mask="configData[key].inputMask" - type="text" + type="text" + :name="key" + no-error-icon + :rule="configData[key].rules" :standout="!isEdit" :outlined="isEdit" text-color="black" @@ -43,8 +44,10 @@ v-else :disabled="!isEdit" v-model="data.username" + :name="key" :mask="configData[key].inputMask" :rule="configData[key].rules" + no-error-icon @update:model-value="checkChangeInput" type="text" :standout="!isEdit" @@ -71,14 +74,25 @@ import BaseInput from "@/components/base/BaseInput.vue"; import BaseButton from "@/components/base/BaseButton.vue"; import BaseSelectNetworks from "@/components/base/BaseSelectNetworks.vue"; import { mapNetworks } from "@/pages/newMedicalCard/utils/medicalConfig"; -import { checkChangeData } from "@/shared/utils/changesObjects"; +import TheNotificationProvider from "@/components/Notifications/TheNotificationProvider.vue"; +import { addNotification } from "@/components/Notifications/notificationContext"; +import { + checkChangeData, + getFieldsNameUnvalidated, +} from "@/shared/utils/changesObjects"; import { contactsDataForm } from "@/pages/newMedicalCard/utils/medicalConfig"; import { getRequestArrayData } from "@/shared/utils/wrapperRequestChangeData"; import { mapState, mapGetters } from "vuex"; export default { name: "ContactsForm", - components: { MedicalFormWrapper, BaseInput, BaseSelectNetworks, BaseButton }, + components: { + MedicalFormWrapper, + BaseInput, + BaseSelectNetworks, + BaseButton, + TheNotificationProvider, + }, data() { return { isEdit: false, @@ -145,6 +159,16 @@ export default { this.isEdit = false; this.isCheckChange = false; }); + } else { + getFieldsNameUnvalidated(form).forEach((errorKey) => { + addNotification( + this.configData[errorKey].title, + this.configData[errorKey].title, + this.configData[errorKey].error, + "error", + 5000 + ); + }); } }); }, @@ -161,12 +185,12 @@ export default { if (key === "networks") { this.contacts[key].push({ filled: false, - kind: null, + kind: this.configData[key]?.kind, username: null, }); } else { this.contacts[key].push({ - kind: this.configData[key].kind, + kind: this.configData[key]?.kind, username: null, }); } @@ -190,7 +214,7 @@ export default { &.new color: var(--icon-violet-color) .input-wrapper - width: 324px + width: 302px .form-wrap display: grid grid-template-columns: repeat(3, auto) diff --git a/src/pages/newMedicalCard/utils/medicalConfig.js b/src/pages/newMedicalCard/utils/medicalConfig.js index 80c43bf..28fb543 100644 --- a/src/pages/newMedicalCard/utils/medicalConfig.js +++ b/src/pages/newMedicalCard/utils/medicalConfig.js @@ -1,4 +1,9 @@ -import { ruleLengthValue, ruleEmailValue } from "@/shared/utils/rulesInputs"; +import { + ruleLengthValue, + ruleEmailValue, + ruleMaxLength, + ruleNotValue, +} from "@/shared/utils/rulesInputs"; export const baseDataForm = [ { @@ -61,12 +66,16 @@ export const baseDataForm = [ label: "Дом", type: "text", rules: [(val) => !/^[\W0]/.test(val)], + error: + "Номер дома должен начинатся не с нуля и только с цифробуквенного символа", }, { key: "apartment_number", label: "Квартира", type: "text", rules: [(val) => !/^[\W0]/.test(val)], + error: + "Номер квартиры должен начинатся не с нуля и только с цифробуквенного символа", }, ], }, @@ -94,17 +103,34 @@ export const baseDataForm = [ label: "Дом", type: "text", rules: [(val) => !/^[\W0]/.test(val)], + error: + "Номер дома должен начинатся не с нуля и только с цифробуквенного символа", }, { key: "apartment_number", label: "Квартира", type: "text", rules: [(val) => !/^[\W0]/.test(val)], + error: + "Номер квартиры должен начинатся не с нуля и только с цифробуквенного символа", }, ], }, ]; +export const errorMap = { + house_number: { + title: "Дом", + error: + "Номер дома должен начинатся не с нуля и только с цифробуквенного символа", + }, + apartment_number: { + title: "Квартира", + error: + "Номер квартиры должен начинатся не с нуля и только с цифробуквенного символа", + }, +}; + export const mapNetworks = { VK: { title: "VK", @@ -148,6 +174,8 @@ export const contactsDataForm = { phones: { title: "Телефон", fieldName: "Телефон", + error: + "Не корректно введен номер телефона. Номер телефона должен состоять из 10 цифр (без учета +7)", kind: "PHONE", rules: [(val) => ruleLengthValue(val, 18)], inputMask: "+7 (###) ###-##-##", @@ -155,12 +183,31 @@ export const contactsDataForm = { emails: { title: "Почта", fieldName: "Email", + error: + "Не корректно введен адрес почты. Почта должна содержать локальное имя, знак @ и имя домена", rules: [(val) => ruleEmailValue(val)], kind: "EMAIL", }, networks: { title: "Социальные сети", fieldName: "Соцсеть", + error: "Отсутсвует ссылка соцсети", + rules: [(val) => ruleNotValue(val)], + kind: "TELEGRAM", + }, +}; + +export const allergiesConfig = { + name: { + title: "Аллерген", + error: + "Некорректно введен аллерген. Значение должно быть больше 1 и меньше 50 символов", + }, + title: { + title: "Проявление аллергии", + rules: [(val) => ruleMaxLength(val, 100)], + error: + "Некорректно введено проявление аллергии. Значение должно быть больше 1 и меньше 100 символов", }, }; diff --git a/src/shared/utils/changesObjects.js b/src/shared/utils/changesObjects.js index 93ba76f..87f234d 100644 --- a/src/shared/utils/changesObjects.js +++ b/src/shared/utils/changesObjects.js @@ -27,3 +27,10 @@ export function checkTypeofObject(obj) { export function checkChangeData(dataInit, dataChange) { return JSON.stringify(dataInit) !== JSON.stringify(dataChange); } + +export function getFieldsNameUnvalidated(form) { + const unvalidatedFields = form + .getValidationComponents() + .filter((el) => !el.validate()); + return [...new Set(unvalidatedFields.map((field) => field.name))]; +} diff --git a/src/shared/utils/rulesInputs.js b/src/shared/utils/rulesInputs.js index dd619e6..1a1a004 100644 --- a/src/shared/utils/rulesInputs.js +++ b/src/shared/utils/rulesInputs.js @@ -1,5 +1,5 @@ export function ruleNotValue(val, text) { - return val || text || false; + return !!val || text || false; } export function ruleLengthValue(val, minLength, text) { @@ -17,3 +17,7 @@ export function ruleEmailValue(val, text) { false ); } + +export function ruleMaxLength(val, maxLength, text) { + return (!!val && val.length <= maxLength) || text || false; +}