Initial commit: Hoomat storefront with Meshkee API and static-image slots.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
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 };
|
||||
}
|
||||
Reference in New Issue
Block a user