Skip to content

Commit

Permalink
#1206 product: Allow admin to add multiple values for option (#1244)
Browse files Browse the repository at this point in the history
  • Loading branch information
nguyenvanhadncntt authored Oct 30, 2024
1 parent 89b05fe commit 0a49d70
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 31 deletions.
76 changes: 66 additions & 10 deletions backoffice/modules/catalog/components/ProductVariation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const ProductVariations = ({ getValue, setValue }: Props) => {
const [productOptions, setProductOptions] = useState<ProductOption[]>([]);
const [selectedOptions, setSelectedOptions] = useState<string[]>([]);
const [optionCombines, setOptionCombines] = useState<string[]>([]);
const [optionValueArray, setOptionValueArray] = useState<any>({});

useEffect(() => {
if (id) {
Expand Down Expand Up @@ -80,6 +81,9 @@ const ProductVariations = ({ getValue, setValue }: Props) => {
const index = selectedOptions.indexOf(currentOption.name);
if (index === -1) {
setSelectedOptions([...selectedOptions, currentOption.name]);
if (!optionValueArray[currentOption.name]) {
setOptionValueArray({ ...optionValueArray, ...{ [currentOption.name]: 1 } });
}
} else {
toast.info(`${currentOption.name} is selected. Select Other`);
}
Expand Down Expand Up @@ -115,32 +119,45 @@ const ProductVariations = ({ getValue, setValue }: Props) => {
return toast.warning('Combined Option Values are Duplicated');
}

let optionValuesByOptionIds = {};

optionValuesByOptionId.forEach((value, key, fooMap) => {
optionValuesByOptionIds = Object.assign(optionValuesByOptionIds, { [key]: value });
});

const newVariation: ProductVariation = {
optionName: variationName,
optionGTin: getValue('gtin') ?? '',
optionSku: getValue('sku') ?? '',
optionPrice: getValue('price') ?? 0,
optionValuesByOptionId: Object.fromEntries(optionValuesByOptionId),
optionValuesByOptionId: optionValuesByOptionIds,
};
setOptionCombines([variationName]);
setValue('productVariations', [...formProductVariations, newVariation]);
};

const generateProductOptionCombinations = (): Map<number, string> => {
const optionValuesByOptionId = new Map<number, string>();
const generateProductOptionCombinations = (): Map<number, string[]> => {
const optionValuesByOptionId = new Map<number, string[]>();
let isEmptyOptions = false;
const optionValues = [] as string[];
selectedOptions.forEach((option) => {
if (isEmptyOptions) return;
document.getElementsByName(option).forEach((element) => {
const value = (element as HTMLInputElement).value;
if (value !== '') {
optionValues.push(value);
}
});
const optionValue = (document.getElementById(option) as HTMLInputElement).value;
if (optionValue === '') {
isEmptyOptions = true;
return;
}
const productOption = productOptions.find((productOption) => productOption.name === option);
const productOptionId = productOption?.id ?? -1;
optionValuesByOptionId.set(productOptionId, optionValue);
optionValuesByOptionId.set(productOptionId, optionValues);
});
return isEmptyOptions ? new Map<number, string>() : optionValuesByOptionId;
return isEmptyOptions ? new Map<number, string[]>() : optionValuesByOptionId;
};

const onDeleteVariation = (variant: ProductVariation) => {
Expand All @@ -152,6 +169,17 @@ const ProductVariations = ({ getValue, setValue }: Props) => {
setValue('productVariations', productVar);
};

const addOptionValue = (option: string) => {
setOptionValueArray({ ...optionValueArray, ...{ [option]: optionValueArray[option] + 1 } });
};

const removeOptionValue = (option: string) => {
setOptionValueArray({ ...optionValueArray, ...{ [option]: optionValueArray[option] - 1 } });
if (optionValueArray[option] === 0) {
removeOptionValue(option);
}
};

return (
<>
{/* Selection */}
Expand Down Expand Up @@ -184,17 +212,45 @@ const ProductVariations = ({ getValue, setValue }: Props) => {
<h5 className="mb-3">Value Options</h5>
<div className="mb-3">
{(selectedOptions || []).map((option) => (
<div className="mb-3 d-flex gap-4" key={option}>
<div className="mb-3 d-flex gap-4 option-value-box" key={option}>
<label className="form-label flex-grow-1" htmlFor={option}>
{option}
</label>
<input type="text" id={option} className="form-control w-75" />
<button
className="btn btn-danger"
<div className="w-75">
{[...Array(optionValueArray[option])].map((e, i, arr) => {
return (
<div
key={optionValueArray[option] + i + e}
className="d-flex gap-2 w-100 mb-3"
>
<div className="w-75">
<input type="text" id={option} name={option} className="form-control" />
</div>
{i == arr.length - 1 && (
<i
className="fa fa-plus fa-lg"
style={{ paddingTop: '12px' }}
aria-hidden="true"
onClick={() => addOptionValue(option)}
></i>
)}
<i
className="fa fa-minus fa-lg"
style={{ paddingTop: '12px' }}
aria-hidden="true"
onClick={() => removeOptionValue(option)}
></i>
</div>
);
})}
</div>
<span
aria-hidden="true"
className="close"
onClick={(event) => onDeleteOption(event, option)}
>
<i className="bi bi-x"></i>
</button>
</span>
</div>
))}
</div>
Expand Down
2 changes: 1 addition & 1 deletion backoffice/modules/catalog/models/ProductPayload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const createProductOptionValues = (productVariations: ProductVariation[]) => {
productOptionValues.push({
productOptionId: id,
displayOrder: 1,
value: [value],
value: Array.isArray(value) ? value : [value],
});
}
});
Expand Down
4 changes: 2 additions & 2 deletions backoffice/pages/catalog/products/create.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { NextPage } from 'next';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { Tab, Tabs } from 'react-bootstrap';
import { SubmitHandler, useForm } from 'react-hook-form';
import { toast } from 'react-toastify';
Expand All @@ -20,8 +22,6 @@ import { ProductAttributeValuePost } from '../../../modules/catalog/models/Produ
import { mapFormProductToProductPayload } from '../../../modules/catalog/models/ProductPayload';
import { createProductAttributeValueOfProduct } from '../../../modules/catalog/services/ProductAttributeValueService';
import { createProduct } from '../../../modules/catalog/services/ProductService';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';

const ProductCreate: NextPage = () => {
const router = useRouter();
Expand Down
22 changes: 22 additions & 0 deletions backoffice/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,25 @@ a[data-toggle='collapse'] {
margin-left: 5px;
cursor: pointer;
}

.option-value-box {
position: relative;
background-color: #fff;
border: 2px solid #ccc;
padding: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}

.close {
position: absolute;
top: 10px;
right: 10px;
font-size: 20px;
cursor: pointer;
color: #333;
}

.close:hover {
color: #ff0000;
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,12 @@ void initProductPutVm() {

ProductVariationPutVm productVariationPutVm1 = new ProductVariationPutVm(
1L, "Sample Product Variation 2", "sample-product-variation-1", "SKU1",
"GTIN1", 19.99, 101L, new ArrayList<>(), new HashMap<>(Map.of(productOption.getId(), "product-option-value-1")));
"GTIN1", 19.99, 101L, new ArrayList<>(),
new HashMap<>(Map.of(productOption.getId(), List.of("product-option-value-1"))));
ProductVariationPutVm productVariationPutVm2 = new ProductVariationPutVm(
null, "Sample Product Variation 2", "sample-product-variation-2", "SKU2",
"GTIN2", 29.99, 102L, new ArrayList<>(), new HashMap<>(Map.of(productOption.getId(), "product-option-value-2")));
"GTIN2", 29.99, 102L, new ArrayList<>(),
new HashMap<>(Map.of(productOption.getId(), List.of("product-option-value-2"))));
ProductOptionValuePutVm productOptionValuePutVm = new ProductOptionValuePutVm(
productOption.getId(),
"Visible",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ public interface ProductVariationSaveVm extends ProductProperties {

List<Long> productImageIds();

Map<Long, String> optionValuesByOptionId();
Map<Long, List<String>> optionValuesByOptionId();
}
31 changes: 18 additions & 13 deletions product/src/main/java/com/yas/product/service/ProductService.java
Original file line number Diff line number Diff line change
Expand Up @@ -328,20 +328,25 @@ private void createOptionCombinations(

variationVm.optionValuesByOptionId().forEach((optionId, optionValue) -> {
ProductOption productOption = optionsById.get(optionId);
ProductOptionValue foundOptionValue = optionValues.stream()
List<ProductOptionValue> foundOptionValues = optionValues.stream()
.filter(
pov -> pov.getProductOption().getId().equals(optionId) && pov.getValue().equals(optionValue))
.findFirst()
.orElseThrow(()
-> new BadRequestException(Constants.ErrorCode.PRODUCT_OPTION_VALUE_IS_NOT_FOUND, optionValue));

ProductOptionCombination optionCombination = ProductOptionCombination.builder()
.product(savedVariation)
.productOption(productOption)
.value(foundOptionValue.getValue())
.displayOrder(foundOptionValue.getDisplayOrder())
.build();
optionCombinations.add(optionCombination);
pov -> pov.getProductOption().getId().equals(optionId) && optionValue.contains(pov.getValue()))
.toList();

if (CollectionUtils.isEmpty(foundOptionValues)) {
throw new BadRequestException(Constants.ErrorCode.PRODUCT_OPTION_VALUE_IS_NOT_FOUND, optionValue);
}

List<ProductOptionCombination> optionCombinationEntities = foundOptionValues.stream()
.map(foundOptionValue ->
ProductOptionCombination.builder()
.product(savedVariation)
.productOption(productOption)
.value(foundOptionValue.getValue())
.displayOrder(foundOptionValue.getDisplayOrder())
.build()
).toList();
optionCombinations.addAll(optionCombinationEntities);
});
}
productOptionCombinationRepository.saveAll(optionCombinations);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public record ProductVariationPostVm(
Double price,
Long thumbnailMediaId,
List<Long> productImageIds,
Map<Long, String> optionValuesByOptionId
Map<Long, List<String>> optionValuesByOptionId
) implements ProductVariationSaveVm {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ public record ProductVariationPutVm(
Double price,
Long thumbnailMediaId,
List<Long> productImageIds,
Map<Long, String> optionValuesByOptionId
Map<Long, List<String>> optionValuesByOptionId
) implements ProductVariationSaveVm {
}

0 comments on commit 0a49d70

Please sign in to comment.