2026-09-09 17:08:45 +03:30
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { computed, onMounted, ref } from 'vue'
|
|
|
|
|
|
import { useRouter } from 'vue-router'
|
|
|
|
|
|
import { useAuthStore } from '@/stores/auth'
|
|
|
|
|
|
import { useOrderStore } from '@/stores/order'
|
|
|
|
|
|
import { useCrmStore } from '@/stores/crm'
|
|
|
|
|
|
import { useNotificationStore } from '@/stores/notification'
|
|
|
|
|
|
import { fetchAgentProfile, type AgentProfileDto } from '@/services/agentApi'
|
|
|
|
|
|
import type { CatalogProductCard } from '@/types/crm'
|
|
|
|
|
|
import { formatDate, formatNumber } from '@/utils/format'
|
|
|
|
|
|
import { ORDER_STATUS_LABELS, STATUS_BADGE_VARIANT } from '@/utils/constants'
|
|
|
|
|
|
import IconBell from '@/components/icons/IconBell.vue'
|
|
|
|
|
|
import IconSettings from '@/components/icons/IconSettings.vue'
|
|
|
|
|
|
import IconChevronLeft from '@/components/icons/IconChevronLeft.vue'
|
|
|
|
|
|
import IconCart from '@/components/icons/IconCart.vue'
|
|
|
|
|
|
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
|
|
|
|
|
import PageSkeleton from '@/components/ui/PageSkeleton.vue'
|
|
|
|
|
|
import UserAvatar from '@/components/ui/UserAvatar.vue'
|
|
|
|
|
|
|
|
|
|
|
|
const SAMPLE_PRODUCT: CatalogProductCard = {
|
|
|
|
|
|
id: 'sample-base-price',
|
|
|
|
|
|
name: 'پارچه فاستونی ایتالیایی',
|
|
|
|
|
|
code: 'DF-1204',
|
|
|
|
|
|
categoryId: '',
|
|
|
|
|
|
season: 'all_season',
|
|
|
|
|
|
widthCm: 160,
|
|
|
|
|
|
composition: '',
|
|
|
|
|
|
colors: ['1', '2', '3', '4', '5', '6'],
|
|
|
|
|
|
tags: [],
|
|
|
|
|
|
isNew: false,
|
|
|
|
|
|
rollMeters: 0,
|
|
|
|
|
|
basePrice: 380_000,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const router = useRouter()
|
|
|
|
|
|
const auth = useAuthStore()
|
|
|
|
|
|
const orderStore = useOrderStore()
|
|
|
|
|
|
const crm = useCrmStore()
|
|
|
|
|
|
const notificationStore = useNotificationStore()
|
|
|
|
|
|
const profile = ref<AgentProfileDto | null>(null)
|
|
|
|
|
|
const sampleProduct = ref<CatalogProductCard>(SAMPLE_PRODUCT)
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
|
|
|
|
|
|
/** مطابق موکاپ ۱۱ — بدون gate نقش */
|
|
|
|
|
|
const quickActions = [
|
|
|
|
|
|
{ label: 'ثبت مشتری جدید', to: '/customers', icon: 'vuesax-linear:add' },
|
|
|
|
|
|
{ label: 'سفارشها', to: '/orders', icon: 'vuesax-linear:document-text' },
|
|
|
|
|
|
{ label: 'مشتریان من', to: '/customers', icon: 'vuesax-linear:people' },
|
|
|
|
|
|
{ label: 'گزارشها', to: '/reminders', icon: 'vuesax-linear:chart-2' },
|
|
|
|
|
|
{ label: 'درخواست کالیته', to: '/catalog-request', icon: 'vuesax-linear:category' },
|
|
|
|
|
|
{ label: 'قیمتگذاری', to: '/categories', icon: 'vuesax-linear:money-4' },
|
|
|
|
|
|
] as const
|
|
|
|
|
|
|
|
|
|
|
|
const recentOrders = computed(() =>
|
|
|
|
|
|
[...orderStore.orders]
|
|
|
|
|
|
.sort((a, b) => String(b.createdAt).localeCompare(String(a.createdAt)))
|
|
|
|
|
|
.slice(0, 3)
|
|
|
|
|
|
.map((o) => {
|
|
|
|
|
|
const variant = STATUS_BADGE_VARIANT[o.status] ?? 'info'
|
|
|
|
|
|
const tone =
|
|
|
|
|
|
variant === 'danger' || variant === 'neutral'
|
|
|
|
|
|
? 'danger'
|
|
|
|
|
|
: variant === 'success'
|
|
|
|
|
|
? 'success'
|
|
|
|
|
|
: variant === 'warning'
|
|
|
|
|
|
? 'warning'
|
|
|
|
|
|
: 'info'
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: o.id,
|
|
|
|
|
|
title: `سفارش #${o.orderNumber}`,
|
|
|
|
|
|
date: o.createdAt ? formatDate(o.createdAt) : '',
|
|
|
|
|
|
customer: o.companyName || '',
|
|
|
|
|
|
statusLabel: ORDER_STATUS_LABELS[o.status] || o.status,
|
|
|
|
|
|
statusTone: tone as 'warning' | 'success' | 'info' | 'danger',
|
|
|
|
|
|
}
|
|
|
|
|
|
}),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const displayName = computed(() => auth.fullName || 'کاربر')
|
|
|
|
|
|
const code = computed(() => profile.value?.code || auth.user?.agentCode || '—')
|
|
|
|
|
|
const levelLabel = computed(
|
|
|
|
|
|
() => profile.value?.levelLabel || auth.user?.levelLabel || 'برنزی',
|
|
|
|
|
|
)
|
|
|
|
|
|
const nextLevelLabel = computed(
|
|
|
|
|
|
() => profile.value?.nextLevelLabel || auth.user?.nextLevelLabel || 'نقرهای',
|
|
|
|
|
|
)
|
|
|
|
|
|
const pointsToNext = computed(
|
|
|
|
|
|
() => profile.value?.pointsToNext ?? auth.user?.pointsToNext ?? 0,
|
|
|
|
|
|
)
|
|
|
|
|
|
const periodLabel = computed(() => profile.value?.periodLabel || '۳۰ روز اخیر')
|
|
|
|
|
|
const customersCount = computed(() => profile.value?.customersCount ?? 0)
|
|
|
|
|
|
const ordersCount = computed(() => profile.value?.ordersCount ?? orderStore.orders.length)
|
|
|
|
|
|
const salesVolume = computed(() => profile.value?.salesVolumeToman ?? 0)
|
|
|
|
|
|
const profit = computed(() => profile.value?.profitToman ?? 0)
|
|
|
|
|
|
const levelProgress = computed(
|
|
|
|
|
|
() => profile.value?.levelProgress ?? auth.user?.levelProgress ?? 72,
|
|
|
|
|
|
)
|
|
|
|
|
|
const unreadLabel = computed(() =>
|
|
|
|
|
|
notificationStore.unreadCount > 9 ? '9+' : String(notificationStore.unreadCount),
|
|
|
|
|
|
)
|
|
|
|
|
|
const samplePrice = computed(() => sampleProduct.value.basePrice ?? SAMPLE_PRODUCT.basePrice ?? 0)
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
void notificationStore.hydrate()
|
|
|
|
|
|
try {
|
|
|
|
|
|
profile.value = await fetchAgentProfile()
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
profile.value = null
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
const products = await crm.loadBrowseProducts()
|
|
|
|
|
|
const first = products[0]
|
|
|
|
|
|
sampleProduct.value = first
|
|
|
|
|
|
? { ...SAMPLE_PRODUCT, ...first, basePrice: first.basePrice ?? SAMPLE_PRODUCT.basePrice }
|
|
|
|
|
|
: SAMPLE_PRODUCT
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
sampleProduct.value = SAMPLE_PRODUCT
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!orderStore.orders.length) await orderStore.hydrate()
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="page profile-page">
|
2026-09-10 21:46:55 +03:30
|
|
|
|
<div class="page__scroll page__scroll--with-nav profile-scroll">
|
|
|
|
|
|
<header class="hero">
|
|
|
|
|
|
<div class="hero__pin">
|
|
|
|
|
|
<div class="hero__top">
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="icon-btn"
|
|
|
|
|
|
aria-label="تنظیمات"
|
|
|
|
|
|
@click="router.push('/profile/display')"
|
|
|
|
|
|
>
|
|
|
|
|
|
<IconSettings :size="20" color="#ffffff" />
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<h1>پروفایل من</h1>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="icon-btn"
|
|
|
|
|
|
aria-label="اعلانها"
|
|
|
|
|
|
@click="router.push('/profile/notifications')"
|
|
|
|
|
|
>
|
|
|
|
|
|
<IconBell :size="20" color="#ffffff" />
|
|
|
|
|
|
<span
|
|
|
|
|
|
v-if="notificationStore.hasUnread"
|
|
|
|
|
|
class="icon-btn__badge"
|
|
|
|
|
|
:aria-label="`${notificationStore.unreadCount} اعلان خواندهنشده`"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ unreadLabel }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<UserAvatar
|
|
|
|
|
|
class="hero__avatar"
|
|
|
|
|
|
:src="auth.user?.avatarUrl"
|
|
|
|
|
|
:initials="displayName.charAt(0)"
|
|
|
|
|
|
:size="84"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="hero__meta">
|
|
|
|
|
|
<h2>{{ displayName }}</h2>
|
|
|
|
|
|
<p>کد واسطه: {{ code }}</p>
|
|
|
|
|
|
<span class="level-pill">
|
|
|
|
|
|
<svg class="level-pill__crown" viewBox="0 0 24 24" aria-hidden="true">
|
|
|
|
|
|
<path
|
|
|
|
|
|
fill="currentColor"
|
|
|
|
|
|
d="M3.5 17.5h17l-1.2-8.2a1 1 0 0 0-1.5-.7L14 11l-2.5-5.2a1 1 0 0 0-1.8 0L7.2 11 4.2 8.6a1 1 0 0 0-1.5.7L3.5 17.5Zm1.7 1.5h13.6v1.2H5.2V19Z"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
سطح: {{ levelLabel }}
|
2026-09-09 17:08:45 +03:30
|
|
|
|
</span>
|
2026-09-10 21:46:55 +03:30
|
|
|
|
</div>
|
|
|
|
|
|
</header>
|
2026-09-09 17:08:45 +03:30
|
|
|
|
|
2026-09-10 21:46:55 +03:30
|
|
|
|
<div class="profile-sheet">
|
2026-09-09 17:08:45 +03:30
|
|
|
|
<PageSkeleton v-if="loading" variant="profile" />
|
|
|
|
|
|
<template v-else>
|
|
|
|
|
|
<section class="panel">
|
|
|
|
|
|
<div class="panel__head">
|
|
|
|
|
|
<h3>خلاصه عملکرد</h3>
|
|
|
|
|
|
<span class="panel__period">
|
|
|
|
|
|
{{ periodLabel }}
|
|
|
|
|
|
<BasicIcon icon="vuesax-linear:arrow-down" :size="14" :stroke-width="1.5" />
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stats">
|
|
|
|
|
|
<div class="stat-card">
|
|
|
|
|
|
<div class="stat-card__text">
|
|
|
|
|
|
<span class="stat-card__label">سفارشها</span>
|
|
|
|
|
|
<strong class="stat-card__value">{{ formatNumber(ordersCount) }}</strong>
|
|
|
|
|
|
<em class="stat-card__hint">سفارش</em>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="stat-card__icon stat-card__icon--orders">
|
|
|
|
|
|
<IconCart :size="18" />
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-card">
|
|
|
|
|
|
<div class="stat-card__text">
|
|
|
|
|
|
<span class="stat-card__label">مشتریان من</span>
|
|
|
|
|
|
<strong class="stat-card__value">{{ formatNumber(customersCount) }}</strong>
|
|
|
|
|
|
<em class="stat-card__hint">مشتری فعال</em>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="stat-card__icon stat-card__icon--customers">
|
|
|
|
|
|
<BasicIcon icon="vuesax-linear:people" :size="18" :stroke-width="1.5" />
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-card">
|
|
|
|
|
|
<div class="stat-card__text">
|
|
|
|
|
|
<span class="stat-card__label">سود شما (ریال)</span>
|
|
|
|
|
|
<strong class="stat-card__value">{{ formatNumber(profit) }}</strong>
|
|
|
|
|
|
<em class="stat-card__hint">تومان</em>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="stat-card__icon stat-card__icon--profit">
|
|
|
|
|
|
<BasicIcon icon="vuesax-linear:chart-2" :size="18" :stroke-width="1.5" />
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-card">
|
|
|
|
|
|
<div class="stat-card__text">
|
|
|
|
|
|
<span class="stat-card__label">حجم فروش (ریال)</span>
|
|
|
|
|
|
<strong class="stat-card__value">{{ formatNumber(salesVolume) }}</strong>
|
|
|
|
|
|
<em class="stat-card__hint">تومان</em>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="stat-card__icon stat-card__icon--sales">
|
|
|
|
|
|
<BasicIcon icon="vuesax-linear:wallet" :size="18" :stroke-width="1.5" />
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section class="loyalty">
|
|
|
|
|
|
<div class="loyalty__row">
|
|
|
|
|
|
<div class="loyalty__crown" aria-hidden="true">
|
|
|
|
|
|
<svg viewBox="0 0 48 48" width="40" height="40">
|
|
|
|
|
|
<path
|
|
|
|
|
|
fill="#E0C35A"
|
|
|
|
|
|
d="M8 34h32l-2.4-16.2a2 2 0 0 0-3-.1L28 24l-4.8-10a2 2 0 0 0-3.6 0L14.8 24 9.4 17.7a2 2 0 0 0-3.1.1L8 34Z"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<path fill="#C9A227" d="M10 36h28v3H10z" />
|
|
|
|
|
|
<circle cx="24" cy="28" r="3" fill="#F7E7A1" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="loyalty__text">
|
|
|
|
|
|
شما در سطح {{ levelLabel }} قرار دارید. تا سطح
|
|
|
|
|
|
{{ nextLevelLabel }} فقط
|
|
|
|
|
|
{{ formatNumber(pointsToNext) }} تومان فروش دیگر نیاز دارید.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="loyalty__bar">
|
|
|
|
|
|
<span :style="{ width: `${Math.min(100, Math.max(0, levelProgress))}%` }" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section>
|
|
|
|
|
|
<h3 class="section-title">دسترسی سریع</h3>
|
|
|
|
|
|
<div class="quick-grid">
|
|
|
|
|
|
<button
|
|
|
|
|
|
v-for="item in quickActions"
|
|
|
|
|
|
:key="item.label"
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="quick-item"
|
|
|
|
|
|
@click="router.push(item.to)"
|
|
|
|
|
|
>
|
|
|
|
|
|
<span class="quick-item__icon">
|
|
|
|
|
|
<BasicIcon :icon="item.icon" :size="22" :stroke-width="1.5" />
|
|
|
|
|
|
</span>
|
|
|
|
|
|
{{ item.label }}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section class="orders-box">
|
|
|
|
|
|
<div class="section-head">
|
|
|
|
|
|
<h3>آخرین سفارشها</h3>
|
|
|
|
|
|
<button type="button" class="link" @click="router.push('/orders')">مشاهده همه</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<article
|
|
|
|
|
|
v-for="order in recentOrders"
|
|
|
|
|
|
:key="order.id"
|
|
|
|
|
|
class="order-row"
|
|
|
|
|
|
role="button"
|
|
|
|
|
|
tabindex="0"
|
|
|
|
|
|
@click="router.push({ path: '/order', query: { id: order.id } })"
|
|
|
|
|
|
@keydown.enter="router.push({ path: '/order', query: { id: order.id } })"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="order-row__meta">
|
|
|
|
|
|
<span class="order-row__date">{{ order.date }}</span>
|
|
|
|
|
|
<strong class="order-row__title">{{ order.title }}</strong>
|
|
|
|
|
|
<p v-if="order.customer" class="order-row__customer">مشتری: {{ order.customer }}</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="tone" :class="`tone--${order.statusTone}`">{{ order.statusLabel }}</span>
|
|
|
|
|
|
<IconChevronLeft :size="16" class="order-row__chevron" />
|
|
|
|
|
|
</article>
|
|
|
|
|
|
<button type="button" class="orders-all" @click="router.push('/orders')">
|
|
|
|
|
|
مشاهده همه سفارشها
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<section class="price-section">
|
|
|
|
|
|
<h3 class="section-title">قیمتهای پایه (نمونه)</h3>
|
|
|
|
|
|
<div class="price-card">
|
|
|
|
|
|
<div class="price-card__body">
|
|
|
|
|
|
<div class="price-card__info">
|
|
|
|
|
|
<strong>{{ sampleProduct.name }}</strong>
|
|
|
|
|
|
<p>کد: {{ sampleProduct.code }}</p>
|
|
|
|
|
|
<p>عرض: {{ formatNumber(sampleProduct.widthCm) }} سانتیمتر</p>
|
|
|
|
|
|
<p>رنگها: {{ formatNumber(sampleProduct.colors.length) }} رنگ</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="price-card__image"
|
|
|
|
|
|
:style="
|
|
|
|
|
|
sampleProduct.imageUrl
|
|
|
|
|
|
? { backgroundImage: `url(${sampleProduct.imageUrl})` }
|
|
|
|
|
|
: undefined
|
|
|
|
|
|
"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<footer>قیمت پایه به شما: {{ formatNumber(samplePrice) }} تومان / متر</footer>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
</template>
|
2026-09-10 21:46:55 +03:30
|
|
|
|
</div>
|
2026-09-09 17:08:45 +03:30
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.profile-page {
|
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hero {
|
2026-09-10 21:46:55 +03:30
|
|
|
|
color: #fff;
|
|
|
|
|
|
padding: 0 0 28px;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
background: linear-gradient(
|
|
|
|
|
|
145deg,
|
|
|
|
|
|
var(--color-primary) 0%,
|
|
|
|
|
|
color-mix(in srgb, var(--color-primary) 78%, #0a1f18) 100%
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hero__pin {
|
|
|
|
|
|
position: sticky;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
z-index: 3;
|
|
|
|
|
|
padding: 12px var(--page-padding) 10px;
|
2026-09-09 17:08:45 +03:30
|
|
|
|
background: linear-gradient(
|
|
|
|
|
|
145deg,
|
|
|
|
|
|
var(--color-primary) 0%,
|
|
|
|
|
|
color-mix(in srgb, var(--color-primary) 78%, #0a1f18) 100%
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hero__top {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: 40px 1fr 40px;
|
|
|
|
|
|
align-items: center;
|
2026-09-10 21:46:55 +03:30
|
|
|
|
margin-bottom: 12px;
|
2026-09-09 17:08:45 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hero__top h1 {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 1rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.icon-btn {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
place-items: center;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.icon-btn__badge {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 4px;
|
|
|
|
|
|
inset-inline-start: 4px;
|
|
|
|
|
|
min-width: 16px;
|
|
|
|
|
|
height: 16px;
|
|
|
|
|
|
padding: 0 4px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: #f59e0b;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
font-size: 0.625rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
line-height: 16px;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hero__avatar {
|
2026-09-10 21:46:55 +03:30
|
|
|
|
margin: 0 auto;
|
2026-09-09 17:08:45 +03:30
|
|
|
|
border: 3px solid rgba(255, 255, 255, 0.35);
|
|
|
|
|
|
color: #fff;
|
2026-09-10 21:41:07 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hero__meta {
|
2026-09-10 21:46:55 +03:30
|
|
|
|
padding: 10px var(--page-padding) 0;
|
2026-09-09 17:08:45 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hero h2 {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 1.2rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.hero p {
|
|
|
|
|
|
margin: 6px 0 12px;
|
|
|
|
|
|
opacity: 0.88;
|
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.level-pill {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
border: 1px solid var(--color-level-gold);
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
padding: 5px 14px;
|
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
|
background: color-mix(in srgb, var(--color-level-gold) 16%, transparent);
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.level-pill__crown {
|
|
|
|
|
|
width: 14px;
|
|
|
|
|
|
height: 14px;
|
|
|
|
|
|
color: var(--color-level-gold);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.profile-scroll {
|
2026-09-10 21:41:07 +03:30
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-height: 0;
|
2026-09-10 21:46:55 +03:30
|
|
|
|
background: transparent;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.profile-sheet {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
margin-top: -20px;
|
2026-09-09 17:08:45 +03:30
|
|
|
|
padding: 20px var(--page-padding) 24px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 18px;
|
|
|
|
|
|
background: var(--color-bg);
|
|
|
|
|
|
border-radius: 24px 24px 0 0;
|
|
|
|
|
|
box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.06);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-10 21:46:55 +03:30
|
|
|
|
.profile-sheet > * {
|
2026-09-09 17:08:45 +03:30
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.panel__head,
|
|
|
|
|
|
.section-head {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.panel__head h3,
|
|
|
|
|
|
.section-head h3,
|
|
|
|
|
|
.section-title {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 0.95rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: var(--color-text);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.panel__period {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 2px;
|
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.link {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stats {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: 1fr 1fr;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card {
|
|
|
|
|
|
background: var(--color-surface);
|
|
|
|
|
|
border-radius: 14px;
|
|
|
|
|
|
padding: 12px;
|
|
|
|
|
|
box-shadow: var(--shadow-sm);
|
|
|
|
|
|
border: 1px solid var(--color-border);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card__text {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 2px;
|
|
|
|
|
|
text-align: start;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card__icon {
|
|
|
|
|
|
width: 32px;
|
|
|
|
|
|
height: 32px;
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
place-items: center;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card__icon--orders,
|
|
|
|
|
|
.stat-card__icon--customers {
|
|
|
|
|
|
background: color-mix(in srgb, var(--color-success) 14%, transparent);
|
|
|
|
|
|
color: var(--color-success);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card__icon--profit {
|
|
|
|
|
|
background: color-mix(in srgb, var(--color-info) 14%, transparent);
|
|
|
|
|
|
color: var(--color-info);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card__icon--sales {
|
|
|
|
|
|
background: color-mix(in srgb, var(--color-level-gold) 22%, transparent);
|
|
|
|
|
|
color: #b8860b;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card__label {
|
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card__value {
|
|
|
|
|
|
font-size: 1.05rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: var(--color-text);
|
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stat-card__hint {
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
font-style: normal;
|
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loyalty {
|
|
|
|
|
|
background: var(--color-primary-soft);
|
|
|
|
|
|
border-radius: 14px;
|
|
|
|
|
|
padding: 14px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loyalty__row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loyalty__crown {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
place-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loyalty__text {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
|
line-height: 1.75;
|
|
|
|
|
|
color: var(--color-text);
|
|
|
|
|
|
text-align: start;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loyalty__bar {
|
|
|
|
|
|
height: 8px;
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
background: rgba(var(--color-primary-rgb), 0.15);
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loyalty__bar span {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
|
border-radius: inherit;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.quick-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(3, 1fr);
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.quick-item {
|
|
|
|
|
|
border: 1px solid var(--color-border);
|
|
|
|
|
|
background: var(--color-surface);
|
|
|
|
|
|
border-radius: 14px;
|
|
|
|
|
|
padding: 14px 8px 12px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
font-size: 0.72rem;
|
|
|
|
|
|
color: var(--color-text);
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
font-family: inherit;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
|
min-height: 88px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.quick-item__icon {
|
|
|
|
|
|
width: 40px;
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
place-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.orders-box {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
background: var(--color-surface);
|
|
|
|
|
|
border: 1px solid var(--color-border);
|
|
|
|
|
|
border-radius: 14px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
padding: 4px 0 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.orders-box .section-head {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 12px 14px 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.order-row {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: 1fr auto 16px;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-top: 1px solid var(--color-border);
|
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
|
padding: 12px 14px;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.orders-all {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 12px 14px;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-top: 1px solid var(--color-border);
|
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
|
font: inherit;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.order-row__meta {
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
text-align: start;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.order-row__date {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.order-row__title {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.order-row__customer {
|
|
|
|
|
|
margin: 2px 0 0;
|
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.order-row__chevron {
|
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tone {
|
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
|
padding: 4px 10px;
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tone--warning {
|
|
|
|
|
|
background: var(--color-warning-light);
|
|
|
|
|
|
color: #b45309;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tone--success {
|
|
|
|
|
|
background: var(--color-success-light);
|
|
|
|
|
|
color: var(--color-success);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tone--info {
|
|
|
|
|
|
background: var(--color-info-light);
|
|
|
|
|
|
color: var(--color-info);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tone--danger {
|
|
|
|
|
|
background: var(--color-danger-light);
|
|
|
|
|
|
color: var(--color-danger);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.price-section {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.price-section .section-title {
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.price-card {
|
|
|
|
|
|
background: var(--color-surface);
|
|
|
|
|
|
border: 1px solid var(--color-border);
|
|
|
|
|
|
border-radius: 14px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.price-card__body {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: 1fr 88px;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
padding: 12px;
|
|
|
|
|
|
align-items: start;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.price-card__image {
|
|
|
|
|
|
border-radius: 12px;
|
2026-09-13 15:36:48 +03:30
|
|
|
|
background: linear-gradient(145deg, var(--color-primary), var(--color-primary-dark));
|
2026-09-09 17:08:45 +03:30
|
|
|
|
background-size: cover;
|
|
|
|
|
|
background-position: center;
|
|
|
|
|
|
min-height: 88px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.price-card__info {
|
|
|
|
|
|
text-align: start;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.price-card__info strong {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.price-card__info p {
|
|
|
|
|
|
margin: 2px 0 0;
|
|
|
|
|
|
color: var(--color-text-muted);
|
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.price-card footer {
|
|
|
|
|
|
border-top: 1px solid var(--color-border);
|
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
background: var(--color-elevated);
|
|
|
|
|
|
text-align: start;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|