416 lines
10 KiB
Vue
416 lines
10 KiB
Vue
<template>
|
|
<v-menu
|
|
v-model="menu"
|
|
:close-on-content-click="false"
|
|
location="top end"
|
|
origin="bottom right"
|
|
transition="scale-transition"
|
|
open-on-hover
|
|
>
|
|
<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-3 ma-1 rounded-xl profile-activator"
|
|
link
|
|
>
|
|
<template v-slot:prepend>
|
|
<div class="avatar-wrapper">
|
|
<v-avatar size="44" class="avatar-glow">
|
|
<v-img
|
|
:src="user?.picture || 'https://i.pravatar.cc/300?img=68'"
|
|
:alt="`${user?.name || 'User'} Profile`"
|
|
></v-img>
|
|
</v-avatar>
|
|
<div class="status-dot"></div>
|
|
</div>
|
|
</template>
|
|
<template v-slot:append>
|
|
<v-icon size="20" class="chevron-icon">mdi-menu-down</v-icon>
|
|
</template>
|
|
</v-list-item>
|
|
|
|
<v-btn
|
|
v-else
|
|
icon
|
|
v-bind="menuProps"
|
|
size="large"
|
|
variant="flat"
|
|
class="avatar-btn"
|
|
:title="user?.name || 'Profile'"
|
|
>
|
|
<div class="avatar-wrapper">
|
|
<v-avatar size="44" class="avatar-glow">
|
|
<v-img
|
|
:src="user?.picture || 'https://i.pravatar.cc/300?img=68'"
|
|
:alt="`${user?.name || 'User'} Profile`"
|
|
></v-img>
|
|
</v-avatar>
|
|
<div class="status-dot"></div>
|
|
</div>
|
|
</v-btn>
|
|
</template>
|
|
|
|
<v-card class="profile-card rounded-xl elevation-12" width="340">
|
|
<!-- Decorative Header -->
|
|
<div class="card-header">
|
|
<div class="header-pattern"></div>
|
|
<div class="header-content pa-6 text-center">
|
|
<div class="avatar-container mb-4">
|
|
<v-avatar size="90" class="profile-avatar">
|
|
<v-img
|
|
:src="user?.picture || 'https://i.pravatar.cc/300?img=68'"
|
|
:alt="`${user?.name || 'User'} Profile`"
|
|
></v-img>
|
|
</v-avatar>
|
|
<div class="status-badge">
|
|
<v-icon size="12" color="white">mdi-check</v-icon>
|
|
</div>
|
|
</div>
|
|
<h3 class="text-h6 font-weight-bold text-white mb-1">
|
|
{{ user?.name || user?.preferred_username || 'User' }}
|
|
</h3>
|
|
<p class="text-body-2 text-white opacity-90 mb-2">
|
|
{{ user?.email || 'No email' }}
|
|
</p>
|
|
<v-chip
|
|
size="small"
|
|
class="glass-chip"
|
|
prepend-icon="mdi-identifier"
|
|
>
|
|
{{ user?.id?.substring(0, 10) }}...
|
|
</v-chip>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Menu Items -->
|
|
<v-card-text class="pa-4">
|
|
<div class="menu-grid">
|
|
<div
|
|
class="menu-tile rounded-lg pa-4 text-center cursor-pointer"
|
|
@click="handleAction('profile')"
|
|
>
|
|
<div class="tile-icon-wrapper mb-2 mx-auto">
|
|
<v-icon size="24" color="blue-darken-2">mdi-account-circle</v-icon>
|
|
</div>
|
|
<div class="text-caption font-weight-medium">Profil</div>
|
|
</div>
|
|
|
|
<div
|
|
class="menu-tile rounded-lg pa-4 text-center cursor-pointer"
|
|
@click="handleAction('account')"
|
|
>
|
|
<div class="tile-icon-wrapper mb-2 mx-auto">
|
|
<v-icon size="24" color="orange-darken-2">mdi-cog-outline</v-icon>
|
|
</div>
|
|
<div class="text-caption font-weight-medium">Pengaturan</div>
|
|
</div>
|
|
|
|
<div
|
|
class="menu-tile rounded-lg pa-4 text-center cursor-pointer"
|
|
@click="handleAction('darkMode')"
|
|
>
|
|
<div class="tile-icon-wrapper mb-2 mx-auto">
|
|
<v-icon size="24" :color="darkMode ? 'deep-purple-darken-2' : 'amber-darken-2'">
|
|
{{ darkMode ? 'mdi-moon-waning-crescent' : 'mdi-white-balance-sunny' }}
|
|
</v-icon>
|
|
</div>
|
|
<div class="text-caption font-weight-medium">
|
|
{{ darkMode ? 'Gelap' : 'Terang' }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<v-divider class="my-4"></v-divider>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="quick-actions">
|
|
<v-list-item
|
|
class="rounded-lg px-3 py-2 action-item"
|
|
link
|
|
@click="handleAction('notifications')"
|
|
>
|
|
<template v-slot:prepend>
|
|
<v-icon size="20" color="blue-darken-1">mdi-bell-outline</v-icon>
|
|
</template>
|
|
<v-list-item-title class="text-body-2 font-weight-medium">
|
|
Notifikasi
|
|
</v-list-item-title>
|
|
<template v-slot:append>
|
|
<v-badge color="orange-darken-2" content="3" inline></v-badge>
|
|
</template>
|
|
</v-list-item>
|
|
|
|
<v-list-item
|
|
class="rounded-lg px-3 py-2 action-item"
|
|
link
|
|
@click="handleAction('help')"
|
|
>
|
|
<template v-slot:prepend>
|
|
<v-icon size="20" color="blue-darken-1">mdi-help-circle-outline</v-icon>
|
|
</template>
|
|
<v-list-item-title class="text-body-2 font-weight-medium">
|
|
Bantuan & Dukungan
|
|
</v-list-item-title>
|
|
</v-list-item>
|
|
</div>
|
|
|
|
<v-divider class="my-4"></v-divider>
|
|
|
|
<!-- Logout Button -->
|
|
<v-btn
|
|
block
|
|
class="rounded-lg logout-btn"
|
|
variant="flat"
|
|
color="error"
|
|
prepend-icon="mdi-logout"
|
|
@click="signOut"
|
|
:disabled="isLoggingOut"
|
|
:loading="isLoggingOut"
|
|
>
|
|
{{ isLoggingOut ? 'Keluar...' : 'Keluar' }}
|
|
</v-btn>
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-menu>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref } from 'vue';
|
|
import { navigateTo } from '#app';
|
|
|
|
const props = defineProps({
|
|
user: {
|
|
type: Object,
|
|
required: true
|
|
},
|
|
rail: {
|
|
type: Boolean,
|
|
required: true
|
|
}
|
|
})
|
|
|
|
const menu = ref(false);
|
|
const darkMode = ref(false);
|
|
const isLoggingOut = ref(false);
|
|
const emit = defineEmits(['logout']);
|
|
|
|
const signOut = async () => {
|
|
if (isLoggingOut.value) return;
|
|
isLoggingOut.value = true;
|
|
menu.value = false;
|
|
|
|
try {
|
|
emit('logout');
|
|
} finally {
|
|
isLoggingOut.value = false;
|
|
}
|
|
};
|
|
|
|
const handleAction = (action) => {
|
|
switch(action) {
|
|
case 'account':
|
|
navigateTo('/Profile/Pengaturan')
|
|
break;
|
|
case 'profile':
|
|
navigateTo('/Profile/Profil')
|
|
break;
|
|
case 'notifications':
|
|
navigateTo('/notifications')
|
|
break;
|
|
case 'help':
|
|
navigateTo('/help')
|
|
break;
|
|
case 'darkMode':
|
|
darkMode.value = !darkMode.value;
|
|
return;
|
|
}
|
|
|
|
menu.value = false;
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.profile-activator {
|
|
background: linear-gradient(135deg, rgba(25, 118, 210, 0.1) 0%, rgba(245, 124, 0, 0.1) 100%);
|
|
border: 1px solid rgba(25, 118, 210, 0.2);
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
|
|
.profile-activator:hover {
|
|
background: linear-gradient(135deg, rgba(25, 118, 210, 0.15) 0%, rgba(245, 124, 0, 0.15) 100%);
|
|
border-color: rgba(25, 118, 210, 0.3);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.avatar-wrapper {
|
|
position: relative;
|
|
}
|
|
|
|
.avatar-glow {
|
|
border: 3px solid white;
|
|
box-shadow: 0 0 20px rgba(25, 118, 210, 0.4);
|
|
}
|
|
|
|
.status-dot {
|
|
position: absolute;
|
|
bottom: 2px;
|
|
right: 2px;
|
|
width: 12px;
|
|
height: 12px;
|
|
background: linear-gradient(135deg, #4caf50 0%, #8bc34a 100%);
|
|
border: 2px solid white;
|
|
border-radius: 50%;
|
|
animation: pulse 2s infinite;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0%, 100% {
|
|
opacity: 1;
|
|
transform: scale(1);
|
|
}
|
|
50% {
|
|
opacity: 0.8;
|
|
transform: scale(1.1);
|
|
}
|
|
}
|
|
|
|
.chevron-icon {
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.profile-activator:hover .chevron-icon {
|
|
transform: translateY(2px);
|
|
}
|
|
|
|
.avatar-btn {
|
|
background: transparent !important;
|
|
}
|
|
|
|
.profile-card {
|
|
overflow: hidden;
|
|
border: 1px solid rgba(25, 118, 210, 0.1);
|
|
}
|
|
|
|
.card-header {
|
|
position: relative;
|
|
background: linear-gradient(135deg, #1976d2 0%, #fb8c00 100%);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.header-pattern {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-image:
|
|
radial-gradient(circle at 20% 50%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
|
|
radial-gradient(circle at 80% 80%, rgba(255, 255, 255, 0.1) 0%, transparent 50%);
|
|
}
|
|
|
|
.header-content {
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.avatar-container {
|
|
position: relative;
|
|
display: inline-block;
|
|
}
|
|
|
|
.profile-avatar {
|
|
border: 4px solid rgba(255, 255, 255, 0.3);
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.status-badge {
|
|
position: absolute;
|
|
bottom: 0;
|
|
right: 0;
|
|
width: 28px;
|
|
height: 28px;
|
|
background: linear-gradient(135deg, #4caf50 0%, #8bc34a 100%);
|
|
border: 3px solid white;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.glass-chip {
|
|
background: rgba(255, 255, 255, 0.2) !important;
|
|
backdrop-filter: blur(10px);
|
|
color: white !important;
|
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
}
|
|
|
|
.menu-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 12px;
|
|
}
|
|
|
|
.menu-tile {
|
|
background: linear-gradient(135deg, rgba(25, 118, 210, 0.05) 0%, rgba(245, 124, 0, 0.05) 100%);
|
|
border: 1px solid rgba(25, 118, 210, 0.1);
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.menu-tile:hover {
|
|
background: linear-gradient(135deg, rgba(25, 118, 210, 0.1) 0%, rgba(245, 124, 0, 0.1) 100%);
|
|
border-color: rgba(25, 118, 210, 0.3);
|
|
transform: translateY(-4px);
|
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.tile-icon-wrapper {
|
|
width: 48px;
|
|
height: 48px;
|
|
background: white;
|
|
border-radius: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
.quick-actions {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
|
|
.action-item {
|
|
transition: all 0.2s ease;
|
|
background: transparent;
|
|
}
|
|
|
|
.action-item:hover {
|
|
background: linear-gradient(135deg, rgba(25, 118, 210, 0.05) 0%, rgba(245, 124, 0, 0.05) 100%);
|
|
transform: translateX(4px);
|
|
}
|
|
|
|
.logout-btn {
|
|
text-transform: none;
|
|
font-weight: 600;
|
|
letter-spacing: 0.5px;
|
|
box-shadow: 0 4px 12px rgba(244, 67, 54, 0.3);
|
|
}
|
|
|
|
.logout-btn:hover {
|
|
box-shadow: 0 6px 20px rgba(244, 67, 54, 0.4);
|
|
}
|
|
|
|
.cursor-pointer {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.opacity-90 {
|
|
opacity: 0.9;
|
|
}
|
|
</style> |