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

This commit is contained in:
bagus-arie05
2025-10-14 09:13:20 +07:00
17 changed files with 3584 additions and 4548 deletions

View File

@@ -1,3 +1,4 @@
<!-- components/AppBar.vue -->
<template>
<v-app-bar app color="#ff9248" dark>
<v-app-bar-nav-icon @click="emit('toggle-rail')"></v-app-bar-nav-icon>

View File

@@ -2,31 +2,62 @@
<v-menu
v-model="menu"
:close-on-content-click="false"
location="bottom right"
origin="top right"
location="top end"
origin="bottom right"
transition="slide-y-transition"
open-on-hover
>
<template v-slot:activator="{ props }">
<v-btn icon v-bind="props">
<template v-slot:activator="{ props: menuProps }">
<v-list-item
v-if="!rail"
v-bind="menuProps"
:title="user?.name || user?.preferred_username || 'User'"
:subtitle="user?.email || 'No email'"
class="pa-2 ma-1 rounded-lg bg-blue-grey-lighten-5 text-blue-grey-darken-4"
color="blue-grey-darken-4"
link
>
<template v-slot:prepend>
<v-avatar size="40">
<v-img
:src="user?.picture || 'https://i.pravatar.cc/300?img=68'"
:alt="`${user?.name || 'User'} Profile`"
></v-img>
</v-avatar>
</template>
<template v-slot:append>
<v-icon size="20">mdi-chevron-up</v-icon>
</template>
</v-list-item>
<v-btn
v-else
icon
v-bind="menuProps"
size="large"
variant="text"
color="blue-grey-darken-4"
:title="user?.name || 'Profile'"
>
<v-avatar size="40">
<v-img
:src="user?.picture || 'https://i.pravatar.cc/300?img=68'"
<v-img
:src="user?.picture || 'https://i.pravatar.cc/300?img=68'"
:alt="`${user?.name || 'User'} Profile`"
></v-img>
</v-avatar>
</v-btn>
</template>
<v-card class="rounded-lg elevation-4 pa-4" width="300">
<v-card class="rounded-lg elevation-4 pa-4" width="300" color="blue-grey-lighten-5">
<div class="d-flex align-center pb-2">
<v-avatar size="48">
<v-img
:src="user?.picture || 'https://i.pravatar.cc/300?img=68'"
<v-img
:src="user?.picture || 'https://i.pravatar.cc/300?img=68'"
:alt="`${user?.name || 'User'} Profile`"
></v-img>
</v-avatar>
<div class="ml-4">
<div class="text-subtitle-1 font-weight-bold">
<div class="text-subtitle-1 font-weight-bold text-blue-grey-darken-4">
{{ user?.name || user?.preferred_username || 'User' }}
</div>
<div class="text-caption text-grey-darken-1">
@@ -40,16 +71,16 @@
<v-divider class="my-2"></v-divider>
<v-list dense>
<v-list-item link class="rounded-lg" @click="handleAction('account')">
<v-list-item link class="rounded-lg text-blue-grey-darken-4" @click="handleAction('account')">
<template v-slot:prepend>
<v-icon>mdi-cog</v-icon>
<v-icon color="blue-grey-darken-4">mdi-cog</v-icon>
</template>
<v-list-item-title>Pengaturan Akun</v-list-item-title>
</v-list-item>
<v-list-item link class="rounded-lg" @click="handleAction('darkMode')">
<v-list-item link class="rounded-lg text-blue-grey-darken-4" @click="handleAction('darkMode')">
<template v-slot:prepend>
<v-icon>mdi-weather-night</v-icon>
<v-icon color="blue-grey-darken-4">mdi-weather-night</v-icon>
</template>
<v-list-item-title>Mode Gelap</v-list-item-title>
<template v-slot:append>
@@ -57,23 +88,23 @@
v-model="darkMode"
hide-details
density="compact"
color="primary"
color="orange-darken-2"
></v-switch>
</template>
</v-list-item>
<v-list-item link class="rounded-lg" @click="handleAction('profile')">
<v-list-item link class="rounded-lg text-blue-grey-darken-4" @click="handleAction('profile')">
<template v-slot:prepend>
<v-icon>mdi-account</v-icon>
<v-icon color="blue-grey-darken-4">mdi-account</v-icon>
</template>
<v-list-item-title>Profil Saya</v-list-item-title>
</v-list-item>
<v-divider class="my-2"></v-divider>
<v-list-item
link
class="rounded-lg text-red"
<v-list-item
link
class="rounded-lg text-red"
@click="signOut"
:disabled="isLoggingOut"
>
@@ -91,12 +122,16 @@
<script setup>
import { ref } from 'vue';
import { navigateTo } from '#app';
// Props
const props = defineProps({
user: {
type: Object,
required: true
},
rail: {
type: Boolean,
required: true
}
})
@@ -105,43 +140,29 @@ const darkMode = ref(false);
const isLoggingOut = ref(false);
const emit = defineEmits(['logout']);
/**
* Handles the logout action - delegates to parent
*/
const signOut = async () => {
if (isLoggingOut.value) return;
isLoggingOut.value = true;
menu.value = false;
try {
console.log('🚪 ProfilePopup signOut called...')
emit('logout');
// 🚨 Emits event up to SideBar to trigger useAuth().logout()
emit('logout');
} finally {
isLoggingOut.value = false;
}
};
const handleAction = (action) => {
console.log('Action triggered:', action);
switch(action) {
case 'account':
// Navigate to account settings
navigateTo('/settings/account')
break;
case 'profile':
// Navigate to profile page
navigateTo('/profile')
break;
case 'darkMode':
// Dark mode toggle is handled by v-model
break;
default:
console.log('Unknown action:', action);
}
// Close menu for navigation actions
if (action !== 'darkMode') {
menu.value = false;
}

View File

@@ -1,3 +1,4 @@
<!-- components/sideBar.vue -->
<template>
<v-navigation-drawer
v-model="drawer"
@@ -112,27 +113,15 @@
<v-divider class="my-4 mx-2"></v-divider>
</v-list>
<v-spacer></v-spacer>
<template v-slot:append>
<div class="pa-3">
<ProfileMenu
:user="user.value" :rail="rail"
@logout="handleLogout"
class="full-width-on-expand"
/>
</div>
</template>
</v-navigation-drawer>
</template>
<script setup>
import { ref, computed } from "vue";
import { useRoute } from "vue-router";
// Import store Pinia
import { useNavItemsStore } from "@/stores/navItems"; // Sesuaikan path ini jika perlu
import ProfileMenu from "./ProfileMenu.vue";
import { useNavItemsStore } from "@/stores/navItems";
import ProfilePopup from "./ProfilePopup.vue";
import { useAuth } from "~/composables/useAuth";
// State
const drawer = ref(true);
@@ -143,7 +132,7 @@ const hoverTimeout = ref(null);
const user = ref({
name: 'Adam Sulfat',
email: 'adam@rssa.com',
picture: 'https://i.pravatar.cc/150?img=33', // Ganti dengan URL gambar dinamis
picture: 'https://i.pravatar.cc/150?img=33',
id: 'a1b2c3d4e5f6g7h8'
});
const navItemsStore = useNavItemsStore();
@@ -151,10 +140,10 @@ const navItemsStore = useNavItemsStore();
const currentRoute = useRoute();
const currentActiveMenu = computed(() => {
// Menggunakan Getter untuk mendapatkan array menu yang pasti valid
const items = navItemsStore.getNavItems;
// Pengecekan keamanan (walaupun Getter seharusnya sudah menjamin array)
if (!Array.isArray(items) || items.length === 0) {
return "";
}