update sidebar profile dan loket
This commit is contained in:
No files matched your search
@@ -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>
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user