Initial commit: fabric-trade-user Vue 3 app with mobile and desktop layouts
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,723 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useOrderStore } from '@/stores/order'
|
||||
import IconChevronLeft from '@/components/icons/IconChevronLeft.vue'
|
||||
import IconEditPen from '@/components/icons/IconEditPen.vue'
|
||||
import IconOrders from '@/components/icons/IconOrders.vue'
|
||||
import IconCheckBook from '@/components/icons/IconCheckBook.vue'
|
||||
import IconCash from '@/components/icons/IconCash.vue'
|
||||
import IconBuilding from '@/components/icons/IconBuilding.vue'
|
||||
import IconBolt from '@/components/icons/IconBolt.vue'
|
||||
import IconSettings from '@/components/icons/IconSettings.vue'
|
||||
import IconProfile from '@/components/icons/IconProfile.vue'
|
||||
import IconBell from '@/components/icons/IconBell.vue'
|
||||
import IconMapPin from '@/components/icons/IconMapPin.vue'
|
||||
import IconHeadset from '@/components/icons/IconHeadset.vue'
|
||||
import IconCashSettlement from '@/components/icons/IconCashSettlement.vue'
|
||||
import IconPhone from '@/components/icons/IconPhone.vue'
|
||||
import IconMail from '@/components/icons/IconMail.vue'
|
||||
import IconVerified from '@/components/icons/IconVerified.vue'
|
||||
import IconLayoutGrid from '@/components/icons/IconLayoutGrid.vue'
|
||||
import IconLock from '@/components/icons/IconLock.vue'
|
||||
import IconIdCard from '@/components/icons/IconIdCard.vue'
|
||||
import { formatCurrency, formatDate, formatNumber, toPersianDigits } from '@/utils/format'
|
||||
import type { Component } from 'vue'
|
||||
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
const orderStore = useOrderStore()
|
||||
|
||||
const emit = defineEmits<{ logout: [] }>()
|
||||
|
||||
const initials = computed(() => {
|
||||
const first = auth.user?.firstName?.[0] ?? ''
|
||||
const last = auth.user?.lastName?.[0] ?? ''
|
||||
return `${first}${last}` || '؟'
|
||||
})
|
||||
|
||||
const ordersCount = computed(() => {
|
||||
if (!auth.user) return 0
|
||||
return orderStore.userOrders(auth.user.id).length
|
||||
})
|
||||
|
||||
const checksCount = computed(() => {
|
||||
if (!auth.user) return 0
|
||||
const submitted = orderStore
|
||||
.userOrders(auth.user.id)
|
||||
.reduce((sum, order) => sum + (order.checks?.length ?? 0), 0)
|
||||
return submitted + orderStore.draftChecks.length
|
||||
})
|
||||
|
||||
const customerCode = computed(() => {
|
||||
if (auth.user?.nationalId) return toPersianDigits(auth.user.nationalId.slice(-6).padStart(6, '0'))
|
||||
return toPersianDigits(auth.user?.id.slice(-6) ?? '—')
|
||||
})
|
||||
|
||||
const displayEmail = computed(() => {
|
||||
const phone = auth.user?.phone ?? ''
|
||||
if (!phone) return '—'
|
||||
return `user${phone.slice(-4)}@parsbaft.ir`
|
||||
})
|
||||
|
||||
const joinDate = computed(() =>
|
||||
auth.user?.createdAt ? formatDate(auth.user.createdAt) : '—',
|
||||
)
|
||||
|
||||
const creditBalance = computed(() => 2_450_000)
|
||||
|
||||
const companyRows = computed(() => [
|
||||
{ label: 'نام شرکت', value: auth.fullName || '—' },
|
||||
{ label: 'شناسه ملی', value: auth.user?.nationalId ? toPersianDigits(auth.user.nationalId) : '—' },
|
||||
{
|
||||
label: 'کد اقتصادی',
|
||||
value: auth.user?.nationalId ? toPersianDigits(`4${auth.user.nationalId.slice(0, 10)}`) : '—',
|
||||
},
|
||||
{ label: 'تاریخ عضویت', value: joinDate.value },
|
||||
{ label: 'نوع مشتری', value: 'مشتری عمده', badge: true },
|
||||
{ label: 'وضعیت حساب', value: 'فعال', badge: true },
|
||||
])
|
||||
|
||||
const quickAccess = [
|
||||
{ label: 'حسابهای بانکی', icon: IconCashSettlement },
|
||||
{ label: 'آدرسها', icon: IconMapPin },
|
||||
{ label: 'حمایت و پشتیبانی', icon: IconHeadset, href: 'tel:02112345678' },
|
||||
{ label: 'فاکتورهای من', icon: IconOrders, path: '/orders' },
|
||||
]
|
||||
|
||||
const settingsItems: Array<{
|
||||
id: string
|
||||
label: string
|
||||
description: string
|
||||
icon: Component
|
||||
path?: string
|
||||
action?: 'logout'
|
||||
}> = [
|
||||
{
|
||||
id: 'profile',
|
||||
label: 'اطلاعات کاربری',
|
||||
description: 'ویرایش نام، موبایل و ایمیل',
|
||||
icon: IconProfile,
|
||||
path: '/profile/edit',
|
||||
},
|
||||
{
|
||||
id: 'security',
|
||||
label: 'امنیت حساب',
|
||||
description: 'تغییر رمز عبور و تنظیمات ورود',
|
||||
icon: IconLock,
|
||||
path: '/profile/edit',
|
||||
},
|
||||
{
|
||||
id: 'notifications',
|
||||
label: 'اعلانها',
|
||||
description: 'مدیریت اعلانهای ایمیل و پیامک',
|
||||
icon: IconBell,
|
||||
path: '/profile/edit',
|
||||
},
|
||||
{
|
||||
id: 'display',
|
||||
label: 'تنظیمات نمایش',
|
||||
description: 'زبان، واحد پول و حالت نمایش',
|
||||
icon: IconLayoutGrid,
|
||||
path: '/profile/edit',
|
||||
},
|
||||
]
|
||||
|
||||
function handleQuick(item: (typeof quickAccess)[number]) {
|
||||
if (item.path) router.push(item.path)
|
||||
else if (item.href) window.location.href = item.href
|
||||
}
|
||||
|
||||
function handleSetting(item: (typeof settingsItems)[number]) {
|
||||
if (item.action === 'logout') {
|
||||
emit('logout')
|
||||
return
|
||||
}
|
||||
if (item.path) router.push(item.path)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="desktop-page desktop-profile">
|
||||
<header class="desktop-profile__head">
|
||||
<h1 class="desktop-profile__title">پروفایل</h1>
|
||||
<p class="desktop-profile__subtitle">مدیریت اطلاعات حساب کاربری و تنظیمات</p>
|
||||
</header>
|
||||
|
||||
<section class="profile-overview">
|
||||
<div class="profile-overview__identity">
|
||||
<div class="profile-overview__aside">
|
||||
<div class="profile-overview__avatar">{{ initials }}</div>
|
||||
<button
|
||||
type="button"
|
||||
class="profile-overview__edit"
|
||||
@click="router.push('/profile/edit')"
|
||||
>
|
||||
<IconEditPen :size="16" />
|
||||
ویرایش اطلاعات
|
||||
</button>
|
||||
</div>
|
||||
<div class="profile-overview__details">
|
||||
<div class="profile-overview__name-row">
|
||||
<h2 class="profile-overview__name">{{ auth.fullName }}</h2>
|
||||
<span class="profile-overview__verified" title="حساب تأیید شده">
|
||||
<IconVerified :size="18" />
|
||||
</span>
|
||||
</div>
|
||||
<p class="profile-overview__meta">
|
||||
<IconIdCard :size="15" class="profile-overview__meta-icon" />
|
||||
<span class="profile-overview__meta-label">کد مشتری:</span>
|
||||
<span>{{ customerCode }}</span>
|
||||
</p>
|
||||
<p class="profile-overview__meta">
|
||||
<IconPhone :size="15" class="profile-overview__meta-icon" />
|
||||
<span dir="ltr">{{ auth.user?.phone }}</span>
|
||||
</p>
|
||||
<p class="profile-overview__meta">
|
||||
<IconMail :size="15" class="profile-overview__meta-icon" />
|
||||
<span dir="ltr">{{ displayEmail }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-overview__stats">
|
||||
<article class="profile-stat">
|
||||
<span class="profile-stat__icon profile-stat__icon--amber">
|
||||
<IconCash :size="20" />
|
||||
</span>
|
||||
<strong class="profile-stat__value profile-stat__value--sm">
|
||||
{{ formatCurrency(creditBalance) }}
|
||||
</strong>
|
||||
<span class="profile-stat__label">موجودی اعتبار</span>
|
||||
<button type="button" class="profile-stat__link">مشاهده جزئیات</button>
|
||||
</article>
|
||||
|
||||
<article class="profile-stat">
|
||||
<span class="profile-stat__icon profile-stat__icon--green">
|
||||
<IconCheckBook :size="20" />
|
||||
</span>
|
||||
<strong class="profile-stat__value">{{ formatNumber(checksCount) }}</strong>
|
||||
<span class="profile-stat__label">چک فعال</span>
|
||||
<button type="button" class="profile-stat__link" @click="router.push('/profile/checks')">
|
||||
مشاهده همه
|
||||
</button>
|
||||
</article>
|
||||
|
||||
<article class="profile-stat">
|
||||
<span class="profile-stat__icon profile-stat__icon--blue">
|
||||
<IconOrders :size="20" />
|
||||
</span>
|
||||
<strong class="profile-stat__value">{{ formatNumber(ordersCount) }}</strong>
|
||||
<span class="profile-stat__label">سفارش ثبتشده</span>
|
||||
<button type="button" class="profile-stat__link" @click="router.push('/orders')">
|
||||
مشاهده همه
|
||||
</button>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="desktop-profile__grid">
|
||||
<section class="profile-panel profile-panel--settings">
|
||||
<h3 class="profile-panel__title">
|
||||
<IconSettings :size="18" />
|
||||
تنظیمات حساب
|
||||
</h3>
|
||||
<nav class="profile-settings" aria-label="تنظیمات حساب">
|
||||
<button
|
||||
v-for="item in settingsItems"
|
||||
:key="item.id"
|
||||
type="button"
|
||||
class="profile-settings__item"
|
||||
@click="handleSetting(item)"
|
||||
>
|
||||
<span class="profile-settings__icon">
|
||||
<component :is="item.icon" :size="20" />
|
||||
</span>
|
||||
<span class="profile-settings__body">
|
||||
<strong class="profile-settings__label">{{ item.label }}</strong>
|
||||
<span class="profile-settings__desc">{{ item.description }}</span>
|
||||
</span>
|
||||
<IconChevronLeft :size="16" class="profile-settings__chevron" aria-hidden="true" />
|
||||
</button>
|
||||
</nav>
|
||||
</section>
|
||||
|
||||
<section class="profile-panel profile-panel--quick">
|
||||
<h3 class="profile-panel__title">
|
||||
<IconBolt :size="18" />
|
||||
دسترسی سریع
|
||||
</h3>
|
||||
<div class="profile-quick-grid">
|
||||
<button
|
||||
v-for="item in quickAccess"
|
||||
:key="item.label"
|
||||
type="button"
|
||||
class="profile-quick-grid__item"
|
||||
@click="handleQuick(item)"
|
||||
>
|
||||
<span class="profile-quick-grid__icon">
|
||||
<component :is="item.icon" :size="22" />
|
||||
</span>
|
||||
<span class="profile-quick-grid__label">{{ item.label }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="profile-panel profile-panel--company">
|
||||
<h3 class="profile-panel__title">
|
||||
<IconBuilding :size="18" />
|
||||
اطلاعات شرکت
|
||||
</h3>
|
||||
<dl class="profile-info-list">
|
||||
<div
|
||||
v-for="row in companyRows"
|
||||
:key="row.label"
|
||||
class="profile-info-list__row"
|
||||
>
|
||||
<dt>{{ row.label }}</dt>
|
||||
<dd>
|
||||
<span v-if="row.badge" class="profile-badge">{{ row.value }}</span>
|
||||
<template v-else>{{ row.value }}</template>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.desktop-profile {
|
||||
max-width: 980px;
|
||||
margin-inline: auto;
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
|
||||
.desktop-profile__head {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.desktop-profile__title {
|
||||
margin: 0 0 6px;
|
||||
font-size: 1.375rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.desktop-profile__subtitle {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.profile-overview {
|
||||
direction: rtl;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 0;
|
||||
padding: 22px 24px;
|
||||
margin-bottom: 16px;
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
|
||||
.profile-overview__identity {
|
||||
flex: 1.1;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 18px;
|
||||
padding-inline-end: 24px;
|
||||
border-inline-end: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.profile-overview__aside {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.profile-overview__avatar {
|
||||
width: 92px;
|
||||
height: 92px;
|
||||
border-radius: 50%;
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.875rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.profile-overview__edit {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
min-width: 132px;
|
||||
padding: 9px 14px;
|
||||
border: 1px solid var(--color-primary);
|
||||
border-radius: var(--radius-md);
|
||||
background: #fff;
|
||||
color: var(--color-primary);
|
||||
font-family: inherit;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.profile-overview__edit:hover {
|
||||
background: var(--color-primary-soft);
|
||||
}
|
||||
|
||||
.profile-overview__details {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.profile-overview__name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.profile-overview__name {
|
||||
margin: 0;
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.profile-overview__verified {
|
||||
color: var(--color-success);
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.profile-overview__meta {
|
||||
margin: 0 0 8px;
|
||||
font-size: 0.8125rem;
|
||||
color: var(--color-text-muted);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.profile-overview__meta-icon {
|
||||
color: #94a3b8;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.profile-overview__meta-label {
|
||||
color: var(--color-text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.profile-overview__stats {
|
||||
direction: ltr;
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.profile-stat {
|
||||
direction: rtl;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: 12px 14px;
|
||||
border-inline-start: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.profile-stat:first-child {
|
||||
border-inline-start: none;
|
||||
}
|
||||
|
||||
.profile-stat__icon {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.profile-stat__icon--blue {
|
||||
background: var(--color-primary-soft);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.profile-stat__icon--green {
|
||||
background: var(--color-success-soft);
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
.profile-stat__icon--amber {
|
||||
background: #fff6e5;
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.profile-stat__value {
|
||||
font-size: 1.375rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-text);
|
||||
margin-bottom: 4px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.profile-stat__value--sm {
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.profile-stat__label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-text-muted);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.profile-stat__link {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: var(--color-primary);
|
||||
font-family: inherit;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.profile-stat__link:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
.desktop-profile__grid {
|
||||
direction: ltr;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.profile-panel {
|
||||
direction: rtl;
|
||||
padding: 18px 18px 16px;
|
||||
background: var(--color-surface);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
|
||||
.profile-panel__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 0 0 14px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.profile-panel__title svg {
|
||||
color: var(--color-primary);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.profile-info-list {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.profile-info-list__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 11px 0;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.profile-info-list__row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.profile-info-list__row dt {
|
||||
color: var(--color-text-muted);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.profile-info-list__row dd {
|
||||
margin: 0;
|
||||
color: var(--color-text);
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.profile-badge {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
border-radius: 999px;
|
||||
background: var(--color-success-soft);
|
||||
color: var(--color-success);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.profile-quick-grid {
|
||||
direction: ltr;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.profile-quick-grid__item {
|
||||
direction: rtl;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
min-height: 108px;
|
||||
padding: 16px 10px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
background: #f8fafc;
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
transition: border-color 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.profile-quick-grid__item:hover {
|
||||
border-color: #c7d7ff;
|
||||
background: var(--color-primary-soft);
|
||||
}
|
||||
|
||||
.profile-quick-grid__icon {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
color: var(--color-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.profile-quick-grid__label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
text-align: center;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.profile-settings {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.profile-settings__item {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
padding: 14px 4px;
|
||||
border: none;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
text-align: right;
|
||||
font-family: inherit;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.profile-settings__item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.profile-settings__item:hover {
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.profile-settings__icon {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 10px;
|
||||
background: var(--color-primary-soft);
|
||||
color: var(--color-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.profile-settings__body {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.profile-settings__label {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.profile-settings__desc {
|
||||
font-size: 0.6875rem;
|
||||
color: var(--color-text-muted);
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.profile-settings__chevron {
|
||||
color: #cbd5e1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.profile-overview {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.profile-overview__identity {
|
||||
border-inline-end: none;
|
||||
padding-inline-end: 0;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.profile-overview__stats {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.profile-stat:first-child {
|
||||
border-inline-start: none;
|
||||
}
|
||||
|
||||
.desktop-profile__grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,224 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useOrderStore } from '@/stores/order'
|
||||
import IconChevronLeft from '@/components/icons/IconChevronLeft.vue'
|
||||
import IconProfile from '@/components/icons/IconProfile.vue'
|
||||
import IconCheckBook from '@/components/icons/IconCheckBook.vue'
|
||||
import IconOrders from '@/components/icons/IconOrders.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
const orderStore = useOrderStore()
|
||||
|
||||
const initials = computed(() => {
|
||||
const first = auth.user?.firstName?.[0] ?? ''
|
||||
const last = auth.user?.lastName?.[0] ?? ''
|
||||
return `${first}${last}` || '؟'
|
||||
})
|
||||
|
||||
const draftCheckCount = computed(() => orderStore.draftChecks.length)
|
||||
|
||||
const menuItems = computed(() => [
|
||||
{
|
||||
id: 'edit',
|
||||
label: 'اطلاعات کاربری',
|
||||
description: 'نام، نام خانوادگی و کد ملی',
|
||||
path: '/profile/edit',
|
||||
icon: IconProfile,
|
||||
},
|
||||
{
|
||||
id: 'checks',
|
||||
label: 'لیست چکها',
|
||||
description: 'چکهای ثبتشده و رأس چک',
|
||||
path: '/profile/checks',
|
||||
icon: IconCheckBook,
|
||||
badge: draftCheckCount.value,
|
||||
},
|
||||
{
|
||||
id: 'orders',
|
||||
label: 'سفارشهای من',
|
||||
description: 'پیگیری وضعیت سفارشها',
|
||||
path: '/orders',
|
||||
icon: IconOrders,
|
||||
},
|
||||
])
|
||||
|
||||
const emit = defineEmits<{ logout: [] }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page profile-page">
|
||||
<div class="profile-hero">
|
||||
<div class="profile-hero__avatar">{{ initials }}</div>
|
||||
<h1 class="profile-hero__name">{{ auth.fullName }}</h1>
|
||||
<p class="profile-hero__phone" dir="ltr">{{ auth.user?.phone }}</p>
|
||||
</div>
|
||||
|
||||
<div class="page__scroll page__scroll--with-nav profile-page__scroll">
|
||||
<nav class="profile-menu" aria-label="منوی پروفایل">
|
||||
<button
|
||||
v-for="item in menuItems"
|
||||
:key="item.id"
|
||||
type="button"
|
||||
class="profile-menu__item"
|
||||
@click="router.push(item.path)"
|
||||
>
|
||||
<span class="profile-menu__icon">
|
||||
<component :is="item.icon" :size="22" />
|
||||
</span>
|
||||
<span class="profile-menu__content">
|
||||
<strong class="profile-menu__label">{{ item.label }}</strong>
|
||||
<span class="profile-menu__desc">{{ item.description }}</span>
|
||||
</span>
|
||||
<span v-if="item.badge" class="profile-menu__badge">{{ item.badge }}</span>
|
||||
<IconChevronLeft :size="18" class="profile-menu__chevron" />
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<button type="button" class="profile-logout" @click="emit('logout')">خروج از حساب</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.profile-page {
|
||||
overflow: hidden;
|
||||
padding-top: 0;
|
||||
background: var(--color-bg);
|
||||
}
|
||||
|
||||
.profile-hero {
|
||||
flex-shrink: 0;
|
||||
margin: 0 calc(-1 * var(--page-padding));
|
||||
padding: calc(28px + env(safe-area-inset-top, 0)) var(--page-padding) 32px;
|
||||
background: linear-gradient(160deg, var(--color-primary) 0%, #2a4a73 100%);
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.profile-hero__avatar {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
margin: 0 auto 12px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
border: 2px solid rgba(255, 255, 255, 0.35);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.profile-hero__name {
|
||||
margin: 0 0 6px;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.profile-hero__phone {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.profile-page__scroll {
|
||||
margin-top: -18px;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.profile-menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.profile-menu__item {
|
||||
display: grid;
|
||||
grid-template-columns: 44px 1fr auto auto;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
padding: 14px 16px;
|
||||
border: none;
|
||||
border-radius: 16px;
|
||||
background: var(--color-surface);
|
||||
box-shadow: var(--shadow-sm);
|
||||
cursor: pointer;
|
||||
text-align: right;
|
||||
transition: box-shadow 0.15s;
|
||||
}
|
||||
|
||||
.profile-menu__item:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.profile-menu__icon {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 12px;
|
||||
background: var(--color-primary-light);
|
||||
color: var(--color-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.profile-menu__content {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.profile-menu__label {
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.profile-menu__desc {
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.profile-menu__badge {
|
||||
min-width: 22px;
|
||||
height: 22px;
|
||||
padding: 0 6px;
|
||||
border-radius: 999px;
|
||||
background: var(--color-danger);
|
||||
color: #fff;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.profile-menu__chevron {
|
||||
color: var(--color-text-muted);
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.profile-logout {
|
||||
width: 100%;
|
||||
height: var(--btn-height);
|
||||
border: 1px solid #fecaca;
|
||||
border-radius: 14px;
|
||||
background: #fff;
|
||||
color: var(--color-danger);
|
||||
font-family: inherit;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.profile-logout:hover {
|
||||
background: var(--color-danger-light);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user