update sidebar profile dan loket

This commit is contained in:
Fanrouver
2026-01-19 10:14:05 +07:00
parent cbd8f44b50
commit f2efd8355e
8 changed files with 184 additions and 89 deletions

No files matched your search

+40
View File
@@ -0,0 +1,40 @@
<template>
<v-avatar :size="size" :class="avatarClass">
<v-img
v-if="src"
:src="src"
:alt="alt"
:lazy-src="defaultAvatar"
></v-img>
<v-img
v-else
:src="defaultAvatar"
:alt="alt"
></v-img>
</v-avatar>
</template>
<script setup>
import { DEFAULT_AVATAR } from '@/constants/avatar'
const props = defineProps({
src: {
type: String,
default: null
},
alt: {
type: String,
default: 'User Avatar'
},
size: {
type: [Number, String],
default: 44
},
avatarClass: {
type: String,
default: ''
}
})
const defaultAvatar = DEFAULT_AVATAR
</script>
+38 -70
View File
@@ -17,10 +17,10 @@
link
>
<template v-slot:prepend>
<div class="avatar-wrapper">
<v-avatar size="44" class="avatar-glow">
<div class="profile-avatar-container">
<v-avatar size="44" class="profile-avatar elevation-8">
<v-img
:src="user?.picture || 'https://i.pravatar.cc/300?img=68'"
:src="getAvatarSrc(user?.picture)"
:alt="`${user?.name || 'User'} Profile`"
></v-img>
</v-avatar>
@@ -41,10 +41,10 @@
class="avatar-btn"
:title="user?.name || 'Profile'"
>
<div class="avatar-wrapper">
<v-avatar size="44" class="avatar-glow">
<div class="profile-avatar-container">
<v-avatar size="44" class="profile-avatar elevation-8">
<v-img
:src="user?.picture || 'https://i.pravatar.cc/300?img=68'"
:src="getAvatarSrc(user?.picture)"
:alt="`${user?.name || 'User'} Profile`"
></v-img>
</v-avatar>
@@ -58,10 +58,10 @@
<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">
<div class="profile-avatar-container mb-4">
<v-avatar size="90" class="profile-avatar elevation-8">
<v-img
:src="user?.picture || 'https://i.pravatar.cc/300?img=68'"
:src="getAvatarSrc(user?.picture)"
:alt="`${user?.name || 'User'} Profile`"
></v-img>
</v-avatar>
@@ -93,34 +93,10 @@
@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>
<v-icon size="24" color="primary-600">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>
@@ -146,6 +122,7 @@
<script setup>
import { ref } from 'vue';
import { navigateTo } from '#app';
import { DEFAULT_AVATAR } from '@/constants/avatar';
const props = defineProps({
user: {
@@ -159,10 +136,13 @@ const props = defineProps({
})
const menu = ref(false);
const darkMode = ref(false);
const isLoggingOut = ref(false);
const emit = defineEmits(['logout']);
const getAvatarSrc = (userPicture) => {
return userPicture || DEFAULT_AVATAR;
};
const signOut = async () => {
if (isLoggingOut.value) return;
isLoggingOut.value = true;
@@ -177,41 +157,38 @@ const signOut = async () => {
const handleAction = (action) => {
switch(action) {
case 'account':
navigateTo('/Profile/Pengaturan')
break;
case 'profile':
navigateTo('/Profile/Profil')
break;
case 'darkMode':
darkMode.value = !darkMode.value;
return;
}
menu.value = false;
};
</script>
<style scoped>
<style scoped lang="scss">
@import '@/assets/scss/_colors.scss';
.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);
background: linear-gradient(135deg, $primary-100 0%, $primary-50 100%);
border: 1px solid $primary-200;
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);
background: linear-gradient(135deg, $primary-200 0%, $primary-100 100%);
border-color: $primary-300;
transform: translateY(-2px);
}
.avatar-wrapper {
.profile-avatar-container {
position: relative;
display: inline-block;
}
.avatar-glow {
.profile-avatar {
border: 3px solid white;
box-shadow: 0 0 20px rgba(25, 118, 210, 0.4);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.status-dot {
@@ -220,7 +197,7 @@ const handleAction = (action) => {
right: 2px;
width: 12px;
height: 12px;
background: linear-gradient(135deg, #4caf50 0%, #8bc34a 100%);
background: linear-gradient(135deg, $success-600 0%, $success-400 100%);
border: 2px solid white;
border-radius: 50%;
animation: pulse 2s infinite;
@@ -251,12 +228,12 @@ const handleAction = (action) => {
.profile-card {
overflow: hidden;
border: 1px solid rgba(25, 118, 210, 0.1);
border: 1px solid $primary-200;
}
.card-header {
position: relative;
background: linear-gradient(135deg, #1976d2 0%, #fb8c00 100%);
background: linear-gradient(135deg, $primary-700 0%, $primary-500 100%);
overflow: hidden;
}
@@ -276,15 +253,6 @@ const handleAction = (action) => {
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;
@@ -292,7 +260,7 @@ const handleAction = (action) => {
right: 0;
width: 28px;
height: 28px;
background: linear-gradient(135deg, #4caf50 0%, #8bc34a 100%);
background: linear-gradient(135deg, $success-600 0%, $success-400 100%);
border: 3px solid white;
border-radius: 50%;
display: flex;
@@ -310,20 +278,20 @@ const handleAction = (action) => {
.menu-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-columns: 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);
background: linear-gradient(135deg, $primary-100 0%, $primary-50 100%);
border: 1px solid $primary-200;
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);
background: linear-gradient(135deg, $primary-200 0%, $primary-100 100%);
border-color: $primary-300;
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
}
@@ -351,7 +319,7 @@ const handleAction = (action) => {
}
.action-item:hover {
background: linear-gradient(135deg, rgba(25, 118, 210, 0.05) 0%, rgba(245, 124, 0, 0.05) 100%);
background: linear-gradient(135deg, $primary-100 0%, $primary-50 100%);
transform: translateX(4px);
}
@@ -359,11 +327,11 @@ const handleAction = (action) => {
text-transform: none;
font-weight: 600;
letter-spacing: 0.5px;
box-shadow: 0 4px 12px rgba(244, 67, 54, 0.3);
box-shadow: 0 4px 12px rgba(222, 71, 59, 0.3);
}
.logout-btn:hover {
box-shadow: 0 6px 20px rgba(244, 67, 54, 0.4);
box-shadow: 0 6px 20px rgba(222, 71, 59, 0.4);
}
.cursor-pointer {
+74 -2
View File
@@ -152,11 +152,77 @@
<v-btn
v-bind="tooltipProps"
:class="['sidebar-lock-btn', { 'sidebar-lock-btn-locked': isLocked }]"
:icon="isLocked ? 'mdi-lock' : 'mdi-lock-open-variant'"
variant="text"
size="small"
@click="handleLockToggle(!isLocked)"
></v-btn>
>
<!-- Icon Collapsed Sidebar (sidebar collapsed/locked) -->
<svg
v-if="isLocked"
class="sidebar-toggle-icon"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<!-- Rounded rectangle container -->
<rect
x="2"
y="2"
width="16"
height="16"
rx="3"
fill="var(--color-primary-100)"
/>
<!-- Sidebar area (kiri, narrow ~30%, biru gelap) -->
<rect
x="2"
y="2"
width="6"
height="16"
rx="3"
fill="var(--color-primary-600)"
/>
</svg>
<!-- Icon Expanded Sidebar (sidebar expanded/unlocked) -->
<svg
v-else
class="sidebar-toggle-icon"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<!-- Rounded rectangle container -->
<rect
x="2"
y="2"
width="16"
height="16"
rx="3"
fill="var(--color-primary-100)"
/>
<!-- Sidebar area (kiri, wider ~55%, biru gelap) -->
<rect
x="2"
y="2"
width="11"
height="16"
rx="3"
fill="var(--color-primary-600)"
/>
<!-- Left-pointing arrow in the white area -->
<path
d="M 15.5 10 L 13.5 8 M 15.5 10 L 13.5 12"
stroke="var(--color-primary-600)"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</v-btn>
</template>
</v-tooltip>
</div>
@@ -413,6 +479,12 @@ onMounted(async () => {
}
}
.sidebar-toggle-icon {
width: 20px;
height: 20px;
display: block;
}
.sidebar-popup-menu {
min-width: 200px;
border-radius: 8px;