2026-10-21 20:31:05 +03:30
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { ROLE_LABELS } from "@imen/shared";
|
2026-10-23 21:06:15 +03:30
|
|
|
|
import { computed, h, onMounted, ref, watch } from "vue";
|
2026-10-21 20:31:05 +03:30
|
|
|
|
import { useRoute, useRouter } from "vue-router";
|
2026-10-23 21:06:15 +03:30
|
|
|
|
import { api } from "@/api/http";
|
2026-10-22 17:18:17 +03:30
|
|
|
|
import VuesaxIcon from "@/components/VuesaxIcon.vue";
|
2026-10-21 20:31:05 +03:30
|
|
|
|
import { useAuthStore } from "@/stores/auth";
|
|
|
|
|
|
import { SITE_URL } from "@/utils/format";
|
|
|
|
|
|
|
|
|
|
|
|
const auth = useAuthStore();
|
|
|
|
|
|
const route = useRoute();
|
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
const drawer = ref(false);
|
2026-10-23 21:06:15 +03:30
|
|
|
|
const unreadCount = ref(0);
|
|
|
|
|
|
|
|
|
|
|
|
async function fetchUnread() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await api<{ total: number }>("/admin/messages", {
|
|
|
|
|
|
query: { status: "NEW", pageSize: 1 },
|
|
|
|
|
|
});
|
|
|
|
|
|
unreadCount.value = res.total;
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
// ignore
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(fetchUnread);
|
|
|
|
|
|
watch(() => route.fullPath, fetchUnread);
|
2026-10-21 20:31:05 +03:30
|
|
|
|
|
2026-10-22 17:18:17 +03:30
|
|
|
|
/** Icon badge box + Vuesax glyph (keeps pre-swap menu structure) */
|
|
|
|
|
|
const menuIcon = (name: string) => () =>
|
2026-10-23 21:06:15 +03:30
|
|
|
|
h("span", { class: "admin-menu__icon" }, [h(VuesaxIcon, { name, size: 20 })]);
|
2026-10-22 17:18:17 +03:30
|
|
|
|
|
2026-10-21 20:31:05 +03:30
|
|
|
|
const items = computed(() => [
|
2026-10-23 21:06:15 +03:30
|
|
|
|
{ key: "dashboard", icon: menuIcon("chart-square"), label: "داشبورد" },
|
2026-10-22 17:18:17 +03:30
|
|
|
|
{ key: "products", icon: menuIcon("bag-2"), label: "محصولات" },
|
|
|
|
|
|
{ key: "categories", icon: menuIcon("category"), label: "دستهبندیها" },
|
|
|
|
|
|
{ key: "sections", icon: menuIcon("row-vertical"), label: "بخشهای سایت" },
|
|
|
|
|
|
{ key: "messages", icon: menuIcon("sms"), label: "پیامهای تماس" },
|
|
|
|
|
|
{ key: "media", icon: menuIcon("gallery"), label: "کتابخانه رسانه" },
|
|
|
|
|
|
...(auth.isAdmin ? [{ key: "users", icon: menuIcon("profile-2user"), label: "کاربران" }] : []),
|
2026-10-21 20:31:05 +03:30
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
/** menu entry to highlight for nested pages (products/12 → products) */
|
|
|
|
|
|
const MENU_OF: Record<string, string> = {
|
|
|
|
|
|
"product-new": "products",
|
|
|
|
|
|
"product-edit": "products",
|
|
|
|
|
|
"category-new": "categories",
|
|
|
|
|
|
"category-edit": "categories",
|
|
|
|
|
|
"section-edit": "sections",
|
|
|
|
|
|
};
|
2026-10-22 17:18:17 +03:30
|
|
|
|
|
|
|
|
|
|
const PAGE_TITLES: Record<string, string> = {
|
|
|
|
|
|
dashboard: "داشبورد",
|
|
|
|
|
|
products: "محصولات",
|
|
|
|
|
|
"product-new": "محصول جدید",
|
|
|
|
|
|
"product-edit": "ویرایش محصول",
|
|
|
|
|
|
categories: "دستهبندیها",
|
|
|
|
|
|
"category-new": "دستهبندی جدید",
|
|
|
|
|
|
"category-edit": "ویرایش دستهبندی",
|
|
|
|
|
|
sections: "بخشهای سایت",
|
|
|
|
|
|
"section-edit": "ویرایش بخش",
|
|
|
|
|
|
messages: "پیامهای تماس",
|
|
|
|
|
|
media: "کتابخانه رسانه",
|
|
|
|
|
|
users: "کاربران",
|
|
|
|
|
|
profile: "حساب کاربری",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-10-21 20:31:05 +03:30
|
|
|
|
const selected = computed(() => {
|
|
|
|
|
|
const name = String(route.name ?? "");
|
|
|
|
|
|
return [MENU_OF[name] ?? name];
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-10-22 17:18:17 +03:30
|
|
|
|
const pageTitle = computed(() => PAGE_TITLES[String(route.name ?? "")] ?? "پنل مدیریت");
|
|
|
|
|
|
const userInitial = computed(() => (auth.user?.name?.trim().charAt(0) || "ک").slice(0, 1));
|
|
|
|
|
|
|
2026-10-23 21:06:15 +03:30
|
|
|
|
const isChildRoute = computed(() => {
|
|
|
|
|
|
const name = String(route.name ?? "");
|
|
|
|
|
|
return ["product-new", "product-edit", "category-new", "category-edit", "section-edit"].includes(name);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const parentBack = computed(() => {
|
|
|
|
|
|
const name = String(route.name ?? "");
|
|
|
|
|
|
if (name.startsWith("product")) return { name: "products", label: "محصولات" };
|
|
|
|
|
|
if (name.startsWith("category")) return { name: "categories", label: "دستهبندیها" };
|
|
|
|
|
|
if (name.startsWith("section")) return { name: "sections", label: "بخشهای سایت" };
|
|
|
|
|
|
return { name: "dashboard", label: "داشبورد" };
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-10-21 20:31:05 +03:30
|
|
|
|
const go = ({ key }: { key: string | number }) => {
|
|
|
|
|
|
drawer.value = false;
|
|
|
|
|
|
void router.push({ name: String(key) });
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-10-23 21:06:15 +03:30
|
|
|
|
const navigateTo = (name: string) => {
|
|
|
|
|
|
drawer.value = false;
|
|
|
|
|
|
void router.push({ name });
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-10-21 20:31:05 +03:30
|
|
|
|
watch(() => route.fullPath, () => (drawer.value = false));
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
2026-10-22 17:18:17 +03:30
|
|
|
|
<a-layout class="admin-shell">
|
2026-10-23 21:06:15 +03:30
|
|
|
|
<!-- Desktop Sider (lg and up) -->
|
2026-10-21 20:31:05 +03:30
|
|
|
|
<a-layout-sider
|
2026-10-22 17:18:17 +03:30
|
|
|
|
:width="248"
|
2026-10-21 20:31:05 +03:30
|
|
|
|
breakpoint="lg"
|
|
|
|
|
|
collapsed-width="0"
|
|
|
|
|
|
:trigger="null"
|
2026-10-22 17:18:17 +03:30
|
|
|
|
class="admin-sider !hidden lg:!block"
|
2026-10-21 20:31:05 +03:30
|
|
|
|
>
|
2026-10-22 17:18:17 +03:30
|
|
|
|
<div class="admin-sider__brand">
|
|
|
|
|
|
<span class="admin-sider__logo">
|
|
|
|
|
|
<img src="/logo.png" alt="" class="size-7 object-contain" />
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<div class="leading-tight">
|
|
|
|
|
|
<div class="text-[14px] font-bold text-[#0f172a]">ایمن پیشگامان</div>
|
|
|
|
|
|
<div class="text-[10px] font-medium tracking-wide text-[#94a3b8]">پنل مدیریت</div>
|
2026-10-21 20:31:05 +03:30
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-10-22 17:18:17 +03:30
|
|
|
|
|
|
|
|
|
|
<div class="admin-sider__nav">
|
|
|
|
|
|
<div class="admin-sider__label">منوی اصلی</div>
|
|
|
|
|
|
<a-menu
|
|
|
|
|
|
class="admin-menu"
|
|
|
|
|
|
mode="inline"
|
|
|
|
|
|
:selected-keys="selected"
|
|
|
|
|
|
:items="items"
|
|
|
|
|
|
@click="go"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2026-10-21 20:31:05 +03:30
|
|
|
|
</a-layout-sider>
|
|
|
|
|
|
|
2026-10-23 21:06:15 +03:30
|
|
|
|
<!-- Mobile App Hub Drawer (Rich App Grid) -->
|
2026-10-22 17:18:17 +03:30
|
|
|
|
<a-drawer
|
|
|
|
|
|
v-model:open="drawer"
|
|
|
|
|
|
placement="right"
|
2026-10-23 21:06:15 +03:30
|
|
|
|
:width="320"
|
2026-10-22 17:18:17 +03:30
|
|
|
|
:closable="false"
|
2026-10-23 21:06:15 +03:30
|
|
|
|
class="mobile-hub-drawer"
|
|
|
|
|
|
:body-style="{ padding: 0, background: '#f8fafc' }"
|
2026-10-22 17:18:17 +03:30
|
|
|
|
>
|
2026-10-23 21:06:15 +03:30
|
|
|
|
<div class="flex flex-col h-full">
|
|
|
|
|
|
<!-- Hub User Profile Header -->
|
|
|
|
|
|
<div class="p-4 bg-white border-b border-slate-200/80">
|
|
|
|
|
|
<div class="flex items-center justify-between mb-3">
|
|
|
|
|
|
<div class="flex items-center gap-2.5">
|
|
|
|
|
|
<span class="size-8 rounded-lg bg-orange-50 border border-orange-100 flex items-center justify-center">
|
|
|
|
|
|
<img src="/logo.png" alt="" class="size-5 object-contain" />
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div class="text-sm font-bold text-slate-800">ایمن پیشگامان</div>
|
|
|
|
|
|
<div class="text-[10px] text-slate-400">اپلیکیشن مدیریت</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="size-8 rounded-full flex items-center justify-center text-slate-400 hover:text-slate-600 hover:bg-slate-100 transition"
|
|
|
|
|
|
@click="drawer = false"
|
|
|
|
|
|
>
|
|
|
|
|
|
<VuesaxIcon name="close-circle" :size="20" />
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="flex items-center gap-3 p-2.5 rounded-xl bg-slate-50 border border-slate-100 cursor-pointer active:scale-[0.99] transition"
|
|
|
|
|
|
@click="navigateTo('profile')"
|
|
|
|
|
|
>
|
|
|
|
|
|
<span class="size-10 rounded-full bg-linear-to-br from-[#ff5a00] to-[#ff8a3d] text-white flex items-center justify-center font-bold text-sm shadow-xs">
|
|
|
|
|
|
{{ userInitial }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<div class="min-w-0 flex-1">
|
|
|
|
|
|
<div class="text-xs font-bold text-slate-800 truncate">{{ auth.user?.name }}</div>
|
|
|
|
|
|
<div class="flex items-center gap-2 mt-0.5">
|
|
|
|
|
|
<span class="text-[10px] text-orange-600 font-medium">
|
|
|
|
|
|
{{ auth.user && ROLE_LABELS[auth.user.role] }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span class="text-[10px] text-slate-400 font-mono" dir="ltr">{{ auth.user?.phone }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<VuesaxIcon name="arrow-left" :size="14" class="text-slate-400" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Hub Apps Grid Content -->
|
|
|
|
|
|
<div class="flex-1 overflow-y-auto p-4 space-y-4">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div class="text-[11px] font-bold text-slate-400 mb-2.5 px-1">بخشها و کاتالوگ</div>
|
|
|
|
|
|
<div class="grid grid-cols-2 gap-2">
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="flex flex-col items-center justify-center p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-orange-200 active:bg-orange-50/40 transition"
|
|
|
|
|
|
:class="{ '!border-orange-500 !bg-orange-50/30 font-bold text-orange-600': route.name === 'dashboard' }"
|
|
|
|
|
|
@click="navigateTo('dashboard')"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="size-9 rounded-lg bg-orange-50 text-orange-600 flex items-center justify-center mb-1.5">
|
|
|
|
|
|
<VuesaxIcon name="chart-square" :size="18" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="text-xs text-slate-700">داشبورد</span>
|
|
|
|
|
|
<span class="text-[9px] text-slate-400 mt-0.5">نمای کلی آمار</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="flex flex-col items-center justify-center p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-orange-200 active:bg-orange-50/40 transition"
|
|
|
|
|
|
:class="{ '!border-orange-500 !bg-orange-50/30 font-bold text-orange-600': selected.includes('products') }"
|
|
|
|
|
|
@click="navigateTo('products')"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="size-9 rounded-lg bg-orange-50 text-orange-600 flex items-center justify-center mb-1.5">
|
|
|
|
|
|
<VuesaxIcon name="bag-2" :size="18" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="text-xs text-slate-700">محصولات</span>
|
|
|
|
|
|
<span class="text-[9px] text-slate-400 mt-0.5">کاتالوگ و موجودی</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="flex flex-col items-center justify-center p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-blue-200 active:bg-blue-50/40 transition"
|
|
|
|
|
|
:class="{ '!border-blue-500 !bg-blue-50/30 font-bold text-blue-600': selected.includes('categories') }"
|
|
|
|
|
|
@click="navigateTo('categories')"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="size-9 rounded-lg bg-blue-50 text-blue-600 flex items-center justify-center mb-1.5">
|
|
|
|
|
|
<VuesaxIcon name="category" :size="18" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="text-xs text-slate-700">دستهبندیها</span>
|
|
|
|
|
|
<span class="text-[9px] text-slate-400 mt-0.5">گروههای کالا</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="flex flex-col items-center justify-center p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-amber-200 active:bg-amber-50/40 transition relative"
|
|
|
|
|
|
:class="{ '!border-amber-500 !bg-amber-50/30 font-bold text-amber-600': route.name === 'messages' }"
|
|
|
|
|
|
@click="navigateTo('messages')"
|
|
|
|
|
|
>
|
|
|
|
|
|
<span v-if="unreadCount > 0" class="absolute top-2 end-2 size-2.5 rounded-full bg-orange-600 ring-2 ring-white animate-pulse" />
|
|
|
|
|
|
<div class="size-9 rounded-lg bg-amber-50 text-amber-600 flex items-center justify-center mb-1.5">
|
|
|
|
|
|
<VuesaxIcon name="sms" :size="18" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="text-xs text-slate-700">پیامهای تماس</span>
|
|
|
|
|
|
<span class="text-[9px] text-slate-400 mt-0.5">درخواستهای مشاوره</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div class="text-[11px] font-bold text-slate-400 mb-2.5 px-1">محتوا و وبسایت</div>
|
|
|
|
|
|
<div class="grid grid-cols-2 gap-2">
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="flex flex-col items-center justify-center p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-purple-200 active:bg-purple-50/40 transition"
|
|
|
|
|
|
:class="{ '!border-purple-500 !bg-purple-50/30 font-bold text-purple-600': route.name === 'media' }"
|
|
|
|
|
|
@click="navigateTo('media')"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="size-9 rounded-lg bg-purple-50 text-purple-600 flex items-center justify-center mb-1.5">
|
|
|
|
|
|
<VuesaxIcon name="gallery" :size="18" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="text-xs text-slate-700">کتابخانه رسانه</span>
|
|
|
|
|
|
<span class="text-[9px] text-slate-400 mt-0.5">تصاویر WebP</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="flex flex-col items-center justify-center p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-emerald-200 active:bg-emerald-50/40 transition"
|
|
|
|
|
|
:class="{ '!border-emerald-500 !bg-emerald-50/30 font-bold text-emerald-600': selected.includes('sections') }"
|
|
|
|
|
|
@click="navigateTo('sections')"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="size-9 rounded-lg bg-emerald-50 text-emerald-600 flex items-center justify-center mb-1.5">
|
|
|
|
|
|
<VuesaxIcon name="row-vertical" :size="18" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="text-xs text-slate-700">بخشهای سایت</span>
|
|
|
|
|
|
<span class="text-[9px] text-slate-400 mt-0.5">تنظیمات صفحه اصلی</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="auth.isAdmin">
|
|
|
|
|
|
<div class="text-[11px] font-bold text-slate-400 mb-2.5 px-1">مدیریت سیستم</div>
|
|
|
|
|
|
<div class="grid grid-cols-1 gap-2">
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="flex items-center gap-3 p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-slate-300 active:bg-slate-50 transition text-right"
|
|
|
|
|
|
:class="{ '!border-orange-500 font-bold': route.name === 'users' }"
|
|
|
|
|
|
@click="navigateTo('users')"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="size-9 rounded-lg bg-slate-100 text-slate-600 flex items-center justify-center shrink-0">
|
|
|
|
|
|
<VuesaxIcon name="profile-2user" :size="18" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="flex-1 min-w-0">
|
|
|
|
|
|
<div class="text-xs font-bold text-slate-800">مدیریت کاربران پنل</div>
|
|
|
|
|
|
<div class="text-[10px] text-slate-400">حسابها و سطح دسترسیها</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<VuesaxIcon name="arrow-left" :size="14" class="text-slate-400" />
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Hub Footer System Actions -->
|
|
|
|
|
|
<div class="p-3 bg-white border-t border-slate-200/80 space-y-2">
|
|
|
|
|
|
<a
|
|
|
|
|
|
:href="SITE_URL"
|
|
|
|
|
|
target="_blank"
|
|
|
|
|
|
rel="noopener"
|
|
|
|
|
|
class="flex items-center justify-between p-2.5 rounded-lg text-slate-600 hover:bg-slate-50 text-xs font-semibold transition"
|
|
|
|
|
|
>
|
|
|
|
|
|
<span class="flex items-center gap-2">
|
|
|
|
|
|
<VuesaxIcon name="export" :size="16" class="text-slate-400" />
|
|
|
|
|
|
مشاهده وبسایت اصلی
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<VuesaxIcon name="arrow-left" :size="12" class="text-slate-300" />
|
|
|
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="w-full flex items-center justify-center gap-2 p-2.5 rounded-lg text-rose-600 hover:bg-rose-50 text-xs font-semibold transition"
|
|
|
|
|
|
@click="auth.logout()"
|
|
|
|
|
|
>
|
|
|
|
|
|
<VuesaxIcon name="logout" :size="16" />
|
|
|
|
|
|
خروج از حساب کاربری
|
|
|
|
|
|
</button>
|
2026-10-22 17:18:17 +03:30
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-10-21 20:31:05 +03:30
|
|
|
|
</a-drawer>
|
|
|
|
|
|
|
|
|
|
|
|
<a-layout>
|
2026-10-23 21:06:15 +03:30
|
|
|
|
<!-- Dedicated Mobile App Bar (Mobile Only) -->
|
|
|
|
|
|
<header class="mobile-app-bar lg:!hidden">
|
|
|
|
|
|
<div v-if="isChildRoute" class="flex items-center justify-between w-full">
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="mobile-app-bar__back-btn"
|
|
|
|
|
|
@click="router.push(parentBack)"
|
|
|
|
|
|
>
|
|
|
|
|
|
<VuesaxIcon name="arrow-right" :size="20" />
|
|
|
|
|
|
<span class="text-xs font-bold text-slate-700">بازگشت به {{ parentBack.label }}</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<div class="text-xs font-bold text-slate-800 truncate max-w-[140px]">
|
|
|
|
|
|
{{ pageTitle }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-10-22 17:18:17 +03:30
|
|
|
|
|
2026-10-23 21:06:15 +03:30
|
|
|
|
<div v-else class="flex items-center justify-between w-full">
|
|
|
|
|
|
<div class="flex items-center gap-2.5">
|
|
|
|
|
|
<span class="size-8 rounded-lg bg-orange-50 border border-orange-100 flex items-center justify-center">
|
|
|
|
|
|
<img src="/logo.png" alt="" class="size-5 object-contain" />
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<div class="leading-none">
|
|
|
|
|
|
<div class="text-xs font-extrabold text-slate-800">ایمن پیشگامان</div>
|
|
|
|
|
|
<div class="text-[10px] font-medium text-orange-600 mt-0.5">{{ pageTitle }}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="flex items-center gap-1.5">
|
|
|
|
|
|
<a
|
|
|
|
|
|
:href="SITE_URL"
|
|
|
|
|
|
target="_blank"
|
|
|
|
|
|
rel="noopener"
|
|
|
|
|
|
class="size-8 rounded-lg flex items-center justify-center text-slate-500 hover:text-orange-600 hover:bg-orange-50 transition"
|
|
|
|
|
|
title="مشاهده سایت"
|
|
|
|
|
|
>
|
|
|
|
|
|
<VuesaxIcon name="export" :size="18" />
|
|
|
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="size-8 rounded-lg bg-linear-to-br from-[#ff5a00] to-[#ff8a3d] text-white flex items-center justify-center font-bold text-xs shadow-xs"
|
|
|
|
|
|
@click="router.push({ name: 'profile' })"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ userInitial }}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Desktop Header (Desktop Only) -->
|
|
|
|
|
|
<a-layout-header class="admin-header !hidden lg:!flex">
|
|
|
|
|
|
<div class="admin-header__start">
|
2026-10-22 17:18:17 +03:30
|
|
|
|
<div class="admin-header__crumb">
|
|
|
|
|
|
<span class="admin-header__crumb-kicker">پنل مدیریت</span>
|
|
|
|
|
|
<span class="admin-header__crumb-sep" aria-hidden="true">/</span>
|
|
|
|
|
|
<span class="admin-header__crumb-page">{{ pageTitle }}</span>
|
|
|
|
|
|
</div>
|
2026-10-21 20:31:05 +03:30
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-10-22 17:18:17 +03:30
|
|
|
|
<div class="admin-header__end">
|
|
|
|
|
|
<a
|
|
|
|
|
|
:href="SITE_URL"
|
|
|
|
|
|
target="_blank"
|
|
|
|
|
|
rel="noopener"
|
|
|
|
|
|
class="admin-header__site"
|
|
|
|
|
|
>
|
|
|
|
|
|
<VuesaxIcon name="export" :size="16" />
|
|
|
|
|
|
<span>مشاهده سایت</span>
|
|
|
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="admin-header__divider" aria-hidden="true" />
|
|
|
|
|
|
|
|
|
|
|
|
<a-dropdown placement="bottomLeft" :trigger="['click']">
|
|
|
|
|
|
<button type="button" class="admin-header__user">
|
|
|
|
|
|
<span class="admin-header__avatar">{{ userInitial }}</span>
|
|
|
|
|
|
<span class="admin-header__user-meta">
|
|
|
|
|
|
<span class="admin-header__user-name">{{ auth.user?.name }}</span>
|
|
|
|
|
|
<span v-if="auth.user" class="admin-header__user-role">
|
|
|
|
|
|
{{ ROLE_LABELS[auth.user.role] }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<VuesaxIcon name="arrow-down" :size="12" class="admin-header__chevron" />
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<template #overlay>
|
|
|
|
|
|
<a-menu class="admin-header__dropdown">
|
|
|
|
|
|
<a-menu-item key="profile" @click="router.push({ name: 'profile' })">
|
|
|
|
|
|
<VuesaxIcon name="user" :size="16" /> حساب کاربری
|
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
|
<a-menu-divider />
|
|
|
|
|
|
<a-menu-item key="logout" danger @click="auth.logout()">
|
|
|
|
|
|
<VuesaxIcon name="logout" :size="16" /> خروج
|
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
|
</a-menu>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
|
</div>
|
2026-10-21 20:31:05 +03:30
|
|
|
|
</a-layout-header>
|
|
|
|
|
|
|
2026-10-23 21:06:15 +03:30
|
|
|
|
<!-- Content Viewport -->
|
|
|
|
|
|
<a-layout-content class="admin-content" :class="{ 'admin-content--has-bottom-nav': !isChildRoute }">
|
2026-10-22 17:18:17 +03:30
|
|
|
|
<div class="admin-page">
|
|
|
|
|
|
<RouterView v-slot="{ Component, route: r }">
|
|
|
|
|
|
<component :is="Component" :key="r.path" />
|
|
|
|
|
|
</RouterView>
|
|
|
|
|
|
</div>
|
2026-10-21 20:31:05 +03:30
|
|
|
|
</a-layout-content>
|
2026-10-23 21:06:15 +03:30
|
|
|
|
|
|
|
|
|
|
<!-- Purpose-Built Mobile Bottom Navigation Bar -->
|
|
|
|
|
|
<nav v-if="!isChildRoute" class="mobile-nav-bar lg:!hidden">
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="mobile-nav-item"
|
|
|
|
|
|
:class="{ 'mobile-nav-item--active': route.name === 'dashboard' }"
|
|
|
|
|
|
@click="navigateTo('dashboard')"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="mobile-nav-item__icon">
|
|
|
|
|
|
<VuesaxIcon name="chart-square" :size="20" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="mobile-nav-item__label">داشبورد</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="mobile-nav-item"
|
|
|
|
|
|
:class="{ 'mobile-nav-item--active': selected.includes('products') }"
|
|
|
|
|
|
@click="navigateTo('products')"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="mobile-nav-item__icon">
|
|
|
|
|
|
<VuesaxIcon name="bag-2" :size="20" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="mobile-nav-item__label">محصولات</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="mobile-nav-item"
|
|
|
|
|
|
:class="{ 'mobile-nav-item--active': route.name === 'messages' }"
|
|
|
|
|
|
@click="navigateTo('messages')"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="mobile-nav-item__icon relative">
|
|
|
|
|
|
<VuesaxIcon name="sms" :size="20" />
|
|
|
|
|
|
<span
|
|
|
|
|
|
v-if="unreadCount > 0"
|
|
|
|
|
|
class="absolute -top-1 -end-1 size-2 rounded-full bg-orange-600 ring-2 ring-white"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="mobile-nav-item__label">پیامها</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="mobile-nav-item"
|
|
|
|
|
|
:class="{ 'mobile-nav-item--active': drawer || ['categories', 'sections', 'media', 'users', 'profile'].includes(String(route.name ?? '')) }"
|
|
|
|
|
|
@click="drawer = true"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="mobile-nav-item__icon">
|
|
|
|
|
|
<VuesaxIcon name="category" :size="20" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="mobile-nav-item__label">ابزارها</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</nav>
|
2026-10-21 20:31:05 +03:30
|
|
|
|
</a-layout>
|
|
|
|
|
|
</a-layout>
|
|
|
|
|
|
</template>
|
2026-10-22 17:18:17 +03:30
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.admin-shell {
|
|
|
|
|
|
height: 100vh;
|
|
|
|
|
|
height: 100dvh;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-shell > :deep(.ant-layout) {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header {
|
|
|
|
|
|
position: sticky;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
z-index: 20;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
height: 64px !important;
|
|
|
|
|
|
line-height: 1.3 !important;
|
|
|
|
|
|
padding-inline: 16px 20px !important;
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.96) !important;
|
|
|
|
|
|
border-bottom: 1px solid #e8ecf2;
|
|
|
|
|
|
backdrop-filter: blur(10px);
|
|
|
|
|
|
box-shadow: 0 1px 0 rgba(15, 23, 42, 0.02);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-content {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
background: #f5f6fa;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* White panel locked to remaining viewport; pages manage their own inner scroll */
|
|
|
|
|
|
.admin-page {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
|
border: 1px solid #e8ecf2;
|
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
|
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.03);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-23 21:06:15 +03:30
|
|
|
|
.admin-page > :deep(.page-header) {
|
|
|
|
|
|
flex: 0 0 auto !important;
|
|
|
|
|
|
overflow: visible !important;
|
|
|
|
|
|
padding: 16px 20px 14px !important;
|
|
|
|
|
|
border-bottom: 1px solid #eef1f6;
|
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-page > :deep(*:not(.page-header)) {
|
2026-10-22 17:18:17 +03:30
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Pages that don't use AdminPage still scroll inside the panel */
|
2026-10-23 21:06:15 +03:30
|
|
|
|
.admin-page > :deep(*:not(.ap):not(.page-header)) {
|
2026-10-22 17:18:17 +03:30
|
|
|
|
overflow: auto;
|
|
|
|
|
|
padding: 16px;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 768px) {
|
|
|
|
|
|
.admin-header {
|
|
|
|
|
|
padding-inline: 24px !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__start,
|
|
|
|
|
|
.admin-header__end {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__menu-btn {
|
|
|
|
|
|
border-radius: 10px !important;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
border: none !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
color: #475569 !important;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
background: transparent !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__crumb {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__crumb-kicker {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #94a3b8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
|
.admin-header__crumb-kicker {
|
|
|
|
|
|
display: inline;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__crumb-sep {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
color: #cbd5e1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
|
.admin-header__crumb-sep {
|
|
|
|
|
|
display: inline;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__crumb-page {
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: #0f172a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__site {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
height: 36px;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
padding-inline: 8px;
|
|
|
|
|
|
border: none !important;
|
|
|
|
|
|
background: transparent !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
color: #475569;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
text-decoration: none;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
transition: color 0.18s ease;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__site:hover {
|
2026-10-23 21:06:15 +03:30
|
|
|
|
color: #ff5a00;
|
|
|
|
|
|
border: none !important;
|
|
|
|
|
|
background: transparent !important;
|
|
|
|
|
|
box-shadow: none !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__divider {
|
|
|
|
|
|
width: 1px;
|
|
|
|
|
|
height: 28px;
|
|
|
|
|
|
background: #e8ecf2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__user {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
max-width: 260px;
|
|
|
|
|
|
height: 44px;
|
|
|
|
|
|
padding-block: 4px;
|
|
|
|
|
|
padding-inline: 4px 10px;
|
|
|
|
|
|
border: 1px solid #e8ecf2;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
border-radius: var(--ant-border-radius, 8px);
|
2026-10-22 17:18:17 +03:30
|
|
|
|
background: #f8fafc;
|
|
|
|
|
|
color: inherit;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition:
|
|
|
|
|
|
border-color 0.18s ease,
|
|
|
|
|
|
background 0.18s ease,
|
|
|
|
|
|
box-shadow 0.18s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__user:hover {
|
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
|
border-color: #ffd0b5;
|
|
|
|
|
|
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.05);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__avatar {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 34px;
|
|
|
|
|
|
height: 34px;
|
|
|
|
|
|
flex-shrink: 0;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
border-radius: calc(var(--ant-border-radius, 8px) - 2px);
|
2026-10-22 17:18:17 +03:30
|
|
|
|
background: linear-gradient(135deg, #ff5a00, #ff8a3d);
|
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
box-shadow: 0 2px 8px rgba(255, 90, 0, 0.28);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__user-meta {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
line-height: 1.25;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 640px) {
|
|
|
|
|
|
.admin-header__user-meta {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__user-name {
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
max-width: 140px;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: #0f172a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__user-role {
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #94a3b8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-header__chevron {
|
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
|
color: #94a3b8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-sider {
|
|
|
|
|
|
height: 100vh !important;
|
|
|
|
|
|
height: 100dvh !important;
|
|
|
|
|
|
max-height: 100vh !important;
|
|
|
|
|
|
max-height: 100dvh !important;
|
|
|
|
|
|
position: sticky !important;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
background: #ffffff !important;
|
|
|
|
|
|
/* content-facing edge (left in RTL) — continuous vertical rail */
|
|
|
|
|
|
border-inline-end: 1px solid #e8ecf2;
|
|
|
|
|
|
box-shadow: -4px 0 24px rgba(15, 23, 42, 0.04);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-sider :deep(.ant-layout-sider-children) {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-sider__brand {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 10px;
|
|
|
|
|
|
height: 64px;
|
|
|
|
|
|
padding: 0 16px;
|
|
|
|
|
|
border-bottom: 1px solid #eef1f6;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-sider__logo {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 36px;
|
|
|
|
|
|
height: 36px;
|
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
|
background: #fff7f2;
|
|
|
|
|
|
border: 1px solid #ffe0cc;
|
|
|
|
|
|
box-shadow: 0 1px 2px rgba(255, 90, 0, 0.06);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-sider__nav {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-height: 0;
|
|
|
|
|
|
height: calc(100vh - 64px);
|
|
|
|
|
|
height: calc(100dvh - 64px);
|
|
|
|
|
|
padding: 16px 12px 20px;
|
|
|
|
|
|
overflow-x: hidden;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-sider__label {
|
|
|
|
|
|
margin: 0 8px 10px;
|
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
|
color: #94a3b8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-menu {
|
|
|
|
|
|
background: transparent !important;
|
|
|
|
|
|
border: 0 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-menu :deep(.ant-menu-item) {
|
2026-10-23 21:17:51 +03:30
|
|
|
|
display: flex !important;
|
|
|
|
|
|
align-items: center !important;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
height: 48px !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
margin: 0 0 6px !important;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
padding-inline: 10px 14px !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
border: 1px solid transparent !important;
|
|
|
|
|
|
border-radius: 12px !important;
|
|
|
|
|
|
color: #475569 !important;
|
|
|
|
|
|
font-weight: 500;
|
2026-10-23 21:17:51 +03:30
|
|
|
|
line-height: 1.5 !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
transition:
|
|
|
|
|
|
background 0.2s ease,
|
|
|
|
|
|
color 0.2s ease,
|
|
|
|
|
|
border-color 0.2s ease,
|
|
|
|
|
|
box-shadow 0.2s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-23 21:17:51 +03:30
|
|
|
|
.admin-menu :deep(.ant-menu-item-icon) {
|
2026-10-22 17:18:17 +03:30
|
|
|
|
display: inline-flex !important;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
align-items: center !important;
|
|
|
|
|
|
justify-content: center !important;
|
|
|
|
|
|
width: 36px !important;
|
|
|
|
|
|
height: 36px !important;
|
|
|
|
|
|
min-width: 36px !important;
|
|
|
|
|
|
min-height: 36px !important;
|
2026-10-23 21:17:51 +03:30
|
|
|
|
margin: 0 !important;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
margin-inline-end: 12px !important;
|
2026-10-23 21:17:51 +03:30
|
|
|
|
padding: 0 !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
font-size: 0 !important;
|
2026-10-23 21:17:51 +03:30
|
|
|
|
line-height: 0 !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
background: transparent !important;
|
|
|
|
|
|
box-shadow: none !important;
|
2026-10-23 21:17:51 +03:30
|
|
|
|
flex-shrink: 0 !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-menu :deep(.admin-menu__icon) {
|
|
|
|
|
|
display: inline-flex !important;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
align-items: center !important;
|
|
|
|
|
|
justify-content: center !important;
|
|
|
|
|
|
width: 36px !important;
|
|
|
|
|
|
height: 36px !important;
|
|
|
|
|
|
min-width: 36px !important;
|
|
|
|
|
|
min-height: 36px !important;
|
2026-10-23 21:17:51 +03:30
|
|
|
|
margin: 0 !important;
|
|
|
|
|
|
padding: 0 !important;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
flex-shrink: 0 !important;
|
|
|
|
|
|
border-radius: 10px !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
background: #f1f5f9;
|
|
|
|
|
|
color: #64748b;
|
2026-10-23 21:17:51 +03:30
|
|
|
|
line-height: 0 !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
transition:
|
|
|
|
|
|
background 0.2s ease,
|
|
|
|
|
|
color 0.2s ease,
|
|
|
|
|
|
box-shadow 0.2s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-23 21:17:51 +03:30
|
|
|
|
.admin-menu :deep(.admin-menu__icon .vx-icon),
|
|
|
|
|
|
.admin-menu :deep(.admin-menu__icon .anticon) {
|
2026-10-23 21:06:15 +03:30
|
|
|
|
display: inline-flex !important;
|
|
|
|
|
|
align-items: center !important;
|
|
|
|
|
|
justify-content: center !important;
|
|
|
|
|
|
width: 20px !important;
|
|
|
|
|
|
height: 20px !important;
|
|
|
|
|
|
min-width: 20px !important;
|
|
|
|
|
|
min-height: 20px !important;
|
2026-10-23 21:17:51 +03:30
|
|
|
|
max-width: 20px !important;
|
|
|
|
|
|
max-height: 20px !important;
|
|
|
|
|
|
margin: 0 !important;
|
|
|
|
|
|
margin-inline-end: 0 !important;
|
|
|
|
|
|
margin-inline-start: 0 !important;
|
|
|
|
|
|
padding: 0 !important;
|
|
|
|
|
|
line-height: 0 !important;
|
|
|
|
|
|
vertical-align: 0 !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
background: transparent !important;
|
|
|
|
|
|
box-shadow: none !important;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
color: inherit !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-menu :deep(.admin-menu__icon svg) {
|
|
|
|
|
|
width: 20px !important;
|
|
|
|
|
|
height: 20px !important;
|
2026-10-23 21:17:51 +03:30
|
|
|
|
max-width: 20px !important;
|
|
|
|
|
|
max-height: 20px !important;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
display: block !important;
|
2026-10-23 21:17:51 +03:30
|
|
|
|
margin: 0 auto !important;
|
|
|
|
|
|
transform: none !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-menu :deep(.ant-menu-title-content) {
|
|
|
|
|
|
display: flex !important;
|
|
|
|
|
|
align-items: center !important;
|
2026-10-23 21:20:32 +03:30
|
|
|
|
padding-inline-start: 8px !important;
|
|
|
|
|
|
padding-right: 8px !important;
|
2026-10-23 21:17:51 +03:30
|
|
|
|
margin-inline-start: 0 !important;
|
|
|
|
|
|
line-height: 1.5 !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-menu :deep(.ant-menu-item:hover) {
|
|
|
|
|
|
background: #f8fafc !important;
|
|
|
|
|
|
color: #0f172a !important;
|
|
|
|
|
|
border-color: #eef1f6 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-23 21:06:15 +03:30
|
|
|
|
.admin-menu :deep(.ant-menu-item:hover:not(.ant-menu-item-selected) .admin-menu__icon) {
|
|
|
|
|
|
background: #e8eef7 !important;
|
|
|
|
|
|
color: #475569 !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-menu :deep(.ant-menu-item-selected) {
|
|
|
|
|
|
background: #fff7f2 !important;
|
|
|
|
|
|
border-color: #ffd7bf !important;
|
|
|
|
|
|
color: #c2410c !important;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
box-shadow: 0 4px 14px rgba(255, 90, 0, 0.1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-menu :deep(.ant-menu-item-selected .admin-menu__icon) {
|
|
|
|
|
|
background: #ff5a00 !important;
|
|
|
|
|
|
color: #ffffff !important;
|
2026-10-23 21:06:15 +03:30
|
|
|
|
box-shadow: 0 4px 12px rgba(255, 90, 0, 0.35) !important;
|
2026-10-22 17:18:17 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-menu :deep(.ant-menu-item-selected::after) {
|
|
|
|
|
|
display: none !important;
|
|
|
|
|
|
}
|
2026-10-23 21:06:15 +03:30
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------
|
|
|
|
|
|
* Mobile App Experience Styles (Viewport < 1024px)
|
|
|
|
|
|
* ------------------------------------------------------------- */
|
|
|
|
|
|
.mobile-app-bar {
|
|
|
|
|
|
position: sticky;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
z-index: 30;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
height: 52px;
|
|
|
|
|
|
padding-inline: 14px;
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.96);
|
|
|
|
|
|
backdrop-filter: blur(12px);
|
|
|
|
|
|
border-bottom: 1px solid #eef1f6;
|
|
|
|
|
|
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.02);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.mobile-app-bar__back-btn {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 6px;
|
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
|
margin-inline-start: -6px;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
color: #334155;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: background 0.15s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.mobile-app-bar__back-btn:active {
|
|
|
|
|
|
background: #f1f5f9;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.mobile-nav-bar {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
z-index: 40;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-around;
|
|
|
|
|
|
height: 60px;
|
|
|
|
|
|
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.96);
|
|
|
|
|
|
backdrop-filter: blur(16px);
|
|
|
|
|
|
border-top: 1px solid #e2e8f0;
|
|
|
|
|
|
box-shadow: 0 -4px 16px rgba(15, 23, 42, 0.04);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.mobile-nav-item {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
padding-block: 4px;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
color: #64748b;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: color 0.15s ease;
|
|
|
|
|
|
user-select: none;
|
|
|
|
|
|
-webkit-tap-highlight-color: transparent;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.mobile-nav-item__icon {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
height: 24px;
|
|
|
|
|
|
transition: transform 0.15s ease;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.mobile-nav-item__label {
|
|
|
|
|
|
margin-top: 2px;
|
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.mobile-nav-item--active {
|
|
|
|
|
|
color: #ff5a00;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.mobile-nav-item--active .mobile-nav-item__icon {
|
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 1023px) {
|
|
|
|
|
|
.admin-content {
|
|
|
|
|
|
padding: 0 !important;
|
|
|
|
|
|
background: #f8fafc !important;
|
|
|
|
|
|
overflow-y: auto !important;
|
|
|
|
|
|
-webkit-overflow-scrolling: touch;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-content--has-bottom-nav {
|
|
|
|
|
|
padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px)) !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-page {
|
|
|
|
|
|
border: none !important;
|
|
|
|
|
|
border-radius: 0 !important;
|
|
|
|
|
|
background: transparent !important;
|
|
|
|
|
|
box-shadow: none !important;
|
|
|
|
|
|
min-height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-page > :deep(.page-header) {
|
|
|
|
|
|
padding: 12px 14px !important;
|
|
|
|
|
|
border-bottom: 1px solid #eef1f6 !important;
|
|
|
|
|
|
background: #ffffff !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.admin-page > :deep(*:not(.ap):not(.page-header)) {
|
|
|
|
|
|
padding: 12px !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-10-22 17:18:17 +03:30
|
|
|
|
</style>
|