Initial commit: Admin panel
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="flex min-h-screen bg-slate-50">
|
||||
<!-- Sidebar -->
|
||||
<aside class="fixed inset-y-0 right-0 z-30 flex w-64 flex-col border-l border-slate-200 bg-white shadow-sm">
|
||||
<div class="flex items-center gap-3 border-b border-slate-100 px-5 py-5">
|
||||
<div class="flex h-10 w-10 items-center justify-center rounded-xl bg-brand text-lg font-bold text-white">
|
||||
ن
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-slate-900">استودیو نورو</p>
|
||||
<p class="text-xs text-slate-500">پنل مدیریت CMS</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="flex-1 space-y-1 overflow-y-auto p-3">
|
||||
<RouterLink
|
||||
v-for="item in navItems"
|
||||
:key="item.name"
|
||||
:to="{ name: item.name }"
|
||||
class="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors"
|
||||
:class="isActive(item.name)
|
||||
? 'bg-brand/10 text-brand-700'
|
||||
: 'text-slate-600 hover:bg-slate-50 hover:text-slate-900'"
|
||||
>
|
||||
<span class="text-lg" aria-hidden="true">{{ item.icon }}</span>
|
||||
{{ item.label }}
|
||||
</RouterLink>
|
||||
</nav>
|
||||
|
||||
<div class="border-t border-slate-100 p-4">
|
||||
<p class="truncate text-xs text-slate-500">{{ userEmail }}</p>
|
||||
<button
|
||||
type="button"
|
||||
class="mt-2 w-full rounded-lg px-3 py-2 text-right text-sm text-red-600 transition-colors hover:bg-red-50"
|
||||
@click="logout"
|
||||
>
|
||||
خروج از حساب
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main -->
|
||||
<div class="mr-64 flex min-h-screen flex-1 flex-col">
|
||||
<header class="sticky top-0 z-20 border-b border-slate-200 bg-white/90 px-8 py-4 backdrop-blur">
|
||||
<h1 class="text-lg font-semibold text-slate-800">{{ pageTitle }}</h1>
|
||||
</header>
|
||||
<main class="flex-1 p-8">
|
||||
<RouterView />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { api, clearToken } from '@/api/client'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const userEmail = ref('')
|
||||
|
||||
const navItems = [
|
||||
{ name: 'dashboard', label: 'داشبورد', icon: '📊' },
|
||||
{ name: 'site', label: 'تنظیمات سایت', icon: '⚙️' },
|
||||
{ name: 'portfolio', label: 'نمونهکارها', icon: '🎨' },
|
||||
{ name: 'services', label: 'خدمات', icon: '💼' },
|
||||
{ name: 'clients', label: 'مشتریان', icon: '🤝' },
|
||||
{ name: 'blog', label: 'وبلاگ', icon: '📝' },
|
||||
{ name: 'media', label: 'رسانه', icon: '🖼️' },
|
||||
]
|
||||
|
||||
const titles: Record<string, string> = {
|
||||
dashboard: 'داشبورد',
|
||||
site: 'تنظیمات سایت',
|
||||
portfolio: 'نمونهکارها',
|
||||
'portfolio-edit': 'ویرایش نمونهکار',
|
||||
services: 'خدمات',
|
||||
'service-edit': 'ویرایش خدمت',
|
||||
clients: 'مشتریان',
|
||||
blog: 'وبلاگ',
|
||||
'blog-edit': 'ویرایش مطلب',
|
||||
media: 'کتابخانه رسانه',
|
||||
}
|
||||
|
||||
const pageTitle = computed(() => titles[String(route.name)] || 'پنل مدیریت')
|
||||
|
||||
function isActive(name: string) {
|
||||
if (route.name === name) return true
|
||||
if (name === 'portfolio' && route.name === 'portfolio-edit') return true
|
||||
if (name === 'services' && route.name === 'service-edit') return true
|
||||
if (name === 'blog' && route.name === 'blog-edit') return true
|
||||
return false
|
||||
}
|
||||
|
||||
function logout() {
|
||||
clearToken()
|
||||
router.push({ name: 'login' })
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const { user } = await api.me()
|
||||
userEmail.value = user.email
|
||||
} catch {
|
||||
clearToken()
|
||||
router.push({ name: 'login' })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user