Replaces the previous server-cart implementation (which wrongly called
the customer-dashboard-only /businesses/{id}/cart* API) with the
documented guest-cart architecture:
- src/lib/guestCart.ts: storage key/cookie name, cookie attributes,
line shape, and encodeGuestCart() copied verbatim from AI_PROMPT.md
- CartProvider/CartButton: pure client state (localStorage + mirrored
cookie), no bearer auth, no businessId
- CartButton's "ادامه" builds the exact documented Continue URL,
branching on hasCustomerSessionCookie() to either
customer.<domain>/checkout/cart?guestCart=<encoded> or
.../login?redirect=<encoded checkout path>
- Auth cookie corrected to the real documented name
(meshkee_customer_access_token, possibly chunked) via a shared
reassembleChunkedCookie() helper used by both auth.ts (server) and
authUser.ts (client)
- ProductBuyPanel/product pages switched to the documented
GET .../store-items/by-product/{id} endpoint (getStoreItemByProduct)
- isInStock() treats stockQuantity === null as unlimited stock, not
out-of-stock, per spec
- AGENTS.md updated to describe this architecture and remove the
incorrect server-cart documentation
Manually verified in-browser: badge count, popup lines/qty/remove/
total all update correctly against injected guest-cart data; tsc,
eslint, and next build all pass clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
31 lines
1.3 KiB
TypeScript
31 lines
1.3 KiB
TypeScript
const FA_DIGITS = ["۰", "۱", "۲", "۳", "۴", "۵", "۶", "۷", "۸", "۹"];
|
||
|
||
export function toFaDigits(input: string | number): string {
|
||
return String(input).replace(/\d/g, (d) => FA_DIGITS[Number(d)]);
|
||
}
|
||
|
||
// Prices come from the API in Toman.
|
||
export function formatPrice(value: number | null | undefined): string {
|
||
if (value === null || value === undefined) return "";
|
||
return toFaDigits(value.toLocaleString("en-US"));
|
||
}
|
||
|
||
export function discountPercent(price: number, oldPrice: number): number {
|
||
if (!oldPrice || oldPrice <= price) return 0;
|
||
return Math.round(((oldPrice - price) / oldPrice) * 100);
|
||
}
|
||
|
||
// Per AI_PROMPT.md "Shopping cart" step 3: a variant's stockQuantity of
|
||
// `null` means unlimited stock (in stock), not zero/out of stock — never
|
||
// treat it as falsy.
|
||
export function isInStock(stockQuantity: number | null | undefined): boolean {
|
||
return stockQuantity === null || (stockQuantity ?? 0) > 0;
|
||
}
|
||
|
||
// The static-image-slot API documents aspectRatio as "16:9", but CSS's
|
||
// aspect-ratio property needs a slash ("16/9"), not a colon.
|
||
export function toCssAspectRatio(value: string | number | null | undefined): string | undefined {
|
||
if (value === null || value === undefined || value === "") return undefined;
|
||
return String(value).replace(":", "/");
|
||
}
|