feat: move brand name to one config and redesign the accounting card
اپ: نام برند در همهی صفحهها و پیامها یکسان شده و از یک تنظیم مرکزی خوانده میشود. اپ: کارت حسابداری در صفحهی مشتری بازطراحی شد؛ ماندهها و وضعیت لینک واضحتر نمایش داده میشوند. پنل: کارت حسابداری در پروندهی مشتری بازطراحی شد؛ ماندهها، آخرین ارسال و فرم لینک دستی واضحتر شدهاند. پنل: ستون «کد» در جدولها بهطور پیشفرض پنهان است و از انتخاب ستونها قابل نمایش است. پنل: تغییر نمایش ستونها در جدولها بلافاصله اعمال میشود. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 5.5
parent
5d2a1efb0c
commit
532ceee189
@@ -31,7 +31,7 @@
|
||||
/* Until the bundle's palette loads. */
|
||||
html.theme-dark, html.theme-dark body { background-color: #15171A !important; }
|
||||
</style>
|
||||
<title>طلای منتزر</title>
|
||||
<title>%BRAND_TITLE%</title>
|
||||
</head>
|
||||
<body style="background-color: #fff">
|
||||
<div class="w-screen h-screen fixed" style="background-color: var(--c-page, #fff)"></div>
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -275,6 +275,17 @@ export interface GoldPriceQuote {
|
||||
quotedAt: string;
|
||||
}
|
||||
|
||||
/** Body of POST customer/auth/register — the app register form, field for field. */
|
||||
export interface RegisterBody {
|
||||
fname: string;
|
||||
lname: string;
|
||||
mobile: string;
|
||||
store_name: string;
|
||||
phone: string;
|
||||
address: string;
|
||||
city: string;
|
||||
}
|
||||
|
||||
/** One admin-uploaded home banner slide — raw shape as saved by the admin
|
||||
* panel's manage/Const.vue "اسلایدر موبایل" tab (image + optional link). */
|
||||
export interface AdminSlide {
|
||||
@@ -460,6 +471,13 @@ export const api = {
|
||||
*/
|
||||
getCatalogFacets: () => request<CatalogFacets>('/catalog/products/facets'),
|
||||
|
||||
/** App self-registration. The new customer waits for admin approval before signing in. */
|
||||
register: (body: RegisterBody) =>
|
||||
request<{ ok: boolean }>('/customer/auth/register', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
}),
|
||||
|
||||
/**
|
||||
* Step 1 of login — ports henza_server ApiMobileController::userAuth.
|
||||
* Always resolves `{ sent: true }`, even for an unregistered number, so the
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,2 @@
|
||||
// The brand lives in the repo-root brand.ts, shared by all three frontends.
|
||||
export { brand } from '../../../brand';
|
||||
@@ -2,7 +2,7 @@
|
||||
<template>
|
||||
<Spin :spinning="spinning" :tip="tip" :size="(size as 'small' | 'default' | 'large')" :delay="delay" :class="fixed ? '!max-h-screen !fixed' : ''">
|
||||
<template #indicator>
|
||||
<img v-if="!indicator" src="/img/montzar-logo.png" alt="Montzar" class="spinner-brand" width="48" height="48" />
|
||||
<img v-if="!indicator" src="/img/brand-logo.png" :alt="brand.nameEn" class="spinner-brand" width="48" height="48" />
|
||||
<!-- keep-motion: a working spinner, exempt from reduced motion. -->
|
||||
<div class="loading keep-motion" v-else>
|
||||
<div class="effect-1 effects"></div>
|
||||
@@ -17,6 +17,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { brand } from '../brand'
|
||||
import { Spin } from 'ant-design-vue'
|
||||
|
||||
withDefaults(
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
:key="slide.key"
|
||||
class="showcase__slide"
|
||||
:class="{ 'is-active': i === index }"
|
||||
@click="onSlideClick(slide)"
|
||||
>
|
||||
<img v-if="slide.image" :src="slide.image" alt="" class="showcase__art" draggable="false" />
|
||||
</article>
|
||||
@@ -42,7 +41,7 @@
|
||||
type="button"
|
||||
class="showcase__dot"
|
||||
:class="{ 'is-active': i === index }"
|
||||
:aria-label="slide.title"
|
||||
:aria-label="`اسلاید ${i + 1}`"
|
||||
@click="go(i)"
|
||||
/>
|
||||
</div>
|
||||
@@ -53,21 +52,13 @@
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { useAppearanceStore } from '../../stores/appearanceStore'
|
||||
|
||||
/** A slide from the admin slider section. Display-only — it has no destination. */
|
||||
export interface ShowcaseSlide {
|
||||
key: string
|
||||
eyebrow: string
|
||||
title: string
|
||||
subtitle: string
|
||||
cta: string
|
||||
image?: string
|
||||
categoryId?: string
|
||||
/** Admin-uploaded slide's own destination (manage/Const.vue), when it has
|
||||
* one. Takes priority over `categoryId` in the click handler. */
|
||||
link?: string
|
||||
}
|
||||
|
||||
const props = defineProps<{ slides: ShowcaseSlide[] }>()
|
||||
const emit = defineEmits<{ (e: 'select', slide: ShowcaseSlide): void }>()
|
||||
|
||||
const index = ref(0)
|
||||
let timer: ReturnType<typeof setInterval> | undefined
|
||||
@@ -159,13 +150,6 @@ function onPointerCancel(e: PointerEvent) {
|
||||
isSwipe = false
|
||||
}
|
||||
|
||||
function onSlideClick(slide: ShowcaseSlide) {
|
||||
// A drag also fires a trailing click on release; only treat it as a tap
|
||||
// (i.e. open the slide) when the gesture never turned into a swipe.
|
||||
if (isSwipe) return
|
||||
emit('select', slide)
|
||||
}
|
||||
|
||||
const trackStyle = computed(() => {
|
||||
const base = -index.value * 100
|
||||
const dragPercent = trackWidth ? (dragX.value / trackWidth) * 100 : 0
|
||||
@@ -232,7 +216,6 @@ onBeforeUnmount(() => {
|
||||
flex: 0 0 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
// Clips its own image's scale-in (.showcase__art below) to this slide's own
|
||||
// box, so the 1.06x zoom never bleeds into the neighbouring slide.
|
||||
overflow: hidden;
|
||||
|
||||
@@ -279,7 +279,7 @@
|
||||
</template>
|
||||
<template v-slot:title>
|
||||
<div class="p-8 text-justify">
|
||||
برای دسترسی به تمامی امکانات نرم افزار منتزر، ابتدا گزینه ورود/ثبت نام را لمس کنید و در صورت نداشتن حساب کاربری، ثبت نام نمایید.
|
||||
برای دسترسی به تمامی امکانات نرم افزار {{ brand.name }}، ابتدا گزینه ورود/ثبت نام را لمس کنید و در صورت نداشتن حساب کاربری، ثبت نام نمایید.
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:button>
|
||||
@@ -291,6 +291,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { brand } from '../../brand'
|
||||
import { computed, onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import Icon from '../icon/Icon.vue'
|
||||
|
||||
@@ -10,9 +10,9 @@ import { useSessionStore } from './stores/session';
|
||||
import { useAppearanceStore } from './stores/appearanceStore';
|
||||
import Container from './components/ui/Container.vue';
|
||||
// TODO: temporary placeholder — swap Vector.svg back in (or a proper loading
|
||||
// mark) once one exists; montzar-logo.png is the brand logo, borrowed here
|
||||
// mark) once one exists; brand-logo.png is the brand logo, borrowed here
|
||||
// just so lazy-loaded images have something other than a broken icon.
|
||||
import Vector from './assets/img/montzar-logo.png';
|
||||
import Vector from './assets/img/brand-logo.png';
|
||||
|
||||
// Global styles, ported from legacy henza_pwa/src/main.ts — same import order
|
||||
// (ant-design-vue's own theme first, then our Less overrides on top). Legacy
|
||||
|
||||
@@ -29,7 +29,8 @@ import { defineStore } from 'pinia'
|
||||
import { api } from '../api/client'
|
||||
import shippingArt from '../assets/img/shipping-and-delivery.png'
|
||||
import competitionArt from '../assets/img/sports-and-competition.png'
|
||||
import brandArt from '../assets/img/montzar-logo.png'
|
||||
import brandArt from '../assets/img/brand-logo.png'
|
||||
import { brand } from '../brand'
|
||||
|
||||
export interface Slide {
|
||||
text: string | undefined
|
||||
@@ -54,13 +55,13 @@ const PLACEHOLDER_SLIDES: Slide[] = [
|
||||
{
|
||||
type: 'popup',
|
||||
src: competitionArt,
|
||||
text: 'جدیدترین محصولات منتزر را در بخش محصولات جدید ببینید.',
|
||||
text: `جدیدترین محصولات ${brand.name} را در بخش محصولات جدید ببینید.`,
|
||||
link: undefined,
|
||||
},
|
||||
{
|
||||
type: 'popup',
|
||||
src: brandArt,
|
||||
text: 'منتزر، همراه شما در خرید و فروش طلا.',
|
||||
text: `${brand.name}، همراه شما در خرید و فروش طلا.`,
|
||||
link: undefined,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<span v-if="cart.items.length" class="home__badge">{{ cart.items.length }}</span>
|
||||
</router-link>
|
||||
|
||||
<img src="/img/montzar-logo.png" alt="منتزر" class="home__logo" />
|
||||
<img src="/img/brand-logo.png" :alt="brand.name" class="home__logo" />
|
||||
|
||||
<router-link :to="{ name: 'search' }" class="home__iconBtn" aria-label="جستجو">
|
||||
<Icon icon="vuesax-linear:search-normal-1" size="21" />
|
||||
@@ -48,7 +48,6 @@
|
||||
:slides="slides"
|
||||
class="home__hero reveal"
|
||||
:style="revealDelay(1)"
|
||||
@select="onSlide"
|
||||
/>
|
||||
|
||||
<CategoryRail
|
||||
@@ -62,7 +61,7 @@
|
||||
|
||||
<section v-if="featured" class="home__section reveal" :style="revealDelay(3)">
|
||||
<SectionHead
|
||||
title="برگزیده منتزر"
|
||||
:title="`برگزیده ${brand.name}`"
|
||||
subtitle="انتخابشده از میان مجموعههای موجود"
|
||||
action-label="همه محصولات"
|
||||
@action="goSearch"
|
||||
@@ -117,6 +116,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { brand } from '../brand'
|
||||
import { computed, onActivated, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Loading, PullRefresh } from 'vant'
|
||||
@@ -164,50 +164,15 @@ const featured = computed<ProductSummary | null>(() => products.value[0] ?? null
|
||||
const gridProducts = computed(() => products.value.slice(1, 5))
|
||||
const newestProducts = computed(() => products.value.slice(5, 13))
|
||||
|
||||
// The hero prefers the admin-uploaded banner (manage/Const.vue "اسلایدر
|
||||
// موبایل") when one has been configured; otherwise it falls back to a hero
|
||||
// built from REAL categories, since no curated banner payload existed before
|
||||
// that admin screen was wired up. Each fallback slide deep-links into search
|
||||
// filtered by that category, so the artwork is decorative but the
|
||||
// destination is genuine.
|
||||
const SLIDE_COPY = [
|
||||
{ eyebrow: 'مجموعه منتزر', cta: 'دیدن مجموعه' },
|
||||
{ eyebrow: 'انتخاب سردبیر', cta: 'مشاهده' },
|
||||
{ eyebrow: 'پیشنهاد این هفته', cta: 'بررسی کنید' },
|
||||
]
|
||||
|
||||
const slides = computed<ShowcaseSlide[]>(() => {
|
||||
if (adminSlides.value?.length) {
|
||||
return adminSlides.value
|
||||
.filter((s) => s.image)
|
||||
.map((s, i) => ({
|
||||
key: `${i}-${s.image}`,
|
||||
eyebrow: 'مجموعه منتزر',
|
||||
title: '',
|
||||
subtitle: '',
|
||||
cta: '',
|
||||
image: s.image,
|
||||
link: s.link || undefined,
|
||||
}))
|
||||
}
|
||||
|
||||
const cats = memory.cats.slice(0, 3)
|
||||
if (!cats.length) return []
|
||||
return cats.map((cat, i) => ({
|
||||
key: cat.id,
|
||||
eyebrow: SLIDE_COPY[i % SLIDE_COPY.length].eyebrow,
|
||||
title: cat.title,
|
||||
subtitle: 'ساختهشده از طلای ۱۸ عیار، با اجرت شفاف و اصالت تضمینشده.',
|
||||
cta: SLIDE_COPY[i % SLIDE_COPY.length].cta,
|
||||
image: productImageForCategory(cat.id),
|
||||
categoryId: cat.id,
|
||||
}))
|
||||
})
|
||||
|
||||
function productImageForCategory(categoryId: string): string | undefined {
|
||||
const match = products.value.find((p) => p.category?.id === categoryId)
|
||||
return match?.images?.[0]?.url
|
||||
}
|
||||
// The hero shows ONLY what the admin uploaded in the slider section
|
||||
// (manage/Const.vue "اسلایدر موبایل"). It used to fall back to slides built
|
||||
// from categories and filled with product photos, so a newly added product
|
||||
// could land in the hero. Slides are display-only: tapping one does nothing.
|
||||
const slides = computed<ShowcaseSlide[]>(() =>
|
||||
(adminSlides.value ?? [])
|
||||
.filter((s) => s.image)
|
||||
.map((s, i) => ({ key: `${i}-${s.image}`, image: s.image })),
|
||||
)
|
||||
|
||||
// Opens as a morph out of the tapped card (ProductMorph.vue, mounted in
|
||||
// App.vue) — the card is found from the tap, so the emit chain stays as is.
|
||||
@@ -220,24 +185,6 @@ function onCategory(cat: HomeCategory) {
|
||||
router.push({ name: 'search' })
|
||||
}
|
||||
|
||||
function onSlide(slide: ShowcaseSlide) {
|
||||
// Admin-uploaded slides (manage/Const.vue) carry their own destination
|
||||
// instead of a categoryId — an absolute URL opens in a new tab, anything
|
||||
// else is treated as an in-app path.
|
||||
if (slide.link) {
|
||||
if (/^https?:\/\//i.test(slide.link)) {
|
||||
window.open(slide.link, '_blank', 'noopener')
|
||||
} else {
|
||||
router.push(slide.link)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (slide.categoryId) {
|
||||
filters.query.cats = [slide.categoryId]
|
||||
}
|
||||
router.push({ name: 'search' })
|
||||
}
|
||||
|
||||
function goSearch() {
|
||||
router.push({ name: 'search' })
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
</template>
|
||||
<template v-slot:title>
|
||||
<div class="p-8 text-justify">
|
||||
برای دسترسی به تمامی امکانات نرم افزار منتزر، ابتدا گزینه ورود/ثبت نام را لمس کنید و در
|
||||
برای دسترسی به تمامی امکانات نرم افزار {{ brand.name }}، ابتدا گزینه ورود/ثبت نام را لمس کنید و در
|
||||
صورت نداشتن حساب کاربری، ثبت نام نمایید.
|
||||
</div>
|
||||
</template>
|
||||
@@ -154,6 +154,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { brand } from '../../brand'
|
||||
import { computed, onActivated, onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Loading, PullRefresh } from 'vant'
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="auth__mark" aria-hidden="true">
|
||||
<Icon icon="vuesax-linear:security-user" size="28" />
|
||||
</div>
|
||||
<h2 class="auth__title">ورود به منتزر</h2>
|
||||
<h2 class="auth__title">ورود به {{ brand.name }}</h2>
|
||||
<p class="auth__sub">
|
||||
{{ body.step === 1 ? 'شماره موبایل خود را وارد کنید' : 'رمز یکبارمصرف را وارد کنید' }}
|
||||
</p>
|
||||
@@ -81,6 +81,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { brand } from '../../brand'
|
||||
import { computed, nextTick, reactive, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { Form, FormItem, Input } from 'ant-design-vue'
|
||||
@@ -148,7 +149,7 @@ const sendPhone = async () => {
|
||||
// very confusion the 404 was added to remove: they would still not
|
||||
// know whether the SMS was late or the number was never on file.
|
||||
const text =
|
||||
e instanceof Error && /\((?:429|404)\)/.test(e.message) ? extractServerMessage(e) : null
|
||||
e instanceof Error && /\((?:429|404|403)\)/.test(e.message) ? extractServerMessage(e) : null
|
||||
toast.error(text ?? 'خطا در ارسال کد. لطفاً دوباره تلاش کنید.')
|
||||
} finally {
|
||||
body.loading = false
|
||||
|
||||
@@ -12,11 +12,9 @@
|
||||
plain text input. That whole complex, and the parallel unused
|
||||
`userFormModel`/`defaultUserFormModel` objects, are not ported.
|
||||
|
||||
STUBBED, not real, and unchanged from the previous version: legacy's
|
||||
POST /major/register — apps/api has no create-customer endpoint. Submitting
|
||||
shows legacy's exact success message without persisting anything. The copy
|
||||
says the account awaits review, which is what legacy said too, so the screen
|
||||
does not claim an account exists when none was created.
|
||||
Submits to POST customer/auth/register (legacy POST /major/register). The new
|
||||
customer is created inactive and lands in the admin's «مشتریان در انتظار»
|
||||
list; they can sign in once an admin approves them.
|
||||
-->
|
||||
<template>
|
||||
<div class="auth">
|
||||
@@ -97,6 +95,8 @@ import PageBar from '../../components/ui/PageBar.vue'
|
||||
import Spinner from '../../components/Spinner.vue'
|
||||
import DialogVant from '../../components/DialogVant.vue'
|
||||
import { required, mobile as mobileRule } from '../../utils/validator'
|
||||
import { api } from '../../api/client'
|
||||
import { toast } from '../../composables/useToast'
|
||||
|
||||
const emit = defineEmits<{ (e: 'register', value: boolean): void }>()
|
||||
const router = useRouter()
|
||||
@@ -117,12 +117,34 @@ const formModel = reactive({
|
||||
const register = async () => {
|
||||
try {
|
||||
profileData.loading = true
|
||||
// Stubbed — no create-customer endpoint exists in apps/api yet.
|
||||
await api.register({
|
||||
fname: String(formModel.fname ?? ''),
|
||||
lname: String(formModel.lname ?? ''),
|
||||
mobile: String(formModel.mobile ?? ''),
|
||||
store_name: String(formModel.store_name ?? ''),
|
||||
phone: String(formModel.phone ?? ''),
|
||||
address: String(formModel.address ?? ''),
|
||||
city: String(formModel.city ?? ''),
|
||||
})
|
||||
registeredInfo.value = true
|
||||
} catch (e: unknown) {
|
||||
toast.error(serverMessage(e) ?? 'خطا در ثبت اطلاعات. لطفاً دوباره تلاش کنید.')
|
||||
} finally {
|
||||
profileData.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
/** The API's Persian `message` (duplicate number, bad mobile, throttle), if any. */
|
||||
function serverMessage(e: unknown): string | null {
|
||||
const match = e instanceof Error ? e.message.match(/\{.*\}/) : null
|
||||
if (!match) return null
|
||||
try {
|
||||
const parsed = JSON.parse(match[0]) as { message?: string | string[] }
|
||||
return Array.isArray(parsed.message) ? parsed.message[0] ?? null : parsed.message ?? null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
@@ -14,7 +14,7 @@ const curve = {
|
||||
px: 'px',
|
||||
}
|
||||
const vars = {
|
||||
prefix: 'montzar',
|
||||
prefix: 'zartaj',
|
||||
// headers
|
||||
'font-size': curve.font + curve.px,
|
||||
'h1-font-size': curve.font * 2 + curve.px,
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"root":["./src/app.vue","./src/env.d.ts","./src/main.ts","./src/sw.ts","./src/api/cachedreads.ts","./src/api/client.ts","./src/components/appupdategate.vue","./src/components/dialogvant.vue","./src/components/dialogvantonebutton.vue","./src/components/emptystate.vue","./src/components/spinner.vue","./src/components/expert/actingasbar.vue","./src/components/home/categoryrail.vue","./src/components/home/ratestrip.vue","./src/components/home/sectionhead.vue","./src/components/home/showcasecard.vue","./src/components/home/showcaseslider.vue","./src/components/icon/icon.vue","./src/components/input/otpinput.vue","./src/components/input/singleotpinput.vue","./src/components/product/cartitem.vue","./src/components/product/galleryslider.vue","./src/components/product/ordercard.vue","./src/components/product/orderitemcard.vue","./src/components/product/productcard.vue","./src/components/product/productmorph.vue","./src/components/product/singleproduct.vue","./src/components/search/filters.vue","./src/components/search/searchinput.vue","./src/components/ui/container.vue","./src/components/ui/dropdown.vue","./src/components/ui/navigation.vue","./src/components/ui/pagebar.vue","./src/components/ui/scrollfetchsearch.vue","./src/components/ui/toastcard.vue","./src/components/ui/toasthost.vue","./src/components/ui/toggleswitch.vue","./src/components/upload/singleimageuploader.vue","./src/composables/useappupdate.ts","./src/composables/useorderitemproduct.ts","./src/composables/usepagetransition.ts","./src/composables/useproduct.ts","./src/composables/useproductdetail.ts","./src/composables/useproductmorph.ts","./src/composables/useproducts.ts","./src/composables/usereveal.ts","./src/composables/usetoast.ts","./src/layout/fullpagelayout.vue","./src/layout/masterlayout.vue","./src/router/index.ts","./src/stores/appearancestore.ts","./src/stores/cartstore.ts","./src/stores/catalogstore.ts","./src/stores/configstore.ts","./src/stores/favoritesstore.ts","./src/stores/filtersstore.ts","./src/stores/navstore.ts","./src/stores/querycache.ts","./src/stores/session.ts","./src/stores/transitionstore.ts","./src/stores/wagequotestore.ts","./src/stores/windowdatastore.ts","./src/types/moment-jalaali-shim.d.ts","./src/types/swiper-shim.d.ts","./src/utils/checkaccess.ts","./src/utils/filters.ts","./src/utils/orderstatus.ts","./src/utils/validator.ts","./src/views/cartview.vue","./src/views/homeview.vue","./src/views/newestview.vue","./src/views/expert/childordersview.vue","./src/views/expert/customerchoiceview.vue","./src/views/report/reportdocument.vue","./src/views/report/reportview.vue","./src/views/search/collectionview.vue","./src/views/search/searchview.vue","./src/views/user/appearanceview.vue","./src/views/user/completeprofileview.vue","./src/views/user/dashboardview.vue","./src/views/user/favoritesview.vue","./src/views/user/iamcallback.vue","./src/views/user/loginregisterview.vue","./src/views/user/loginview.vue","./src/views/user/notificationsview.vue","./src/views/user/ordersview.vue","./src/views/user/profileview.vue","./src/views/user/registerview.vue","./src/views/user/transactionsview.vue"],"version":"5.9.3"}
|
||||
{"root":["./src/app.vue","./src/brand.ts","./src/env.d.ts","./src/main.ts","./src/sw.ts","./src/api/cachedreads.ts","./src/api/client.ts","./src/components/appupdategate.vue","./src/components/dialogvant.vue","./src/components/dialogvantonebutton.vue","./src/components/emptystate.vue","./src/components/spinner.vue","./src/components/expert/actingasbar.vue","./src/components/home/categoryrail.vue","./src/components/home/ratestrip.vue","./src/components/home/sectionhead.vue","./src/components/home/showcasecard.vue","./src/components/home/showcaseslider.vue","./src/components/icon/icon.vue","./src/components/input/otpinput.vue","./src/components/input/singleotpinput.vue","./src/components/product/cartitem.vue","./src/components/product/galleryslider.vue","./src/components/product/ordercard.vue","./src/components/product/orderitemcard.vue","./src/components/product/productcard.vue","./src/components/product/productmorph.vue","./src/components/product/singleproduct.vue","./src/components/search/filters.vue","./src/components/search/searchinput.vue","./src/components/ui/container.vue","./src/components/ui/dropdown.vue","./src/components/ui/navigation.vue","./src/components/ui/pagebar.vue","./src/components/ui/scrollfetchsearch.vue","./src/components/ui/toastcard.vue","./src/components/ui/toasthost.vue","./src/components/ui/toggleswitch.vue","./src/components/upload/singleimageuploader.vue","./src/composables/useappupdate.ts","./src/composables/useorderitemproduct.ts","./src/composables/usepagetransition.ts","./src/composables/useproduct.ts","./src/composables/useproductdetail.ts","./src/composables/useproductmorph.ts","./src/composables/useproducts.ts","./src/composables/usereveal.ts","./src/composables/usetoast.ts","./src/layout/fullpagelayout.vue","./src/layout/masterlayout.vue","./src/router/index.ts","./src/stores/appearancestore.ts","./src/stores/cartstore.ts","./src/stores/catalogstore.ts","./src/stores/configstore.ts","./src/stores/favoritesstore.ts","./src/stores/filtersstore.ts","./src/stores/navstore.ts","./src/stores/querycache.ts","./src/stores/session.ts","./src/stores/transitionstore.ts","./src/stores/wagequotestore.ts","./src/stores/windowdatastore.ts","./src/types/moment-jalaali-shim.d.ts","./src/types/swiper-shim.d.ts","./src/utils/checkaccess.ts","./src/utils/filters.ts","./src/utils/orderstatus.ts","./src/utils/validator.ts","./src/views/cartview.vue","./src/views/homeview.vue","./src/views/newestview.vue","./src/views/expert/childordersview.vue","./src/views/expert/customerchoiceview.vue","./src/views/report/reportdocument.vue","./src/views/report/reportview.vue","./src/views/search/collectionview.vue","./src/views/search/searchview.vue","./src/views/user/appearanceview.vue","./src/views/user/completeprofileview.vue","./src/views/user/dashboardview.vue","./src/views/user/favoritesview.vue","./src/views/user/iamcallback.vue","./src/views/user/loginregisterview.vue","./src/views/user/loginview.vue","./src/views/user/notificationsview.vue","./src/views/user/ordersview.vue","./src/views/user/profileview.vue","./src/views/user/registerview.vue","./src/views/user/transactionsview.vue"],"version":"5.9.3"}
|
||||
@@ -2,6 +2,7 @@ import { fileURLToPath, URL } from 'node:url';
|
||||
import { defineConfig, loadEnv } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { appVersion } from '../../scripts/app-version.mjs';
|
||||
import { brand } from '../../brand';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
import { $themeLessVars } from './themeConfig/themeConfig';
|
||||
import { lessAlphaPlugin, lightHex, palettePlugin } from './themeConfig/appearance';
|
||||
@@ -18,6 +19,8 @@ export default defineConfig({
|
||||
plugins: [
|
||||
// Build number + «what changed» notes → version.json (scripts/app-version.mjs).
|
||||
appVersion('user-app'),
|
||||
// <title> comes from the repo-root brand.ts, like every other brand text.
|
||||
{ name: 'brand-title', transformIndexHtml: (html: string) => html.replace('%BRAND_TITLE%', brand.appTitle) },
|
||||
vue(),
|
||||
// virtual:palette.css — the light/dark values behind the theme tokens.
|
||||
palettePlugin(),
|
||||
@@ -32,9 +35,9 @@ export default defineConfig({
|
||||
// legacy ran the SW in dev too (mode: "development")
|
||||
devOptions: { enabled: true, type: 'module' },
|
||||
manifest: {
|
||||
name: 'Montzar',
|
||||
short_name: 'Montzar',
|
||||
description: 'Montzar',
|
||||
name: brand.appTitle,
|
||||
short_name: brand.nameEn,
|
||||
description: brand.appTitle,
|
||||
theme_color: lightHex.primary,
|
||||
background_color: lightHex.primary,
|
||||
start_url: '/',
|
||||
|
||||
Reference in New Issue
Block a user