74 lines
3.8 KiB
Vue
74 lines
3.8 KiB
Vue
<script setup lang="ts">
|
|
const links = [
|
|
{ to: '/admin', label: 'Commandes', icon: 'kanban' },
|
|
{ to: '/admin/menu', label: 'Menu', icon: 'list' },
|
|
{ to: '/admin/stats', label: 'Statistiques', icon: 'chart' },
|
|
{ to: '/admin/horaires', label: 'Horaires & réglages', icon: 'clock' }
|
|
]
|
|
useHead({ title: 'PANZAÏ — Espace gérant', meta: [{ name: 'robots', content: 'noindex' }] })
|
|
|
|
const { data: orders, refresh } = useOrders()
|
|
const nNew = computed(() => (orders.value || []).filter(o => o.status === 'new').length)
|
|
|
|
// Rafraîchit les commandes toutes les 2 s et sonne à chaque nouvelle commande
|
|
const sound = useState('adminSound', () => false)
|
|
const seen = new Set<string>()
|
|
let primed = false
|
|
watch(orders, (list) => {
|
|
const fresh = (list || []).filter(o => o.status === 'new' && !seen.has(o.id))
|
|
;(list || []).forEach(o => seen.add(o.id))
|
|
if (primed && fresh.length && sound.value) ding()
|
|
primed = true
|
|
}, { immediate: true })
|
|
onMounted(() => {
|
|
const t = setInterval(() => refresh(), 2000)
|
|
onBeforeUnmount(() => clearInterval(t))
|
|
})
|
|
function enableSound() { sound.value = unlockSound(); if (sound.value) ding() }
|
|
</script>
|
|
|
|
<template>
|
|
<div class="adm">
|
|
<aside class="side">
|
|
<div class="brand"><Logo to="/admin" sub="ESPACE GÉRANT" /></div>
|
|
<nav aria-label="Dashboard" class="nav">
|
|
<NuxtLink v-for="l in links" :key="l.to" :to="l.to" :class="{ on: $route.path === l.to }">
|
|
<Icon :name="l.icon" :size="22" />
|
|
<span>{{ l.label }}</span>
|
|
<span v-if="l.to === '/admin' && nNew" class="cnt">{{ nNew }}</span>
|
|
</NuxtLink>
|
|
</nav>
|
|
<button class="btn soundb" :class="sound ? 'on' : ''" type="button" @click="enableSound">
|
|
<Icon name="sound" :size="18" /> {{ sound ? 'Son activé' : 'Activer le son' }}
|
|
</button>
|
|
<div class="foot">
|
|
<a href="/" target="_blank" rel="noopener" class="small"><Icon name="ext" :size="14" /> Voir le site client</a>
|
|
<p class="small muted">Propulsé par KNRWebsite</p>
|
|
</div>
|
|
</aside>
|
|
<div class="main"><slot /></div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.adm { min-height: 100vh; display: flex; flex-wrap: wrap; }
|
|
.side { flex: 1 1 220px; max-width: 240px; min-width: 200px; padding: 20px 14px; background: #100D0A; border-right: 1px solid rgba(243,227,195,.08); display: flex; flex-direction: column; gap: 22px; }
|
|
.brand { padding: 0 6px; }
|
|
.nav { display: flex; flex-direction: column; gap: 4px; }
|
|
.nav a { display: flex; align-items: center; gap: 12px; min-height: 54px; padding: 0 14px; border-radius: 14px; color: var(--cream); text-decoration: none; font-weight: 700; font-size: 15px; }
|
|
.nav a:hover { background: var(--surface); }
|
|
.nav a.on { background: rgba(255,180,58,.14); color: var(--orange-1); }
|
|
.cnt { margin-left: auto; min-width: 26px; height: 26px; padding: 0 7px; border-radius: 13px; background: var(--orange-2); color: var(--bg); font-size: 13px; font-weight: 800; display: flex; align-items: center; justify-content: center; }
|
|
.soundb { background: var(--surface); color: var(--cream); border: 1px dashed rgba(243,227,195,.3); font-size: 14px; }
|
|
.soundb.on { border: 1px solid rgba(140,198,63,.5); color: var(--leaf); }
|
|
.foot { margin-top: auto; display: flex; flex-direction: column; gap: 6px; padding: 0 8px; }
|
|
.foot a { display: inline-flex; align-items: center; gap: 6px; font-weight: 700; text-decoration: none; }
|
|
.main { flex: 999 1 560px; min-width: 0; padding: 22px clamp(14px, 2vw, 28px) 32px; }
|
|
@media (max-width: 860px) {
|
|
.side { max-width: none; flex-basis: 100%; flex-direction: row; align-items: center; flex-wrap: wrap; gap: 10px; border-right: 0; border-bottom: 1px solid rgba(243,227,195,.08); padding: 12px; }
|
|
.nav { flex-direction: row; overflow-x: auto; flex: 1 1 100%; order: 3; }
|
|
.nav a { flex: none; min-height: 46px; }
|
|
.soundb { margin-left: auto; }
|
|
.foot { display: none; }
|
|
}
|
|
</style>
|