209 lines
5.3 KiB
Vue
209 lines
5.3 KiB
Vue
<template>
|
|
<v-navigation-drawer
|
|
:model-value="drawer"
|
|
:rail="rail"
|
|
permanent
|
|
app
|
|
class="bg-white"
|
|
@update:model-value="emit('update:drawer', $event)"
|
|
|
|
@mouseenter="handleMouseEnter"
|
|
@mouseleave="handleMouseLeave"
|
|
>
|
|
<v-list-item
|
|
class="py-4 px-2"
|
|
:class="{'text-center': rail}"
|
|
>
|
|
<div class="d-flex align-center" :class="{'justify-center': rail}">
|
|
<v-icon color="orange-darken-2" size="36">mdi-hospital-box-outline</v-icon>
|
|
<v-list-item-title v-if="!rail" class="ml-2 text-h6 font-weight-bold text-blue-grey-darken-4">
|
|
<span class="text-orange-darken-2">Antrian</span> RSSA
|
|
</v-list-item-title>
|
|
</div>
|
|
</v-list-item>
|
|
<v-divider></v-divider>
|
|
|
|
<v-list density="compact" nav class="mt-2" v-model:opened="openedGroups">
|
|
<template v-for="item in items" :key="item.name">
|
|
|
|
<v-list-group
|
|
v-if="item.children"
|
|
:value="item.name"
|
|
:disabled="rail"
|
|
>
|
|
<template v-slot:activator="{ props: groupProps }">
|
|
<v-list-item
|
|
v-bind="groupProps"
|
|
:prepend-icon="item.icon"
|
|
:title="item.name"
|
|
color="orange-darken-2"
|
|
active-class="bg-orange-lighten-5 text-orange-darken-2 font-weight-bold"
|
|
></v-list-item>
|
|
</template>
|
|
|
|
<v-list-item
|
|
v-for="child in item.children"
|
|
:key="child.name"
|
|
:to="child.path"
|
|
:title="child.name"
|
|
:prepend-icon="child.icon"
|
|
link
|
|
class="pl-8"
|
|
color="orange-darken-2"
|
|
active-class="bg-orange-lighten-5 text-orange-darken-2 font-weight-bold"
|
|
></v-list-item>
|
|
</v-list-group>
|
|
|
|
<v-tooltip
|
|
v-else
|
|
:disabled="!rail"
|
|
open-on-hover
|
|
location="end"
|
|
:text="item.name"
|
|
>
|
|
<template #activator="{ props: tooltipProps }">
|
|
<v-list-item
|
|
v-bind="tooltipProps"
|
|
:prepend-icon="item.icon"
|
|
:title="item.name"
|
|
:to="item.path"
|
|
link
|
|
color="orange-darken-2"
|
|
active-class="bg-orange-lighten-5 text-orange-darken-2 font-weight-bold"
|
|
></v-list-item>
|
|
</template>
|
|
</v-tooltip>
|
|
</template>
|
|
</v-list>
|
|
|
|
<v-spacer></v-spacer>
|
|
<v-divider></v-divider>
|
|
|
|
<v-list v-if="user" nav density="compact" class="pa-2">
|
|
<ProfilePopup
|
|
:user="user"
|
|
@logout="handleLogout"
|
|
:rail="rail"
|
|
/>
|
|
</v-list>
|
|
<v-list v-else nav density="compact">
|
|
<v-list-item
|
|
@click="redirectToLogin"
|
|
prepend-icon="mdi-login"
|
|
title="Login"
|
|
link
|
|
color="blue-grey-darken-4"
|
|
></v-list-item>
|
|
</v-list>
|
|
|
|
</v-navigation-drawer>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { defineProps, defineEmits, onMounted, ref, watch } from 'vue';
|
|
import { navigateTo, useRoute } from '#app';
|
|
import ProfilePopup from './ProfilePopup.vue';
|
|
import { useAuth } from '~/composables/useAuth'; // Ensure this path is correct
|
|
|
|
const { user, logout, checkAuth } = useAuth();
|
|
const route = useRoute(); // Access the current route
|
|
|
|
interface NavItem {
|
|
id: number;
|
|
name: string;
|
|
path: string;
|
|
icon: string;
|
|
children?: NavItem[];
|
|
}
|
|
|
|
const props = defineProps({
|
|
items: {
|
|
type: Array as () => NavItem[],
|
|
required: true,
|
|
},
|
|
rail: {
|
|
type: Boolean,
|
|
required: true,
|
|
},
|
|
drawer: {
|
|
type: Boolean,
|
|
required: true,
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits(['update:drawer', 'toggle-rail']);
|
|
|
|
const openedGroups = ref<string[]>([]);
|
|
const isHovering = ref(false);
|
|
|
|
// --- WATCHERS FOR DROPDOWN AND HIGHLIGHTING ---
|
|
|
|
// 1. WATCHER: Close dropdowns when sidebar collapses (rail = true)
|
|
watch(() => props.rail, (newRailState) => {
|
|
if (newRailState === true) {
|
|
openedGroups.value = [];
|
|
}
|
|
});
|
|
|
|
// 2. WATCHER: Open the parent group when a child route is active
|
|
watch(() => route.path, (newPath) => {
|
|
// Find the parent item whose children contain the current path
|
|
const activeParent = props.items.find(item =>
|
|
item.children && item.children.some(child =>
|
|
child.path === newPath
|
|
)
|
|
);
|
|
|
|
if (activeParent) {
|
|
// If a parent is active, ensure it's in the openedGroups array
|
|
if (!openedGroups.value.includes(activeParent.name)) {
|
|
openedGroups.value.push(activeParent.name);
|
|
}
|
|
|
|
// Optional: Keep only the active parent open, closing others
|
|
// openedGroups.value = [activeParent.name];
|
|
}
|
|
}, { immediate: true });
|
|
|
|
|
|
// --- HOVER LOGIC for Rail Expansion ---
|
|
|
|
const handleMouseEnter = () => {
|
|
if (props.rail) {
|
|
isHovering.value = true;
|
|
emit('toggle-rail');
|
|
}
|
|
};
|
|
|
|
const handleMouseLeave = () => {
|
|
if (isHovering.value) {
|
|
isHovering.value = false;
|
|
emit('toggle-rail');
|
|
}
|
|
};
|
|
|
|
// --- AUTH LOGIC ---
|
|
|
|
const handleLogout = async () => {
|
|
console.log('🚪 SideBar logout initiated...');
|
|
try {
|
|
// Calls the external logout function (from useAuth.ts)
|
|
await logout();
|
|
} catch (error) {
|
|
console.error('❌ SideBar logout error:', error);
|
|
}
|
|
};
|
|
|
|
const redirectToLogin = () => {
|
|
navigateTo('/LoginPage');
|
|
};
|
|
|
|
onMounted(async () => {
|
|
// Fetches real user data on mount
|
|
await checkAuth();
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* No specific overrides needed */
|
|
</style> |