refactor(ui): replace modal dialogs with toast/sheet components

Remove DialogVant/NotifyVant and add Toast and Sheet components with
useToast, useReveal, and usePageTransition composables. Rework
Navigation, RegisterView, and styles across both apps; rebuild dist assets.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
s.esmaeili
2026-10-04 13:57:25 +03:30
co-authored by Claude Sonnet 5
parent 148b99c4a6
commit c05352d6fd
205 changed files with 3362 additions and 1701 deletions
@@ -81,7 +81,7 @@
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { showNotify } from 'vant'
import { toast } from '../../composables/useToast'
import PageBar from '../../components/ui/PageBar.vue'
import Spinner from '../../components/Spinner.vue'
import DialogVantOneButton from '../../components/DialogVantOneButton.vue'
@@ -106,7 +106,7 @@ async function submit() {
// The reference checked all three client-side before posting
// (completeProfile.vue:191-198); the server requires them too.
if (!form.business_license_image_id || !form.shop_image_id || !form.ncode_image_id) {
showNotify('لطفا تمامی عکس های خواسته شده را آپلود کنید')
toast.warning('لطفا تمامی عکس های خواسته شده را آپلود کنید')
return
}
@@ -162,7 +162,7 @@ function finish() {
padding: 14px;
border: none;
border-radius: 14px;
background: @ink;
background: @primary;
color: #fff;
font-family: inherit;
font-size: 15px;
+23 -11
View File
@@ -49,10 +49,14 @@
</header>
<PullRefresh v-model="refreshing" @refresh="onRefresh">
<container class="dash__body">
<template #pulling><Loading size="18" /></template>
<template #loosing><Loading size="18" /></template>
<template #loading><Loading size="18" /></template>
<template #success><span /></template>
<container class="dash__body" :class="{ 'is-revealed': revealed }">
<!-- Signed in: real position from GET /customers/:id -->
<template v-if="customer">
<section class="dash__balance">
<section class="dash__balance reveal" :style="revealDelay(0)">
<div class="dash__balanceHead">
<div>
<div class="dash__eyebrow">{{ remainText }}</div>
@@ -79,7 +83,7 @@
</template>
<!-- Signed out: a quiet invitation, no fabricated numbers -->
<section v-else class="dash__invite">
<section v-else class="dash__invite reveal" :style="revealDelay(0)">
<div class="dash__inviteIcon" aria-hidden="true">
<Icon icon="vuesax-linear:security-user" size="20" />
</div>
@@ -93,7 +97,7 @@
</section>
<!-- Nav grid -->
<section class="dash__section">
<section class="dash__section reveal" :style="revealDelay(1)">
<SectionHead title="دسترسی‌ها" subtitle="بخش‌های حساب شما" />
<div class="dash__grid">
<button
@@ -113,7 +117,7 @@
</section>
<!-- Live 18k rate — same strip the home screen uses -->
<section class="dash__section">
<section class="dash__section reveal" :style="revealDelay(2)">
<SectionHead title="نرخ روز" subtitle="آخرین نرخ اعلام‌شده" />
<RateStrip :quote="goldPrice" />
</section>
@@ -126,7 +130,7 @@
</template>
<template v-slot:title>آیا می خواهید از برنامه خارج شوید؟</template>
<template v-slot:button>
<div class="text-center text-[#1C2D56]" @click="logOut">بله</div>
<div class="text-center text-[#23493c]" @click="logOut">بله</div>
<div class="text-center text-[#6b7280]" @click="data.notify = false">بی خیال</div>
</template>
</DialogVant>
@@ -142,7 +146,7 @@
</div>
</template>
<template v-slot:button>
<div class="text-center text-[#1C2D56]" @click="router.push({ name: 'login' })">ورود/ثبت نام</div>
<div class="text-center text-[#23493c]" @click="router.push({ name: 'login' })">ورود/ثبت نام</div>
<div class="text-center text-danger" @click="data.loginNotify = false">بیخیال</div>
</template>
</DialogVant>
@@ -152,7 +156,7 @@
<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { PullRefresh } from 'vant'
import { Loading, PullRefresh } from 'vant'
import Icon from '../../components/icon/Icon.vue'
import SectionHead from '../../components/home/SectionHead.vue'
import RateStrip from '../../components/home/RateStrip.vue'
@@ -161,6 +165,7 @@ import exitImage from '../../assets/img/empty/dialogEmpty5.svg'
import loginDialogImage from '../../assets/img/empty/dialogEmpty1.svg'
import { price } from '../../utils/filters'
import { useSessionStore } from '../../stores/session'
import { useReveal } from '../../composables/useReveal'
import { api, type CustomerDetail, type GoldPriceQuote } from '../../api/client'
const router = useRouter()
@@ -243,7 +248,14 @@ async function onRefresh() {
refreshing.value = false
}
onMounted(loadDashboard)
// First-paint entrance, the same choreography HomeView uses: played once,
// after the first load; pull-to-refresh and tab returns don't replay it.
const { revealed, revealDelay, reveal } = useReveal('dashboard')
onMounted(async () => {
await loadDashboard()
reveal()
})
</script>
<style scoped lang="less">
@@ -311,7 +323,7 @@ onMounted(loadDashboard)
.dash__balance {
padding: 16px;
border-radius: 20px;
background: @ink;
background: @primary;
color: #fff;
box-shadow: 0 14px 30px -22px rgba(20, 22, 26, 0.95);
}
@@ -455,7 +467,7 @@ onMounted(loadDashboard)
font-size: 11.5px;
font-weight: 700;
color: #fff;
background: @ink;
background: @primary;
}
.dash__section {
@@ -105,6 +105,6 @@ onMounted(async () => {
font-size: 12.5px;
font-weight: 700;
color: #fff;
background: @ink;
background: @primary;
}
</style>
+8 -13
View File
@@ -19,8 +19,6 @@
-->
<template>
<div class="auth">
<NotifyVant :timeOut="2000" v-model="notify.show" :text="notify.text" :color="'background-color: #DC2626'" />
<PageBar title="ورود" back-to="home" />
<container class="auth__body">
@@ -88,7 +86,7 @@ import { useRoute, useRouter } from 'vue-router'
import { Form, FormItem, Input } from 'ant-design-vue'
import Icon from '../../components/icon/Icon.vue'
import PageBar from '../../components/ui/PageBar.vue'
import NotifyVant from '../../components/NotifyVant.vue'
import { toast } from '../../composables/useToast'
import OtpInput from '../../components/input/OtpInput.vue'
import { required, mobile as mobileRule } from '../../utils/validator'
import { useSessionStore } from '../../stores/session'
@@ -100,7 +98,6 @@ const route = useRoute()
const session = useSessionStore()
const formModel = reactive({ username: undefined as string | undefined })
const notify = reactive({ show: false, text: 'اطلاعات کاربری در سیستم وجود ندارد.' })
const body = reactive({
step: 1,
loading: false,
@@ -146,8 +143,7 @@ const sendPhone = async () => {
// 429 = the send throttle (too soon, or too many in the window). Its message
// is already Persian and user-facing, so show it rather than a generic one.
const text = e instanceof Error && /\(429\)/.test(e.message) ? extractServerMessage(e) : null
notify.text = text ?? 'خطا در ارسال کد. لطفاً دوباره تلاش کنید.'
notify.show = true
toast.error(text ?? 'خطا در ارسال کد. لطفاً دوباره تلاش کنید.')
} finally {
body.loading = false
}
@@ -177,14 +173,13 @@ const sendToken = async (otpCode: string) => {
}
const redirect = typeof route.query.redirect === 'string' ? route.query.redirect : undefined
await router.push(redirect ?? { name: 'home' })
notify.text = 'با موفقیت وارد شدید'
notify.show = true
toast.success('با موفقیت وارد شدید')
} catch (e: unknown) {
notify.text =
toast.error(
e instanceof Error && /\(429\)/.test(e.message)
? (extractServerMessage(e) ?? 'تعداد تلاش‌ها بیش از حد مجاز است.')
: 'کد وارد شده صحیح نیست'
notify.show = true
: 'کد وارد شده صحیح نیست',
)
} finally {
body.loading = false
}
@@ -286,7 +281,7 @@ const btnText = computed(() => (body.step === 1 ? 'دریافت رمز عبور'
font-size: 13px;
font-weight: 700;
color: #fff;
background: @ink;
background: @primary;
transition: transform 0.15s ease, opacity 0.15s ease;
}
@@ -338,7 +333,7 @@ const btnText = computed(() => (body.step === 1 ? 'دریافت رمز عبور'
.otp-input:focus {
outline: none;
border-color: @ink;
border-color: @primary;
}
.otp-input.error {
+4 -4
View File
@@ -261,8 +261,8 @@ onMounted(loadOrders)
.ord__chip.is-on {
color: #fff;
background: @ink;
border-color: @ink;
background: @primary;
border-color: @primary;
}
.ord__chip:active {
@@ -393,8 +393,8 @@ onMounted(loadOrders)
.detail__step.is-done .detail__stepDot {
color: #fff;
background: @ink;
border-color: @ink;
background: @primary;
border-color: @primary;
}
.detail__step.is-active .detail__stepDot {
+1 -1
View File
@@ -247,7 +247,7 @@ onMounted(async () => {
padding: 14px;
border: none;
border-radius: 14px;
background: @ink;
background: @primary;
color: #fff;
font-family: inherit;
font-size: 15px;
@@ -190,7 +190,7 @@ const register = async () => {
font-size: 13px;
font-weight: 700;
color: #fff;
background: @ink;
background: @primary;
transition: transform 0.15s ease;
}