diff --git a/apps/user-app/index.html b/apps/user-app/index.html index 9395686..623aa0d 100644 --- a/apps/user-app/index.html +++ b/apps/user-app/index.html @@ -34,23 +34,8 @@ %BRAND_TITLE% - - -
+ +
diff --git a/apps/user-app/src/App.vue b/apps/user-app/src/App.vue index 7ab7d22..643b1ae 100644 --- a/apps/user-app/src/App.vue +++ b/apps/user-app/src/App.vue @@ -81,23 +81,6 @@ body::-webkit-scrollbar { -webkit-user-select: none; -ms-user-select: none; user-select: none; - // Centred phone column on desktop (width only). Do NOT put transform / - // filter / perspective here — that would make position:fixed bars - // (tabbar, PageBar) stick to #app and scroll away with the page. - max-width: var(--app-max-width); - margin-inline: auto; - min-height: 100dvh; - min-height: 100svh; - background: @page; - position: relative; -} - -@media (min-width: 431px) { - #app { - box-shadow: - 0 0 0 1px @hairline, - 0 28px 64px -28px rgba(20, 22, 26, 0.32); - } } .slideDown-enter-active, diff --git a/apps/user-app/src/assets/styles/style.less b/apps/user-app/src/assets/styles/style.less index 14335d3..e5d80dd 100644 --- a/apps/user-app/src/assets/styles/style.less +++ b/apps/user-app/src/assets/styles/style.less @@ -343,36 +343,8 @@ p { margin-bottom: 10px; } -// Phone column width. On desktop the shell centres at this size so the app -// does not stretch into a loose website layout. Tab bar already sat near 420. -:root { - --app-max-width: 430px; -} - html, body { overscroll-behavior-y: contain; - background: @page; -} - -@media (min-width: 431px) { - html, body { - // Side gutters behind the centred phone column (#app max-width). - background: @canvasSoft !important; - } -} - -// Fixed overlays teleported to (outside #app) — pin them to the same -// column. Below --app-max-width, max(0, …) collapses to full bleed. -.app-frame-x { - left: max(0px, calc(50vw - (var(--app-max-width) / 2))); - right: max(0px, calc(50vw - (var(--app-max-width) / 2))); - width: auto; -} - -// Ant bottom drawers (order detail, status sheet) — same phone width, centred. -.ant-drawer-bottom > .ant-drawer-content-wrapper { - max-width: var(--app-max-width); - margin-inline: auto; } .overflow-x-auto { diff --git a/apps/user-app/src/components/expert/ActingAsBar.vue b/apps/user-app/src/components/expert/ActingAsBar.vue index 6935d55..0c9d66a 100644 --- a/apps/user-app/src/components/expert/ActingAsBar.vue +++ b/apps/user-app/src/components/expert/ActingAsBar.vue @@ -57,8 +57,8 @@ async function leave() { .aab { position: fixed; top: 0; - left: max(0px, calc(50vw - (var(--app-max-width) / 2))); - right: max(0px, calc(50vw - (var(--app-max-width) / 2))); + left: 0; + right: 0; z-index: 2000; display: flex; align-items: center; diff --git a/apps/user-app/src/components/product/ProductMorph.vue b/apps/user-app/src/components/product/ProductMorph.vue index 2a395b5..5f95e62 100644 --- a/apps/user-app/src/components/product/ProductMorph.vue +++ b/apps/user-app/src/components/product/ProductMorph.vue @@ -483,9 +483,7 @@ onBeforeUnmount(() => { position: fixed; top: 0; bottom: 0; - // Stay in the phone column on desktop (same as #app / .app-frame-x). - left: max(0px, calc(50vw - (var(--app-max-width) / 2))); - right: max(0px, calc(50vw - (var(--app-max-width) / 2))); + inset: 0; width: auto; // Fixed bars inside SingleProduct (action bar, …) stay in this column. transform: translateZ(0); diff --git a/apps/user-app/src/components/product/SingleProduct.vue b/apps/user-app/src/components/product/SingleProduct.vue index e395d8a..973cda7 100644 --- a/apps/user-app/src/components/product/SingleProduct.vue +++ b/apps/user-app/src/components/product/SingleProduct.vue @@ -1,19 +1,19 @@ @@ -309,7 +309,7 @@ import { isUserLoggedIn } from '../../utils/checkAccess' import loginDialogImage from '../../assets/img/empty/dialogLogin.svg' // `initial`: the card's own ProductSummary when opened from a list (see -// ProductMorph.vue) — lets the page render whole on its first frame. +// ProductMorph.vue) — lets the page render whole on its first frame. const props = defineProps<{ id: string; initial?: ProductSummary | null }>() const emit = defineEmits<{ (e: 'closeDrawer'): void }>() @@ -362,7 +362,7 @@ const qty = computed(() => productAdapter.value.qty.value) const isPreOrder = computed(() => productAdapter.value.isPreOrder.value) const is_exist = computed(() => productAdapter.value.is_exist.value) -// ── Variant chips ─────────────────────────────────────────────── +// ── Variant chips ─────────────────────────────────────────────── // Compact pills; extras behind a short toggle so weight lists don't dominate. const VARIANT_PREVIEW_COUNT = 8 const showAllVariants = ref(false) @@ -373,13 +373,13 @@ const visibleVariants = computed(() => const isOrderable = (v: ProductVariantSummary) => isVariantOrderable(v) const stockChipLabel = (v: ProductVariantSummary) => { - if ((v.qty ?? 0) > 0) return 'موجود' - return v.isPreOrder ? 'پیش‌سفارش' : 'ناموجود' + if ((v.qty ?? 0) > 0) return 'موجود' + return v.isPreOrder ? 'پیش‌سفارش' : 'ناموجود' } /** One short glyph/word so the chip stays one line. */ const stockChipShort = (v: ProductVariantSummary) => { - if ((v.qty ?? 0) > 0) return 'موجود' - return v.isPreOrder ? 'پیش' : '—' + if ((v.qty ?? 0) > 0) return 'موجود' + return v.isPreOrder ? 'پیش' : '—' } const stockChipClass = (v: ProductVariantSummary) => { if ((v.qty ?? 0) > 0) return 'is-in' @@ -408,8 +408,8 @@ watch(selectedVariant, (v) => { if (i > -1) imageIndex.value = i }) -// ── Full-screen preview ───────────────────────────────────────── -// Tapping the inline gallery (a plain tap, not a swipe — GallerySlider tells +// ── Full-screen preview ───────────────────────────────────────── +// Tapping the inline gallery (a plain tap, not a swipe — GallerySlider tells // the two apart itself and only emits `tap` for the former) opens a // full-screen viewer teleported to , with its own GallerySlider kept in // lockstep via the shared `imageIndex` v-model. @@ -442,8 +442,8 @@ const wageLabel = computed(() => { }) const stockLabel = computed(() => { - if (qty.value > 0) return `${qty.value} عدد موجود` - return isPreOrder.value ? 'پیش‌سفارش' : 'ناموجود' + if (qty.value > 0) return `${qty.value} عدد موجود` + return isPreOrder.value ? 'پیش‌سفارش' : 'ناموجود' }) const stockClass = computed(() => { if (qty.value > 0) return 'is-in' @@ -465,7 +465,7 @@ const cartIndex = computed(() => cart.find_index(storeProduct.value)) const cartQty = computed(() => (cartIndex.value > -1 ? cart.items[cartIndex.value].qty : 0)) // Stock ceiling: a pre-order variant may exceed what is on hand, anything else -// stops at its qty. The store clamps too — this just keeps the button honest. +// stops at its qty. The store clamps too — this just keeps the button honest. const cartMaxQty = computed(() => cart.maxQty(storeProduct.value)) const canIncrement = computed(() => cartMaxQty.value === null || cartQty.value < cartMaxQty.value) const increment = () => { @@ -485,7 +485,7 @@ const addToCart = () => { // Previously the "added to cart" toast fired either way, telling the customer // something had been added when nothing had. if (!cart.addToCart(storeProduct.value)) return - toast.success('محصول با موفقیت به سبد خرید افزوده شد', { title: 'ثبت سفارش' }) + toast.success('محصول با موفقیت به سبد خرید افزوده شد', { title: 'ثبت سفارش' }) } const toggleFav = (e: Event) => { @@ -497,8 +497,8 @@ onBeforeMount(async () => { await productDetail.load() }) -// If the preview is left open when this instance goes away — the drawer is -// closed, or the routed page is navigated away from — the body scroll lock it +// If the preview is left open when this instance goes away — the drawer is +// closed, or the routed page is navigated away from — the body scroll lock it // set would otherwise outlive it and leave the rest of the app unscrollable. onBeforeUnmount(() => { if (lockedBody) document.body.style.overflow = '' @@ -515,12 +515,12 @@ onBeforeUnmount(() => { min-height: 100vh; min-height: 100dvh; background: @canvas; - /* Room for the fixed action bar — keep in sync with .pd__bar height. */ + /* Room for the fixed action bar — keep in sync with .pd__bar height. */ padding-bottom: 104px; box-sizing: border-box; } -/* ── Gallery ─────────────────────────────────────────────── */ +/* ── Gallery ─────────────────────────────────────────────── */ .pd__gallery { position: relative; z-index: 0; @@ -614,7 +614,7 @@ onBeforeUnmount(() => { background: #fff; } -/* ── Sheet ───────────────────────────────────────────────── */ +/* ── Sheet ───────────────────────────────────────────────── */ .pd__sheet { position: relative; z-index: 1; @@ -681,7 +681,7 @@ onBeforeUnmount(() => { background: @hairlineSoft; } -/* ── Figures ─────────────────────────────────────────────── */ +/* ── Figures ─────────────────────────────────────────────── */ .pd__figures { display: flex; gap: 10px; @@ -714,7 +714,7 @@ onBeforeUnmount(() => { padding-inline-start: 3px; } -/* ── Blocks ──────────────────────────────────────────────── */ +/* ── Blocks ──────────────────────────────────────────────── */ .pd__block { margin-top: 22px; } @@ -741,7 +741,7 @@ onBeforeUnmount(() => { white-space: nowrap; } -/* Compact wrapping pills — weight lists stay on-screen without card tiles. */ +/* Compact wrapping pills — weight lists stay on-screen without card tiles. */ .pd__variants { display: flex; flex-wrap: wrap; @@ -916,12 +916,11 @@ onBeforeUnmount(() => { color: @inkFaint; } -/* ── Action bar ──────────────────────────────────────────── */ +/* ── Action bar ──────────────────────────────────────────── */ .pd__bar { position: fixed; bottom: 0; - left: max(0px, calc(50vw - (var(--app-max-width) / 2))); - right: max(0px, calc(50vw - (var(--app-max-width) / 2))); + inset-inline: 0; z-index: 20; display: flex; align-items: center; @@ -991,8 +990,8 @@ onBeforeUnmount(() => { border: 1px solid @primary; } -/* ── Full-screen preview ─────────────────────────────────── */ -/* Teleported to , so it sits outside .pd entirely — see the template +/* ── Full-screen preview ─────────────────────────────────── */ +/* Teleported to , so it sits outside .pd entirely — see the template comment on why. z-index cleared well above ant-design-vue's own Drawer layer (@zindex-modal) so the preview is unconditionally topmost regardless of which Teleport happens to mount last. */ diff --git a/apps/user-app/src/components/ui/AppPullRefresh.vue b/apps/user-app/src/components/ui/AppPullRefresh.vue index d65c933..7a34a61 100644 --- a/apps/user-app/src/components/ui/AppPullRefresh.vue +++ b/apps/user-app/src/components/ui/AppPullRefresh.vue @@ -157,11 +157,7 @@ const ringFillStyle = computed(() => { .app-ptr__overlay { position: fixed; - // Stay in the phone column on desktop (same as #app / .app-frame-x). - top: 0; - bottom: 0; - left: max(0px, calc(50vw - (var(--app-max-width) / 2))); - right: max(0px, calc(50vw - (var(--app-max-width) / 2))); + inset: 0; z-index: 1900; display: grid; place-items: center; diff --git a/apps/user-app/src/components/ui/Container.vue b/apps/user-app/src/components/ui/Container.vue index a2d15f4..e649690 100644 --- a/apps/user-app/src/components/ui/Container.vue +++ b/apps/user-app/src/components/ui/Container.vue @@ -1,7 +1,6 @@ - + @@ -12,13 +11,3 @@ interface Props { } withDefaults(defineProps(), {}) - - diff --git a/apps/user-app/src/components/ui/Navigation.vue b/apps/user-app/src/components/ui/Navigation.vue index c3756e6..dca3ed2 100644 --- a/apps/user-app/src/components/ui/Navigation.vue +++ b/apps/user-app/src/components/ui/Navigation.vue @@ -9,7 +9,7 @@ so it needs no measurement of the container width. --> diff --git a/apps/user-app/src/main.ts b/apps/user-app/src/main.ts index c91b7a2..36d878a 100644 --- a/apps/user-app/src/main.ts +++ b/apps/user-app/src/main.ts @@ -63,5 +63,3 @@ app.use(VueLazyLoad, { }); app.component('container', Container); app.mount('#app'); -// Boot splash (index.html) matched the phone column; drop it once the app paints. -document.getElementById('app-splash')?.remove(); diff --git a/apps/user-app/src/views/CartView.vue b/apps/user-app/src/views/CartView.vue index 50d6c5e..aa4c93f 100644 --- a/apps/user-app/src/views/CartView.vue +++ b/apps/user-app/src/views/CartView.vue @@ -2,8 +2,8 @@ Ported from legacy henza_pwa/src/views/cart.vue, restyled into the redesigned design language (see components/home/* and HomeView). - EVERY REAL BEHAVIOUR IS CARRIED OVER UNCHANGED — only the chrome changes: - - "ثبت نهایی سفارش" still posts to the real POST /orders endpoint. Legacy's + EVERY REAL BEHAVIOUR IS CARRIED OVER UNCHANGED — only the chrome changes: + - "ثبت نهایی سفارش" still posts to the real POST /orders endpoint. Legacy's own `/set-order` payload had two hardcoded placeholder numbers with the original author's comment "how to get?!"; the real Orders API computes gold weight server-side from productVariantId + wagePercent, so @@ -19,7 +19,7 @@ computed at order time. The honest total the cart can show is total weight, which is what it shows. - DROPPED: the old unscoped `.rtl { direction: ltr }` rule, which was dead — + DROPPED: the old unscoped `.rtl { direction: ltr }` rule, which was dead — the global `.rtl` in style.less carries !important, so a non-!important rule could never override it. It was previously kept "for fidelity" while the file was a faithful port; now that the file is deliberately redesigned, @@ -27,9 +27,9 @@ -->