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:
s.esmaeili
2026-09-18 12:54:45 +03:30
parent 05fe780c89
commit 53a304b5d5
355 changed files with 22865 additions and 14 deletions
@@ -0,0 +1,6 @@
// Ported verbatim from legacy henza_pwa/src/Mixins/checkAccess.ts (isUserLoggedIn
// only — canNavigate/getHomeRouteForLoggedInUser were dead/unwired in legacy too).
// No login flow exists yet in this port, so this always returns falsy for now.
export function isUserLoggedIn() {
return localStorage.getItem('appToken')
}
+36
View File
@@ -0,0 +1,36 @@
// Ported from legacy henza_pwa/src/Mixins/filters/index.ts. Note: jalali dates
// render with Western digits (moment-jalaali's default), not Persian numerals
// — preserved exactly, do not swap for Intl's 'fa-IR' locale (which would
// silently introduce Persian digits legacy never used anywhere).
import moment from 'moment-jalaali'
export function price(val: number | string | undefined | null): string {
if (val) {
return val.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}
return val as any
}
export function priceUnit(val: number | string | undefined | null): string {
if (['string', 'number'].includes(typeof val)) {
return `${val!.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')} تومان `
}
return val as any
}
export function priceUnitRialLess(val: number | string | undefined | null): string {
if (['string', 'number'].includes(typeof val)) {
return `${val!.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')} `
}
return val as any
}
export function jdate(val: string | undefined | null): string {
if (val) {
// legacy parsed a fixed 'YYYY-MM-DD HH:mm:ss' Laravel timestamp format;
// our API returns ISO 8601 instead, which moment parses natively without
// needing an explicit format string.
return moment(val).format('jYYYY/jMM/jDD')
}
return val as any
}
+54
View File
@@ -0,0 +1,54 @@
// Persian labels ported from the legacy Laravel Order::$STS enum (henza_server
// app/Models/Order.php) — the authoritative source, not henza_pwa's own
// Orders.vue filter-tab labels, which used a slightly different, inconsistent
// set (its key=0 was labeled "لغو شده"/cancelled, while the actual backend
// model's sts=0 means "معلق"/suspended — legacy itself was inconsistent here).
// Our OrderStatus enum cleanly separates SUSPENDED and CANCELLED, so mapped
// directly, one label per real status.
export const ORDER_STATUS_LABELS: Record<string, string> = {
SUSPENDED: 'معلق',
PLACED: 'ثبت سفارش',
ADMIN_APPROVED: 'تایید مدیر سیستم',
SOURCING: 'درحال تامین',
PREPARING: 'درحال آماده سازی',
DELIVERED_TO_AGENT: 'تحویل مسئول فروش',
DELIVERED_TO_CUSTOMER: 'تحویل مشتری',
CANCELLED: 'لغو شده',
};
// Ported from legacy order.vue/Orders.vue's setColor() — legacy only ever
// colored sts 1,3,4,5,6 (never sts=0 or sts=2), an existing gap preserved here
// for the directly-analogous statuses (ADMIN_APPROVED, SUSPENDED, CANCELLED
// stay uncolored, same as legacy's untouched sts=0/2).
export const ORDER_STATUS_COLORS: Record<string, { font: string; background: string }> = {
PLACED: { font: '#3B82F6', background: '#EFF6FF' },
SOURCING: { font: '#EAB308', background: '#FEFCE8' },
PREPARING: { font: '#F97316', background: '#FFF7ED' },
DELIVERED_TO_AGENT: { font: '#06B6D4', background: '#ECFEFF' },
DELIVERED_TO_CUSTOMER: { font: '#22C55E', background: '#F0FDF4' },
};
// Ported from legacy Orders.vue's 4-step tracker (`steps`/`setCurrentStep`).
// Legacy's ambiguous sts=0 (suspended vs cancelled) is resolved here since our
// enum keeps them distinct; SUSPENDED is grouped with the first step (not yet
// progressing), matching the spirit of legacy's grouping of early statuses.
export const ORDER_STEPS = ['ثبت سفارش', 'آماده سازی', 'تحویل مشتری', 'لغو شده'];
export function orderStepIndex(status: string): number {
switch (status) {
case 'PLACED':
case 'ADMIN_APPROVED':
case 'SUSPENDED':
return 0;
case 'SOURCING':
case 'PREPARING':
return 1;
case 'DELIVERED_TO_AGENT':
case 'DELIVERED_TO_CUSTOMER':
return 2;
case 'CANCELLED':
return 3;
default:
return 0;
}
}
+30
View File
@@ -0,0 +1,30 @@
// Ported from legacy henza_pwa/src/Mixins/validator/index.ts — same rules,
// same messages, verbatim.
export const message = (field: string) => ` مقدار ${field} نا معتبر است `
export const mobile = (field: string) => ({
pattern: /^(\+98|0098|98|0)?9\d{9}$/g,
message: message(field),
})
export const phone = (field: string) => ({
pattern: /^((\+98|0098|98|0)\d1)?\d{8}$/g,
message: message(field),
})
export const fa = (field: string) => ({
pattern: /^([؀-ۿﮊپچگ‌ ])+$/,
message: ` مقدار ${field} باید فارسی باشد `,
})
export const mail = (field: string) => ({ type: 'email', message: message(field) })
export const url = (field: string) => ({ type: 'url', message: message(field) })
export const username = (field: string) => ({
pattern: /^(?!.*\.\.)(?!.*\.$)[^\W][\w.]{0,29}$/gim,
message: ` مقدار ${field} باید شامل حروف و اعداد انگلیسی و _ باشد `,
})
export const required = (field: string) => ({ required: true, message: ` لطفا ${field} را وارد کنید ` })
export const sheba = (field: string) => ({ pattern: /^IR\d{24}$/gim, message: message(field) })
export const card = (field: string) => ({ pattern: /^\d{16}$/gim, message: message(field) })
export const zip = (field: string) => ({ pattern: /^\d{10}$/gim, message: message(field) })
export const num = (field: string) => ({ pattern: /^\d*$/gim, message: message(field) })
export const len = (field: string, len: number) => ({ len, message: ` مقدار ${field} باید ${len} کارکتر باشد ` })
export const max = (field: string, max: number) => ({ max, message: ` مقدار ${field} باید کمتر از ${max} کارکتر باشد ` })
export const min = (field: string, min: number) => ({ min, message: ` مقدار ${field} باید بیشتر از ${min} کارکتر باشد ` })