-
@@ -77,7 +77,7 @@
{{ title }}
{{ productCode }}
- ·
+ ·
{{ categoryTitle }}
@@ -87,31 +87,31 @@
- ÙØ²Ù ØªÙØ±ÛبÛ
+ وزن تقریبی
{{ min_gram }}
- گرÙ
+ گرم
- اجرت
+ اجرت
{{ wageLabel }}
- درصد
+ درصد
-
Ø§ÙØªØ®Ø§Ø¨ تÙÙØ¹
+انتخاب تنوع
- {{ Number(selectedVariant.rashWeightGrams) }} گر٠+ {{ Number(selectedVariant.rashWeightGrams) }} گرم
@@ -137,7 +137,7 @@
/>
{{ item.title }}
- {{ Number(item.rashWeightGrams) }} گرÙ
+ {{ Number(item.rashWeightGrams) }} گرم
{{ stockChipShort(item) }}
@@ -150,32 +150,32 @@
class="pd__variantsMore"
@click="showAllVariants = !showAllVariants"
>
- {{ showAllVariants ? 'ÙÙ
Ø§ÛØ´ Ú©Ù
تر' : `+${variants.length - VARIANT_PREVIEW_COUNT} تÙÙØ¹ دÛگر` }}
+ {{ showAllVariants ? 'نمایش کمتر' : `+${variants.length - VARIANT_PREVIEW_COUNT} تنوع دیگر` }}
-
-
- {{ bodyOpen ? 'بستÙ' : 'اداÙ
Ù Ù
Ø·ÙØ¨' }}
+ {{ bodyOpen ? 'بستن' : 'ادامه مطلب' }}
@@ -203,7 +203,7 @@
٠شخصات
+مشخصات
ÙØ²Ù
- - {{ min_gram }} گر٠+
وزن
+ - {{ min_gram }} گرم
اجرت
- - {{ wageLabel }}Ùª +
اجرت
+ - {{ wageLabel }}٪
تÙÙØ¹
+ تنوع - {{ selectedVariant.title }}
دستÙâØ¨ÙØ¯Û
+ دستهبندی - {{ categoryTitle }}
Ø¨Ø±ÙØ¯
+ برند - {{ brandTitle }}
-
-
-
-
-
ØªÙØ¶ÛØØ§Øª
+توضیحات
{{ body }}
- Ø®Ø±ÛØ¯ اÙ
Ù
- Ø§ØµØ§ÙØª Ú©Ø§ÙØ§
+ خرید امن
+ اصالت کالا
-
+
@@ -264,7 +264,7 @@
type="button"
class="pd__previewThumb"
:class="{ 'is-on': i === imageIndex }"
- :aria-label="`تصÙÛØ± ${i + 1}`"
+ :aria-label="`تصویر ${i + 1}`"
@click="imageIndex = i"
>
@@ -280,12 +280,12 @@
+
@@ -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.
-->
-
- Ø¨Ø±Ø§Û Ø¯Ø³ØªØ±Ø³Û Ø¨Ù ØªÙ
اÙ
Û Ø§Ù
Ú©Ø§ÙØ§Øª ÙØ±Ù
Ø§ÙØ²Ø§Ø± {{ brand.name }}Ø Ø§Ø¨ØªØ¯Ø§ گزÛÙÙ ÙØ±Ùد/ثبت ÙØ§Ù
را ÙÙ
س Ú©ÙÛØ¯ ٠در ØµÙØ±Øª ÙØ¯Ø§Ø´ØªÙ ØØ³Ø§Ø¨ Ú©Ø§Ø±Ø¨Ø±ÛØ ثبت ÙØ§Ù
ÙÙ
اÛÛØ¯.
+ برای دسترسی به تمامی امکانات نرم افزار {{ brand.name }}، ابتدا گزینه ورود/ثبت نام را لمس کنید و در صورت نداشتن حساب کاربری، ثبت نام نمایید.
- ÙØ±Ùد/ثبت ÙØ§Ù
- Ø¨ÛØ®ÛاÙ
+ ورود/ثبت نام
+ بیخیال
@@ -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 @@
-
+
-