118 lines
3.4 KiB
TypeScript
118 lines
3.4 KiB
TypeScript
import type { CartLine, CartState } from "@/lib/meshkee/types";
|
|
|
|
function asRecord(value: unknown): Record<string, unknown> {
|
|
return value && typeof value === "object" ? (value as Record<string, unknown>) : {};
|
|
}
|
|
|
|
function asString(value: unknown): string {
|
|
return typeof value === "string" ? value : value == null ? "" : String(value);
|
|
}
|
|
|
|
function asNumber(value: unknown): number | null {
|
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
if (typeof value === "string" && value.trim() && !Number.isNaN(Number(value))) {
|
|
return Number(value);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function emptyCart(): CartState {
|
|
return { items: [], subtotal: 0, total: 0 };
|
|
}
|
|
|
|
export function cartFromGuest(
|
|
items: Array<{
|
|
id: string;
|
|
name: string;
|
|
slug?: string;
|
|
price: number;
|
|
image?: string | null;
|
|
quantity: number;
|
|
}>,
|
|
): CartState {
|
|
const lines: CartLine[] = items.map((item) => ({
|
|
id: item.id,
|
|
storeItemVariantId: item.id,
|
|
name: item.name,
|
|
slug: item.slug,
|
|
image: item.image ?? null,
|
|
price: item.price,
|
|
quantity: item.quantity,
|
|
}));
|
|
const subtotal = lines.reduce((sum, line) => sum + line.price * line.quantity, 0);
|
|
return { items: lines, subtotal, total: subtotal };
|
|
}
|
|
|
|
export function normalizeCartResponse(payload: unknown): CartState {
|
|
const root = asRecord(payload);
|
|
const cart = asRecord(root.cart ?? root);
|
|
const source = Array.isArray(cart.items)
|
|
? cart.items
|
|
: Array.isArray(root.items)
|
|
? root.items
|
|
: [];
|
|
|
|
const items: CartLine[] = source.map((raw) => {
|
|
const item = asRecord(raw);
|
|
const product = asRecord(item.product);
|
|
const variant = asRecord(item.variant);
|
|
const storeItem = asRecord(item.storeItem);
|
|
const quantity = asNumber(item.quantity) ?? 1;
|
|
const price =
|
|
asNumber(item.unitPrice) ??
|
|
asNumber(item.price) ??
|
|
asNumber(item.discountedPrice) ??
|
|
asNumber(variant.discountedPrice) ??
|
|
asNumber(variant.price) ??
|
|
asNumber(storeItem.discountedPrice) ??
|
|
asNumber(storeItem.price) ??
|
|
0;
|
|
const selections = Array.isArray(item.selections)
|
|
? item.selections
|
|
: Array.isArray(variant.selections)
|
|
? variant.selections
|
|
: [];
|
|
const optionLabel = selections
|
|
.map((entry) => {
|
|
const row = asRecord(entry);
|
|
return asString(row.label || row.optionLabel || row.value);
|
|
})
|
|
.filter(Boolean)
|
|
.join(" / ");
|
|
|
|
return {
|
|
id: asString(item.id || item.itemId),
|
|
storeItemVariantId: asString(
|
|
item.storeItemVariantId || variant.id || storeItem.id || item.variantId,
|
|
),
|
|
name: asString(
|
|
item.productTitle ||
|
|
item.productNameFa ||
|
|
product.nameFa ||
|
|
product.name ||
|
|
product.title ||
|
|
storeItem.productTitle ||
|
|
storeItem.productNameFa ||
|
|
"محصول",
|
|
),
|
|
slug: asString(product.slug || item.productSlug || storeItem.productSlug) || undefined,
|
|
image:
|
|
asString(
|
|
item.productImage ||
|
|
item.image ||
|
|
product.image ||
|
|
product.thumbnailUrl ||
|
|
storeItem.productImage,
|
|
) || null,
|
|
price,
|
|
quantity,
|
|
optionLabel: optionLabel || undefined,
|
|
};
|
|
});
|
|
|
|
const subtotal =
|
|
asNumber(cart.subtotal) ?? items.reduce((sum, line) => sum + line.price * line.quantity, 0);
|
|
const total = asNumber(cart.total) ?? subtotal;
|
|
return { items, subtotal, total };
|
|
}
|