SearchView: Removed the dynamic collection-tile grid (e.g. "پرفروشهای این هفته") and the "نتایج / N محصول" section header from the UI. Underlying data/filtering logic untouched.
DashboardView ("میز کار"): Rebuilt from scratch with a new structure, distinct from the old card+grid layout but visually aligned with HomeView (same blurred bar, @ink/@wine palette, staged reveal animation on mount):
Single hero card (ink→wine gradient) showing balance, gold/fiat debt, and identity — no quick-action icons inside it anymore.
Live gold rate folded into a slim inline row at the bottom instead of a titled section.
Iterated shadow/spacing on the hero card per feedback (softer, less clipped).
DialogVant / DialogVantOneButton (shared modal components, used in 7+ call sites: Dashboard, Cart, SingleProduct, Register, App): Replaced Vant's default dialog chrome with the app's own rounded-card language — circular icon badge for the illustration, pill-shaped primary/secondary buttons. No call-site markup changed.
LoginView: Rebuilt from scratch — full-bleed dark hero (brand + step copy) overlapped by a floating white form sheet, animated crossfade transition between the mobile-number and OTP steps, same staged entrance animation as Home/Dashboard. All auth logic (API check, OTP stub, session login) unchanged.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
// 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`: legacy added the logged-in customer's own `percent` (a
|
||||
// commission/markup field on their account) on top of the variant's
|
||||
// making-charge %. No login exists in this port yet, so wage is just the
|
||||
// selected variant's defaultWagePercent on its own.
|
||||
// - `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'
|
||||
|
||||
function getCheapestVariant(variants: ProductVariantSummary[] | undefined): ProductVariantSummary | null {
|
||||
if (!variants || variants.length === 0) return null
|
||||
return variants.reduce((cheapest, v) =>
|
||||
parseFloat(v.defaultWagePercent) < parseFloat(cheapest.defaultWagePercent) ? v : cheapest,
|
||||
)
|
||||
}
|
||||
|
||||
export function useProduct(productItem: ProductSummary, initialVariant?: ProductVariantSummary) {
|
||||
const product = ref<ProductSummary>(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<ProductVariantSummary | null> = ref(initialVariant ?? getCheapestVariant(unref(variants)))
|
||||
const min_gram = computed(() => selectedVariant.value ? parseFloat(selectedVariant.value.rashWeightGrams) : 0)
|
||||
const wage = computed(() => (selectedVariant.value ? parseFloat(selectedVariant.value.defaultWagePercent) : null))
|
||||
const hasDiscountWage = ref<number | null>(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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user