// Adapts our actual ProductSummary (apps/user-app/src/api/client.ts) into the // same output shape legacy's Mixins/product/index.ts `useProduct()` produced. // The legacy card components it was originally shaped for (ProductHorizontal/ // ProductVertical/ProductVerticalFull) are gone, replaced by the redesigned // components/product/ProductCard.vue, components/home/ShowcaseCard.vue and // components/product/CartItem.vue — all of which consume this same contract. // // Honest gaps vs. legacy (no backend equivalent exists yet — apps/api was not // touched to add these): // - promotions/discounts: legacy matched a customer's order against admin // promotion + ignore-list rules (getCandidPromotions/getPromotion). No such // data exists in the new API, so `hasDiscountWage` is always null here — // the discount badge and "before/after" price never render, which is // correct given there is genuinely nothing to show, not a bug. // - `wage` now comes from the server (wageQuoteStore), which adds the // customer's own percent and the promotion exactly as legacy did. // - `is_exist` (stock) and `has_warranty`: no such fields exist on our // ProductVariant model yet — always true / false respectively. import { computed, ref, unref, type Ref } from 'vue' import type { ProductSummary, ProductVariantSummary } from '../api/client' import { useSessionStore } from '../stores/session' import { useWageQuoteStore } from '../stores/wageQuoteStore' /** A variant can be ordered when it has stock, or when it is flagged pre-order. */ export function isVariantOrderable(v: ProductVariantSummary): boolean { return (v.qty ?? 0) > 0 || Boolean(v.isPreOrder) } function getCheapestVariant(variants: ProductVariantSummary[] | undefined): ProductVariantSummary | null { if (!variants || variants.length === 0) return null // Prefer something the customer can actually order: defaulting to an // out-of-stock variant means the page opens on a disabled button even though // other weights are available. Falls back to the full list when nothing is // orderable, so the page still shows a selection. const pool = variants.filter(isVariantOrderable) const candidates = pool.length ? pool : variants return candidates.reduce((cheapest, v) => parseFloat(v.defaultWagePercent) < parseFloat(cheapest.defaultWagePercent) ? v : cheapest, ) } export function useProduct(productItem: ProductSummary, initialVariant?: ProductVariantSummary) { const product = ref(productItem) const image = computed(() => product.value.images?.[0]?.url) const id = computed(() => product.value.id) const title = computed(() => product.value.title) const variants = computed(() => product.value.variants) const selectedVariant: Ref = ref(initialVariant ?? getCheapestVariant(unref(variants))) const min_gram = computed(() => selectedVariant.value ? parseFloat(selectedVariant.value.rashWeightGrams) : 0) // The making charge the server will charge (MON-08): variant + the // customer's own percent − the best active promotion, by the formula the // admin «اجرت» screen controls. Signed in: the server's figure, hidden for // the moment it loads rather than flashing a different number. Signed out // (no customer, so no customer percent): the variant's own figure. const session = useSessionStore() const wageQuotes = useWageQuoteStore() const wage = computed(() => { const v = selectedVariant.value if (!v) return null if (!session.isLoggedIn) return parseFloat(v.defaultWagePercent) wageQuotes.request(v.id) return wageQuotes.finalPercent(v.id) }) const hasDiscountWage = ref(null) // no promotions backend yet — see file header // REAL stock: ProductVariant.qty / isPreOrder exist and are returned by // GET /catalog/products. A variant is orderable when it has stock, or when // it is flagged pre-order — the same rule the admin cart uses. const qty = computed(() => selectedVariant.value?.qty ?? 0) const isPreOrder = computed(() => selectedVariant.value?.isPreOrder ?? false) const is_exist = computed(() => qty.value > 0 || isPreOrder.value) const productCode = computed(() => product.value.productCode ?? null) const has_warranty = ref(false) // no warranty field on ProductVariant yet return { product, image, id, title, productCode, variants, selectedVariant, min_gram, wage, hasDiscountWage, qty, isPreOrder, is_exist, has_warranty, } }