@salla.sa/twilight-components
Version:
Salla Web Component
182 lines (181 loc) • 6.83 kB
JavaScript
/*!
* Crafted with ❤ by Salla
*/
import { DisplayType } from "../salla-product-options/interfaces";
let addedOrderItemCounter = 0;
function generateAddedOrderItemId(productId) {
const cryptoRef = (typeof globalThis !== 'undefined' ? globalThis.crypto : undefined);
if (cryptoRef && typeof cryptoRef.randomUUID === 'function') {
return `new-${productId}-${cryptoRef.randomUUID()}`;
}
addedOrderItemCounter += 1;
return `new-${productId}-${Date.now()}-${addedOrderItemCounter}`;
}
export function cloneOrderEditProduct(product) {
if (product == null) {
return product;
}
return typeof structuredClone === 'function'
? structuredClone(product)
: JSON.parse(JSON.stringify(product));
}
export function buildSavePayload(payload) {
return {
items: {
update: payload.items.update,
remove: payload.items.remove,
add: payload.items.add.map(({ itemId, ...addItem }) => addItem),
},
};
}
export function createAddedOrderItem(product, quantity, optionDefinitions, selectedOptions = {}) {
const productData = cloneOrderEditProduct(product);
return {
id: generateAddedOrderItemId(product.id),
quantity,
isNew: true,
selectedOptions,
product: {
...productData,
options: cloneOrderEditProduct(optionDefinitions),
},
};
}
function normalizeOrderEditOptionValue(value) {
if (typeof File !== 'undefined' && value instanceof File) {
return value.name;
}
return String(value);
}
function normalizeSelectedOptions(selectedOptions = {}) {
return JSON.stringify(Object.entries(selectedOptions)
.filter(([, value]) => value != null && normalizeOrderEditOptionValue(value).length > 0)
.map(([optionId, value]) => [String(optionId), normalizeOrderEditOptionValue(value)])
.sort(([leftId], [rightId]) => leftId.localeCompare(rightId)));
}
function hasSelectedOptions(selectedOptions) {
return Boolean(selectedOptions &&
Object.values(selectedOptions).some(value => value != null && normalizeOrderEditOptionValue(value).length > 0));
}
const detailIdOptionTypes = new Set([
DisplayType.COLOR,
DisplayType.COUNTRY,
DisplayType.DIGITAL_CARD_VALUE,
DisplayType.MULTIPLE_OPTIONS,
DisplayType.RADIO,
DisplayType.SINGLE_OPTION,
DisplayType.THUMBNAIL,
]);
function getProductOptionSelectedOptions(options = []) {
return options.reduce((acc, option) => {
if (option?.id == null) {
return acc;
}
const selectedDetails = option.details?.filter(detail => detail?.is_selected) || [];
const selectedDetail = selectedDetails[selectedDetails.length - 1];
const value = selectedDetail?.id ?? option.value;
if (value == null || String(value).length === 0) {
return acc;
}
acc[String(option.id)] = String(value);
return acc;
}, {});
}
function getOrderDetailSelectedOptionValue(item, optionId, value) {
const productOption = item?.product?.options?.find(option => String(option.id) === String(optionId));
const normalizedValue = String(value);
if (!productOption?.details?.length || !detailIdOptionTypes.has(productOption.type)) {
return normalizedValue;
}
const selectedDetail = productOption.details.find(detail => [
detail.id,
detail.name,
detail.option_value,
detail.color,
detail.image,
detail.code,
detail.display_value,
].some(detailValue => detailValue != null && String(detailValue) === normalizedValue));
return selectedDetail ? String(selectedDetail.id) : normalizedValue;
}
function getOrderDetailsSelectedOptions(item) {
return (item?.options || []).reduce((acc, option) => {
if (option?.id == null) {
return acc;
}
const value = option.value ?? option.display_value;
if (value == null || String(value).length === 0) {
return acc;
}
acc[String(option.id)] = getOrderDetailSelectedOptionValue(item, option.id, value);
return acc;
}, {});
}
export function getOrderItemSelectedOptions(item) {
if (hasSelectedOptions(item?.selectedOptions)) {
return item.selectedOptions;
}
const productSelectedOptions = getProductOptionSelectedOptions(item?.product?.options);
if (hasSelectedOptions(productSelectedOptions)) {
return productSelectedOptions;
}
return getOrderDetailsSelectedOptions(item);
}
export function findMergeableItem(items = [], productId, selectedOptions = {}) {
const targetSelectedOptions = normalizeSelectedOptions(selectedOptions);
return items.find(item => String(item.product?.id) === String(productId) &&
normalizeSelectedOptions(getOrderItemSelectedOptions(item)) === targetSelectedOptions);
}
export function getOrderEditProductMaxQuantity(product) {
const rawQuantity = product?.max_quantity ?? product?.max_quantity_per_order;
const parsedQuantity = Number(rawQuantity);
return Number.isFinite(parsedQuantity) && parsedQuantity > 0 ? parsedQuantity : undefined;
}
export function clampOrderEditQuantity(quantity, maxQuantity) {
if (!Number.isFinite(quantity) || quantity < 1) {
return 1;
}
if (maxQuantity && quantity > maxQuantity) {
return maxQuantity;
}
return quantity;
}
export function patchOrderItem(item, quantity, optionDefinitions, selectedOptions) {
return {
...item,
quantity,
selectedOptions: selectedOptions ?? getOrderItemSelectedOptions(item),
product: item.product
? {
...item.product,
options: optionDefinitions ?? item.product.options,
}
: item.product,
};
}
export function updateOrderItems(items = [], itemId, quantity, optionDefinitions, selectedOptions) {
return items.map(item => item.id === itemId ? patchOrderItem(item, quantity, optionDefinitions, selectedOptions) : item);
}
export function removeOrderItems(items = [], itemId) {
return items.filter(item => item.id !== itemId);
}
export function mergePayloadItems(items, patch) {
return {
...items,
...patch,
};
}
export function upsertPayloadUpdateList(updateList = [], itemId, quantity, options = {}) {
const nextUpdateList = updateList.filter(item => item.id !== itemId);
nextUpdateList.push({ id: itemId, quantity, options });
return nextUpdateList;
}
export function updateAddedPayloadList(addList = [], itemId, quantity, options) {
return addList.map(item => item.itemId === itemId
? {
...item,
quantity,
options: options ?? item.options,
}
: item);
}