Update flow anjungan
This commit is contained in:
@@ -5,7 +5,6 @@
|
||||
location="top end"
|
||||
origin="bottom right"
|
||||
transition="slide-y-transition"
|
||||
open-on-hover
|
||||
>
|
||||
<template v-slot:activator="{ props: menuProps }">
|
||||
<v-list-item
|
||||
|
||||
@@ -1,249 +1,187 @@
|
||||
<!-- components/sideBar.vue -->
|
||||
<template>
|
||||
<v-navigation-drawer
|
||||
v-model="drawer"
|
||||
:model-value="drawer"
|
||||
:rail="rail"
|
||||
permanent
|
||||
app
|
||||
color="white"
|
||||
width="280"
|
||||
rail-width="72"
|
||||
class="bg-white d-flex flex-column" @update:model-value="emit('update:drawer', $event)"
|
||||
|
||||
@mouseenter="handleMouseEnter"
|
||||
@mouseleave="handleMouseLeave"
|
||||
>
|
||||
<div class="pa-4 d-flex align-center" style="height: 64px">
|
||||
<v-icon color="#ff9248" size="32">mdi-hospital-building</v-icon>
|
||||
<v-toolbar-title v-show="!rail" class="ml-3 text-h6">
|
||||
Antrean RSSA
|
||||
</v-toolbar-title>
|
||||
</div>
|
||||
|
||||
<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="default" nav class="py-2">
|
||||
<!-- search bar
|
||||
<v-list-item
|
||||
prepend-icon="mdi-magnify"
|
||||
:title="!rail ? 'Search' : ''"
|
||||
rounded="lg"
|
||||
class="mx-2 my-1"
|
||||
>
|
||||
</v-list-item> -->
|
||||
<v-list
|
||||
density="compact"
|
||||
nav
|
||||
class="mt-2 flex-grow-1" 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>
|
||||
|
||||
<template v-for="item in navItemsStore.getNavItems" :key="item.id">
|
||||
|
||||
<v-menu
|
||||
v-if="item.children && item.children.length > 0"
|
||||
open-on-hover
|
||||
location="end"
|
||||
:disabled="!rail"
|
||||
>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-list-group v-if="!rail" :value="item.name">
|
||||
<template v-slot:activator="{ props: listGroupProps }">
|
||||
<v-list-item
|
||||
v-bind="listGroupProps"
|
||||
:prepend-icon="item.icon"
|
||||
:title="item.name"
|
||||
:active="item.name === currentActiveMenu"
|
||||
rounded="lg"
|
||||
class="mx-2 my-1"
|
||||
>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<v-list-item
|
||||
v-for="child in item.children"
|
||||
:key="child.id"
|
||||
:to="child.path"
|
||||
:title="child.name"
|
||||
:active="child.path === currentRoute.path"
|
||||
rounded="lg"
|
||||
class="mx-2 my-1 pl-12"
|
||||
>
|
||||
</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-list-item
|
||||
v-else
|
||||
v-bind="props"
|
||||
:prepend-icon="item.icon"
|
||||
:active="item.name === currentActiveMenu"
|
||||
rounded="lg"
|
||||
class="mx-2 my-1"
|
||||
<v-tooltip
|
||||
v-else
|
||||
:disabled="!rail"
|
||||
open-on-hover
|
||||
location="end"
|
||||
:text="item.name"
|
||||
>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
<v-list class="py-2" style="min-width: 200px">
|
||||
<v-list-item>
|
||||
<v-list-item-title class="font-weight-bold">
|
||||
{{ item.name }}
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-divider class="my-2"></v-divider>
|
||||
<v-list-item
|
||||
v-for="child in item.children"
|
||||
:key="child.id"
|
||||
:to="child.path"
|
||||
:title="child.name"
|
||||
:active="child.path === currentRoute.path"
|
||||
rounded="lg"
|
||||
class="mx-2"
|
||||
>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
|
||||
<v-list-item
|
||||
v-else
|
||||
:prepend-icon="item.icon"
|
||||
:title="!rail ? item.name : ''"
|
||||
:to="item.path"
|
||||
:active="item.path === currentRoute.path"
|
||||
rounded="lg"
|
||||
class="mx-2 my-1"
|
||||
>
|
||||
<template v-slot:append v-if="item.badge && !rail">
|
||||
<v-chip size="x-small" color="error" variant="flat">{{
|
||||
item.badge
|
||||
}}</v-chip>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
<v-divider class="my-4 mx-2"></v-divider>
|
||||
<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-divider></v-divider>
|
||||
|
||||
<v-list v-if="user" nav density="compact" class="pa-2 flex-shrink-0">
|
||||
<ProfilePopup
|
||||
:user="user"
|
||||
@logout="handleLogout"
|
||||
:rail="rail"
|
||||
/>
|
||||
</v-list>
|
||||
<v-list v-else nav density="compact" class="flex-shrink-0">
|
||||
<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 } from '#app';
|
||||
import ProfilePopup from './ProfilePopup.vue';
|
||||
import { useAuth } from '~/composables/useAuth';
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useNavItemsStore } from "@/stores/navItems";
|
||||
import ProfilePopup from "./ProfilePopup.vue";
|
||||
import { useAuth } from "~/composables/useAuth";
|
||||
const { user, logout, checkAuth } = useAuth();
|
||||
|
||||
// State
|
||||
const drawer = ref(true);
|
||||
const rail = ref(true);
|
||||
const hoverTimeout = ref(null);
|
||||
interface NavItem {
|
||||
id: number;
|
||||
name: string;
|
||||
path: string;
|
||||
icon: string;
|
||||
children?: NavItem[];
|
||||
}
|
||||
|
||||
// Akses Pinia Store
|
||||
const user = ref({
|
||||
name: 'Adam Sulfat',
|
||||
email: 'adam@rssa.com',
|
||||
picture: 'https://i.pravatar.cc/150?img=33',
|
||||
id: 'a1b2c3d4e5f6g7h8'
|
||||
});
|
||||
const navItemsStore = useNavItemsStore();
|
||||
|
||||
const currentRoute = useRoute();
|
||||
|
||||
const currentActiveMenu = computed(() => {
|
||||
|
||||
const items = navItemsStore.getNavItems;
|
||||
|
||||
|
||||
if (!Array.isArray(items) || items.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Cari item utama yang sedang aktif
|
||||
const currentItem = items.find((item) => item.path === currentRoute.path);
|
||||
if (currentItem) {
|
||||
return currentItem.name;
|
||||
}
|
||||
|
||||
// Cari item anak (children) yang sedang aktif
|
||||
for (const item of items) {
|
||||
if (item.children && Array.isArray(item.children)) {
|
||||
const childItem = item.children.find(
|
||||
(child) => child.path === currentRoute.path
|
||||
);
|
||||
if (childItem) {
|
||||
return item.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
const props = defineProps({
|
||||
items: {
|
||||
type: Array as () => NavItem[],
|
||||
required: true,
|
||||
},
|
||||
rail: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
drawer: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Hover handlers
|
||||
const emit = defineEmits(['update:drawer', 'toggle-rail']);
|
||||
|
||||
|
||||
const openedGroups = ref<string[]>([]);
|
||||
|
||||
|
||||
watch(() => props.rail, (newRailState) => {
|
||||
if (newRailState === true) {
|
||||
|
||||
openedGroups.value = [];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const isHovering = ref(false);
|
||||
|
||||
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
if (hoverTimeout.value) {
|
||||
clearTimeout(hoverTimeout.value);
|
||||
if (props.rail) {
|
||||
isHovering.value = true;
|
||||
emit('toggle-rail');
|
||||
}
|
||||
hoverTimeout.value = setTimeout(() => {
|
||||
rail.value = false;
|
||||
}, 100);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
if (hoverTimeout.value) {
|
||||
clearTimeout(hoverTimeout.value);
|
||||
if (isHovering.value) {
|
||||
isHovering.value = false;
|
||||
emit('toggle-rail');
|
||||
}
|
||||
hoverTimeout.value = setTimeout(() => {
|
||||
rail.value = true;
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
console.log("Logging out...");
|
||||
// Tambahkan logika logout di sini
|
||||
// --- AUTH LOGIC (Kept the same) ---
|
||||
|
||||
const handleLogout = async () => {
|
||||
console.log('🚪 SideBar logout initiated...');
|
||||
try {
|
||||
await logout();
|
||||
} catch (error) {
|
||||
console.error('❌ SideBar logout error:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const redirectToLogin = () => {
|
||||
navigateTo('/LoginPage');
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await checkAuth();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.v-list-item--active {
|
||||
background-color: #e3f2fd !important;
|
||||
color: #1976d2 !important;
|
||||
}
|
||||
|
||||
.v-list-item--active :deep(.v-list-item__prepend) {
|
||||
color: #1976d2 !important;
|
||||
}
|
||||
|
||||
.v-list-item {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.v-list-item:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.v-navigation-drawer {
|
||||
border-right: 1px solid #e0e0e0 !important;
|
||||
transition: width 0.2s ease-in-out !important;
|
||||
}
|
||||
|
||||
.hover-bg:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
:deep(.v-navigation-drawer__content) {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
:deep(.v-navigation-drawer__content)::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
:deep(.v-navigation-drawer__content)::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:deep(.v-navigation-drawer__content)::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
:deep(.v-navigation-drawer__content)::-webkit-scrollbar-thumb:hover {
|
||||
background: #999;
|
||||
}
|
||||
/* No specific overrides needed */
|
||||
</style>
|
||||
Reference in New Issue
Block a user