From d985cc080f36fae45708eeefa1bde32da258b43e Mon Sep 17 00:00:00 2001 From: Ivan Kalachikov Date: Thu, 20 Feb 2025 09:53:45 +0100 Subject: [PATCH 1/2] feat: improve configurable products text section --- .../shared/cart/components/add-to-cart.vue | 2 +- .../components/configuration/option-text.vue | 2 +- .../useConfigurableProduct.test.ts | 16 +++---- .../composables/useConfigurableProduct.ts | 44 +++++++++++-------- 4 files changed, 35 insertions(+), 29 deletions(-) diff --git a/client-app/shared/cart/components/add-to-cart.vue b/client-app/shared/cart/components/add-to-cart.vue index 328295908..2e427f032 100644 --- a/client-app/shared/cart/components/add-to-cart.vue +++ b/client-app/shared/cart/components/add-to-cart.vue @@ -65,7 +65,7 @@ const configurableLineItemId = getUrlSearchParam(LINE_ITEM_ID_URL_SEARCH_PARAM); const { selectedConfigurationInput, changeCartConfiguredItem, - validateInput: validateConfigurableInput, + validateSections: validateConfigurableInput, } = useConfigurableProduct(product.value.id); const loading = ref(false); diff --git a/client-app/shared/catalog/components/configuration/option-text.vue b/client-app/shared/catalog/components/configuration/option-text.vue index 347fa20c2..f4ccfaf60 100644 --- a/client-app/shared/catalog/components/configuration/option-text.vue +++ b/client-app/shared/catalog/components/configuration/option-text.vue @@ -12,7 +12,7 @@ v-model="inputValue" class="option-text__input" :maxlength="MAX_LENGTH" - @input="(selected || isRequired) && $emit('input', inputValue || undefined)" + @input="$emit('input', inputValue || undefined)" /> diff --git a/client-app/shared/catalog/composables/useConfigurableProduct.test.ts b/client-app/shared/catalog/composables/useConfigurableProduct.test.ts index 3e1c15092..54bdb406b 100644 --- a/client-app/shared/catalog/composables/useConfigurableProduct.test.ts +++ b/client-app/shared/catalog/composables/useConfigurableProduct.test.ts @@ -567,7 +567,7 @@ describe("useConfigurableProduct", () => { customText: undefined, }); - const isValid = composable.validateInput(); + const isValid = composable.validateSections(); expect(isValid).toBe(false); expect(composable.validationErrors.value.has("section_1")).toBe(true); }); @@ -586,7 +586,7 @@ describe("useConfigurableProduct", () => { customText: undefined, }); - const isValid = composable.validateInput(); + const isValid = composable.validateSections(); expect(isValid).toBe(true); expect(composable.validationErrors.value.has("section_1")).toBe(false); }); @@ -605,7 +605,7 @@ describe("useConfigurableProduct", () => { customText: undefined, }); - const isValid = composable.validateInput(); + const isValid = composable.validateSections(); expect(isValid).toBe(false); expect(composable.validationErrors.value.has("section_1")).toBe(true); }); @@ -626,7 +626,7 @@ describe("useConfigurableProduct", () => { option: undefined, }); - const isValid = composable.validateInput(); + const isValid = composable.validateSections(); expect(isValid).toBe(false); expect(composable.validationErrors.value.has("text_section_1")).toBe(true); }); @@ -645,7 +645,7 @@ describe("useConfigurableProduct", () => { option: undefined, }); - const isValid = composable.validateInput(); + const isValid = composable.validateSections(); expect(isValid).toBe(true); expect(composable.validationErrors.value.has("text_section_1")).toBe(false); }); @@ -664,7 +664,7 @@ describe("useConfigurableProduct", () => { option: undefined, }); - const isValid = composable.validateInput(); + const isValid = composable.validateSections(); expect(isValid).toBe(false); expect(composable.validationErrors.value.has("text_section_1")).toBe(true); }); @@ -694,7 +694,7 @@ describe("useConfigurableProduct", () => { option: undefined, }); - const isValid = composable.validateInput(); + const isValid = composable.validateSections(); expect(isValid).toBe(false); expect(composable.validationErrors.value.has("section_1")).toBe(true); expect(composable.validationErrors.value.has("text_section_2")).toBe(true); @@ -723,7 +723,7 @@ describe("useConfigurableProduct", () => { option: undefined, }); - const isValid = composable.validateInput(); + const isValid = composable.validateSections(); expect(isValid).toBe(true); expect(composable.validationErrors.value.size).toBe(0); }); diff --git a/client-app/shared/catalog/composables/useConfigurableProduct.ts b/client-app/shared/catalog/composables/useConfigurableProduct.ts index 2feb9b275..36d29ee43 100644 --- a/client-app/shared/catalog/composables/useConfigurableProduct.ts +++ b/client-app/shared/catalog/composables/useConfigurableProduct.ts @@ -42,7 +42,7 @@ provideApolloClient(apolloClient); * @returns {Function} fetchProductConfiguration - Function to fetch the product configuration. * @returns {Function} selectSectionValue - Function to select a section value for a configuration section. * @returns {Function} changeCartConfiguredItem - Function to change the cart configured item. - * @returns {Function} validateInput - Function to validate the configuration input. Populates the validationErrors map with the errors and returns true if there are errors. + * @returns {Function} validateSections - Function to validate the configuration input. Populates the validationErrors map with the errors and returns true if there are errors. * @returns {ComputedRef} loading - Computed ref indicating if any operation is in progress. * @returns {ShallowReadonly>} configuration - Readonly ref containing the product configuration sections. * @returns {Readonly>>} selectedConfiguration - Readonly computed ref of the selected configuration state. @@ -111,7 +111,7 @@ function _useConfigurableProduct(configurableProductId: string) { function selectSectionValue(payload: ConfigurationSectionInput) { changeSelectionValue(payload); void createConfiguredLineItem(); - validateInput(); + validateSection(payload.sectionId); } function getSelectedOptionTextValue(section: ConfigurationSectionInput, sectionId: string) { @@ -158,24 +158,30 @@ function _useConfigurableProduct(configurableProductId: string) { return validateValue(sectionId, value).isValid; } - function validateInput() { - validationErrors.value.clear(); + function validateSection(sectionId: string) { + const section = configuration.value.find(({ id }) => id === sectionId); + if (!section) { + return; + } + const input = selectedConfigurationInput.value.find((value) => value.sectionId === section.id); - configuration.value.forEach((section) => { - const input = selectedConfigurationInput.value.find((value) => value.sectionId === section.id); - - if (!input && section.isRequired) { - validationErrors.value.set( - section.id, - t("shared.catalog.product_details.product_configuration.required_section"), - ); - } else if (input) { - const validationResult = validateValue(section.id, input); - if (!validationResult.isValid) { - validationErrors.value.set(section.id, validationResult.error); - } + if (!input && section.isRequired) { + validationErrors.value.set( + section.id, + t("shared.catalog.product_details.product_configuration.required_section"), + ); + } else if (input) { + const validationResult = validateValue(section.id, input); + if (!validationResult.isValid) { + validationErrors.value.set(section.id, validationResult.error); } - }); + } + } + + function validateSections() { + validationErrors.value.clear(); + + configuration.value.forEach((section) => validateSection(section.id)); return validationErrors.value.size === 0; } @@ -336,7 +342,7 @@ function _useConfigurableProduct(configurableProductId: string) { fetchProductConfiguration, selectSectionValue, changeCartConfiguredItem, - validateInput, + validateSections, loading: readonly(loading), configuration: readonly(configuration), selectedConfiguration: readonly(selectedConfiguration), From a34c9822e64f1f4ef123e075edcb5f176bfc38ab Mon Sep 17 00:00:00 2001 From: Ivan Kalachikov Date: Thu, 20 Feb 2025 15:38:51 +0100 Subject: [PATCH 2/2] fix: validation --- client-app/shared/catalog/composables/useConfigurableProduct.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client-app/shared/catalog/composables/useConfigurableProduct.ts b/client-app/shared/catalog/composables/useConfigurableProduct.ts index 36d29ee43..5b1c67cc3 100644 --- a/client-app/shared/catalog/composables/useConfigurableProduct.ts +++ b/client-app/shared/catalog/composables/useConfigurableProduct.ts @@ -174,6 +174,8 @@ function _useConfigurableProduct(configurableProductId: string) { const validationResult = validateValue(section.id, input); if (!validationResult.isValid) { validationErrors.value.set(section.id, validationResult.error); + } else { + validationErrors.value.delete(section.id); } } }