feat: Add ProfileDialog component for user profile management
- Introduce ProfileDialog.vue to display user information and account settings. - Integrate ProfileDialog into Navbar.vue for easy access to user profile. - Implement logout functionality within the ProfileDialog, enhancing user experience.
This commit is contained in:
parent
82e8c230ab
commit
eb238ca380
2 changed files with 135 additions and 2 deletions
123
src/components/auth/ProfileDialog.vue
Normal file
123
src/components/auth/ProfileDialog.vue
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
import { User, LogOut, Settings, Mail, Key } from 'lucide-vue-next'
|
||||||
|
import { auth } from '@/composables/useAuth'
|
||||||
|
import { toast } from 'vue-sonner'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
isOpen: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'update:isOpen', value: boolean): void
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
|
const userDisplay = computed(() => auth.userDisplay.value)
|
||||||
|
|
||||||
|
function handleLogout() {
|
||||||
|
auth.logout()
|
||||||
|
toast.success('Logged out successfully')
|
||||||
|
handleClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
emit('update:isOpen', false)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dialog :open="isOpen" @update:open="handleClose">
|
||||||
|
<DialogContent class="sm:max-w-[500px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle class="flex items-center gap-2">
|
||||||
|
<User class="w-5 h-5" />
|
||||||
|
User Profile
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Your account information and settings
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div v-if="userDisplay" class="space-y-6">
|
||||||
|
<!-- User Info Card -->
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle class="flex items-center gap-2">
|
||||||
|
<User class="w-5 h-5" />
|
||||||
|
Account Information
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Your profile details
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent class="space-y-4">
|
||||||
|
<div class="grid gap-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-sm font-medium">Name:</span>
|
||||||
|
<span class="text-sm">{{ userDisplay.name }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="userDisplay.username" class="flex items-center justify-between">
|
||||||
|
<span class="text-sm font-medium">Username:</span>
|
||||||
|
<span class="text-sm">{{ userDisplay.username }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="userDisplay.email" class="flex items-center justify-between">
|
||||||
|
<span class="text-sm font-medium">Email:</span>
|
||||||
|
<span class="text-sm">{{ userDisplay.email }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-sm font-medium">User ID:</span>
|
||||||
|
<Badge variant="secondary" class="text-xs">{{ userDisplay.shortId }}</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<!-- Actions Card -->
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle class="flex items-center gap-2">
|
||||||
|
<Settings class="w-5 h-5" />
|
||||||
|
Account Actions
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Manage your account settings
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent class="space-y-4">
|
||||||
|
<div class="grid gap-3">
|
||||||
|
<Button variant="outline" class="w-full justify-start gap-2">
|
||||||
|
<Settings class="w-4 h-4" />
|
||||||
|
Account Settings
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button variant="outline" class="w-full justify-start gap-2">
|
||||||
|
<Key class="w-4 h-4" />
|
||||||
|
Change Password
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button variant="destructive" @click="handleLogout" class="w-full justify-start gap-2">
|
||||||
|
<LogOut class="w-4 h-4" />
|
||||||
|
Logout
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="text-center py-8">
|
||||||
|
<User class="w-12 h-12 mx-auto text-muted-foreground mb-4" />
|
||||||
|
<p class="text-muted-foreground">No user information available</p>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
@ -9,6 +9,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSepara
|
||||||
import { Sun, Moon, Menu, X, User, LogOut } from 'lucide-vue-next'
|
import { Sun, Moon, Menu, X, User, LogOut } from 'lucide-vue-next'
|
||||||
import LanguageSwitcher from '@/components/LanguageSwitcher.vue'
|
import LanguageSwitcher from '@/components/LanguageSwitcher.vue'
|
||||||
import LoginDialog from '@/components/auth/LoginDialog.vue'
|
import LoginDialog from '@/components/auth/LoginDialog.vue'
|
||||||
|
import ProfileDialog from '@/components/auth/ProfileDialog.vue'
|
||||||
import { auth } from '@/composables/useAuth'
|
import { auth } from '@/composables/useAuth'
|
||||||
|
|
||||||
interface NavigationItem {
|
interface NavigationItem {
|
||||||
|
|
@ -21,6 +22,7 @@ const { t } = useI18n()
|
||||||
const { theme, setTheme } = useTheme()
|
const { theme, setTheme } = useTheme()
|
||||||
const isOpen = ref(false)
|
const isOpen = ref(false)
|
||||||
const showLoginDialog = ref(false)
|
const showLoginDialog = ref(false)
|
||||||
|
const showProfileDialog = ref(false)
|
||||||
|
|
||||||
const navigation = computed<NavigationItem[]>(() => [
|
const navigation = computed<NavigationItem[]>(() => [
|
||||||
{ name: t('nav.home'), href: '/' },
|
{ name: t('nav.home'), href: '/' },
|
||||||
|
|
@ -41,6 +43,11 @@ const openLoginDialog = () => {
|
||||||
isOpen.value = false // Close mobile menu
|
isOpen.value = false // Close mobile menu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openProfileDialog = () => {
|
||||||
|
showProfileDialog.value = true
|
||||||
|
isOpen.value = false // Close mobile menu
|
||||||
|
}
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
try {
|
try {
|
||||||
auth.logout()
|
auth.logout()
|
||||||
|
|
@ -87,7 +94,7 @@ const handleLogout = async () => {
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end" class="w-56">
|
<DropdownMenuContent align="end" class="w-56">
|
||||||
<DropdownMenuItem class="gap-2">
|
<DropdownMenuItem @click="openProfileDialog" class="gap-2">
|
||||||
<User class="h-4 w-4" />
|
<User class="h-4 w-4" />
|
||||||
Profile
|
Profile
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|
@ -146,7 +153,7 @@ const handleLogout = async () => {
|
||||||
<Badge variant="secondary" class="text-xs ml-auto">Logged In</Badge>
|
<Badge variant="secondary" class="text-xs ml-auto">Logged In</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<Button variant="ghost" size="sm" class="w-full justify-start gap-2">
|
<Button variant="ghost" size="sm" @click="openProfileDialog" class="w-full justify-start gap-2">
|
||||||
<User class="h-4 w-4" />
|
<User class="h-4 w-4" />
|
||||||
Profile
|
Profile
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -181,5 +188,8 @@ const handleLogout = async () => {
|
||||||
|
|
||||||
<!-- Login Dialog -->
|
<!-- Login Dialog -->
|
||||||
<LoginDialog v-model:is-open="showLoginDialog" />
|
<LoginDialog v-model:is-open="showLoginDialog" />
|
||||||
|
|
||||||
|
<!-- Profile Dialog -->
|
||||||
|
<ProfileDialog v-model:is-open="showProfileDialog" />
|
||||||
</nav>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue