Files
Siro/transit_dashboard/src/components/AppShell.vue
T

104 lines
4.0 KiB
Vue

<script setup>
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { useAuthStore } from '../stores/auth'
const auth = useAuthStore()
const router = useRouter()
const navItems = [
{ to: '/dashboard', label: 'اليوم', icon: 'M4 4h7v7H4zM13 4h7v7h-7zM4 13h7v7H4zM13 13h7v7h-7z' },
{ to: '/fleet', label: 'الأسطول', icon: 'M3 13l1.5-5A2 2 0 0 1 6.4 6.5h11.2A2 2 0 0 1 19.5 8L21 13v6a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1v-1H6v1a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1z' },
{ to: '/routes', label: 'الخطوط', icon: 'M9 20l-5-2V5l5 2m0 13l6-2m-6 2V7m6 11l5 2V6l-5-2m0 15V5m0 2L9 5' },
{ to: '/students', label: 'الطلاب', icon: 'M12 3l9 4.5-9 4.5-9-4.5L12 3zM3 12l9 4.5 9-4.5M3 16.5l9 4.5 9-4.5' },
{ to: '/broadcasts', label: 'الإعلانات', icon: 'M11 5L6 9H2v6h4l5 4V5zM19 8a5 5 0 0 1 0 8' },
]
const contractLabel = computed(() => ({
trial: 'تجريبي', active: 'نشط', suspended: 'معلَّق', terminated: 'منتهٍ',
}[auth.org?.contract_status] || auth.org?.contract_status))
const contractClass = computed(() => ({
trial: 'pill-info', active: 'pill-good', suspended: 'pill-warn', terminated: 'pill-bad',
}[auth.org?.contract_status] || 'pill-neutral'))
async function doLogout() {
await auth.logout()
router.replace('/login')
}
</script>
<template>
<div class="shell">
<aside class="sidebar">
<div class="brand">
<span class="brand-dot"></span>
<span>مواصلاتي</span>
</div>
<nav class="nav">
<router-link v-for="item in navItems" :key="item.to" :to="item.to" class="nav-item" active-class="active">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<path :d="item.icon" />
</svg>
<span>{{ item.label }}</span>
</router-link>
</nav>
</aside>
<div class="main">
<header class="topbar">
<div class="org-info">
<strong>{{ auth.org?.name_ar }}</strong>
<span class="pill" :class="contractClass">{{ contractLabel }}</span>
</div>
<div class="account">
<span class="admin-name">{{ auth.admin?.name }}</span>
<button class="btn btn-ghost btn-sm" @click="doLogout">خروج</button>
</div>
</header>
<main class="content">
<router-view />
</main>
</div>
</div>
</template>
<style scoped>
.shell { display: flex; min-height: 100vh; }
.sidebar {
width: 232px; flex-shrink: 0; background: var(--surface); border-inline-start: 1px solid var(--line);
display: flex; flex-direction: column; padding: 20px 14px;
}
.brand {
display: flex; align-items: center; gap: 9px; font-weight: 800; font-size: 15.5px;
color: var(--brand-deep); padding: 8px 10px 24px;
}
.brand-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--brand); }
.nav { display: flex; flex-direction: column; gap: 3px; }
.nav-item {
display: flex; align-items: center; gap: 12px; padding: 11px 12px; border-radius: var(--r-sm);
color: var(--ink-soft); text-decoration: none; font-size: 14.5px; font-weight: 600;
transition: background .15s ease, color .15s ease;
}
.nav-item svg { width: 19px; height: 19px; flex-shrink: 0; }
.nav-item:hover { background: var(--surface-2); color: var(--ink); }
.nav-item.active { background: var(--brand-soft); color: var(--brand-deep); }
.main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.topbar {
display: flex; align-items: center; justify-content: space-between;
padding: 16px 28px; border-bottom: 1px solid var(--line); background: var(--surface);
}
.org-info { display: flex; align-items: center; gap: 12px; font-size: 15px; }
.account { display: flex; align-items: center; gap: 14px; }
.admin-name { color: var(--ink-soft); font-size: 13.5px; }
.content { flex: 1; padding: 28px; max-width: 1180px; width: 100%; margin: 0 auto; }
@media (max-width: 860px) {
.sidebar { position: fixed; inset-inline-start: 0; top: 0; bottom: 0; z-index: 20; transform: translateX(100%); }
.content { padding: 18px; }
}
</style>