Merge branch 'Antrean-Code' of https://git.rssa.top/arie.bagus.2905/Web-Antrean into Antrean-Code

This commit is contained in:
Fanrouver
2025-10-28 10:22:29 +07:00
22 changed files with 5896 additions and 3978 deletions

No files matched your search

+158
View File
@@ -0,0 +1,158 @@
<template>
<v-menu
v-model="menu"
:close-on-content-click="false"
location="top end"
offset="8"
origin="bottom right"
transition="slide-y-transition"
>
<template v-slot:activator="{ props: menuProps }">
<div
v-bind="menuProps"
class="d-flex align-center cursor-pointer pa-2 rounded-lg hover-bg"
>
<v-avatar size="40">
<v-img
:src="user.picture"
:alt="`${user.name} Profile`"
></v-img>
<v-badge
dot
color="orange"
location="bottom right"
offset-x="2"
offset-y="2"
></v-badge>
</v-avatar>
<div v-show="!rail" class="ml-3 flex-grow-1">
<div class="text-subtitle-2 font-weight-bold">
{{ user.name }}
</div>
<div class="text-caption text-grey">
{{ user.email }}
</div>
</div>
<v-btn v-show="!rail" icon size="small" variant="text">
<v-icon size="20">mdi-dots-vertical</v-icon>
</v-btn>
</div>
</template>
<v-card width="300" elevation="8" rounded="lg">
<v-card-text class="pa-4">
<div class="d-flex align-center mb-4">
<v-avatar size="48">
<v-img :src="user.picture"></v-img>
</v-avatar>
<div class="ml-3">
<div class="text-subtitle-1 font-weight-bold">
{{ user.name }}
</div>
<div class="text-caption text-grey">
{{ user.email }}
</div>
</div>
</div>
<v-list density="compact" class="pa-0">
<v-list-item
prepend-icon="mdi-cog-outline"
class="px-2 rounded-lg"
link
@click="handleAction('setting')"
>
<v-list-item-title class="text-body-2">Setting</v-list-item-title>
</v-list-item>
<v-list-item
prepend-icon="mdi-help-circle-outline"
class="px-2 rounded-lg"
link
@click="handleAction('help')"
>
<v-list-item-title class="text-body-2">Bantuan</v-list-item-title>
</v-list-item>
<v-divider class="my-2"></v-divider>
<v-list-item
prepend-icon="mdi-logout"
class="px-2 rounded-lg text-red"
link
@click="signOut"
:disabled="isLoggingOut"
>
<v-list-item-title class="text-body-2">
{{ isLoggingOut ? 'Log out...' : 'Log out' }}
</v-list-item-title>
</v-list-item>
</v-list>
<div class="text-caption text-grey mt-3 text-center">
v2.5.18 · Terms & Conditions
</div>
</v-card-text>
</v-card>
</v-menu>
</template>
<script setup>
import { ref } from 'vue';
// --- PROPS & EMITS ---
const props = defineProps({
user: {
type: Object,
// Diubah menjadi required: false dengan default yang aman
required: false,
default: () => ({
name: 'Guest',
email: '[email protected]',
picture: 'https://i.pravatar.cc/150?img=33'
})
},
rail: {
type: Boolean,
default: true
}
});
const emit = defineEmits(['logout']); // Hanya emit logout
// --- STATE ---
const menu = ref(false);
const isLoggingOut = ref(false);
// --- METHODS ---
const signOut = () => {
if (isLoggingOut.value) return;
isLoggingOut.value = true;
menu.value = false;
// Memicu event untuk ditangani oleh komponen induk (SideBar)
emit('logout');
// Biarkan parent yang mengurus redirect/state global
setTimeout(() => isLoggingOut.value = false, 1000);
};
const handleAction = (action) => {
console.log(`[PopupSidebar] Action: ${action} triggered.`);
// TODO: Tambahkan logika navigasi/fungsi di sini, misal: router.push('/settings')
menu.value = false;
};
</script>
<style scoped>
/* Tambahkan kembali style yang relevan untuk aktivator di sini */
.hover-bg:hover {
background-color: #f5f5f5;
}
.cursor-pointer {
cursor: pointer;
}
.text-red {
color: rgb(244, 67, 54) !important;
}
</style>
+80 -40
View File
@@ -4,8 +4,7 @@
:rail="rail"
permanent
app
class="bg-white"
@update:model-value="emit('update:drawer', $event)"
class="bg-white d-flex flex-column" @update:model-value="emit('update:drawer', $event)"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
@@ -23,6 +22,7 @@
</v-list-item>
<v-divider></v-divider>
<<<<<<< HEAD
<v-list density="compact" nav class="mt-2" v-model:opened="openedGroups">
<template v-for="item in items" :key="item.name">
@@ -40,53 +40,74 @@
active-class="bg-orange-lighten-5 text-orange-darken-2 font-weight-bold"
></v-list-item>
</template>
=======
<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>
>>>>>>> d7f3240a8226d36d66c5dc319c7a665539d5d8e3
<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-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-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">
<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">
<v-list v-else nav density="compact" class="flex-shrink-0">
<v-list-item
@click="redirectToLogin"
prepend-icon="mdi-login"
@@ -98,10 +119,14 @@
</v-navigation-drawer>
</template>
<script setup lang="ts">
<<<<<<< HEAD
import { defineProps, defineEmits, onMounted, ref, watch } from 'vue';
import { navigateTo, useRoute } from '#app';
=======
import { defineProps, defineEmits, onMounted, ref, watch } from 'vue';
import { navigateTo } from '#app';
>>>>>>> d7f3240a8226d36d66c5dc319c7a665539d5d8e3
import ProfilePopup from './ProfilePopup.vue';
import { useAuth } from '~/composables/useAuth'; // Ensure this path is correct
@@ -133,14 +158,25 @@ const props = defineProps({
const emit = defineEmits(['update:drawer', 'toggle-rail']);
<<<<<<< HEAD
=======
>>>>>>> d7f3240a8226d36d66c5dc319c7a665539d5d8e3
const openedGroups = ref<string[]>([]);
const isHovering = ref(false);
<<<<<<< HEAD
// --- WATCHERS FOR DROPDOWN AND HIGHLIGHTING ---
// 1. WATCHER: Close dropdowns when sidebar collapses (rail = true)
watch(() => props.rail, (newRailState) => {
if (newRailState === true) {
=======
watch(() => props.rail, (newRailState) => {
if (newRailState === true) {
>>>>>>> d7f3240a8226d36d66c5dc319c7a665539d5d8e3
openedGroups.value = [];
}
});
@@ -165,8 +201,12 @@ watch(() => route.path, (newPath) => {
}
}, { immediate: true });
<<<<<<< HEAD
=======
const isHovering = ref(false);
>>>>>>> d7f3240a8226d36d66c5dc319c7a665539d5d8e3
// --- HOVER LOGIC for Rail Expansion ---
const handleMouseEnter = () => {
if (props.rail) {