feat: Implement Logout Confirmation Dialog
- Introduce a new LogoutConfirmDialog component to confirm user logout actions. - Update ProfileDialog.vue, UserProfile.vue, and Navbar.vue to utilize the new dialog for logout confirmation, enhancing user experience and preventing accidental logouts. - Ensure consistent styling and functionality across all instances where logout confirmation is required.
This commit is contained in:
parent
3ceb7f219a
commit
442861e5a5
6 changed files with 175 additions and 10 deletions
|
|
@ -4,6 +4,7 @@ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } f
|
|||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { LogoutConfirmDialog } from '@/components/ui/LogoutConfirmDialog'
|
||||
import { User, LogOut, Settings, Key, Wallet, ExternalLink } from 'lucide-vue-next'
|
||||
import { auth } from '@/composables/useAuth'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
|
@ -125,10 +126,10 @@ function handleClose() {
|
|||
Change Password
|
||||
</Button>
|
||||
|
||||
<Button variant="destructive" @click="handleLogout" class="w-full justify-start gap-2">
|
||||
<LogoutConfirmDialog variant="destructive" class="w-full justify-start gap-2" @confirm="handleLogout">
|
||||
<LogOut class="w-4 h-4" />
|
||||
Logout
|
||||
</Button>
|
||||
</LogoutConfirmDialog>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useRouter } from 'vue-router'
|
|||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { LogoutConfirmDialog } from '@/components/ui/LogoutConfirmDialog'
|
||||
import { User, LogOut, Settings } from 'lucide-vue-next'
|
||||
import { auth } from '@/composables/useAuth'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
|
@ -58,10 +59,10 @@ function handleLogout() {
|
|||
<Settings class="w-4 h-4 mr-2" />
|
||||
Settings
|
||||
</Button>
|
||||
<Button variant="destructive" size="sm" @click="handleLogout" class="flex-1">
|
||||
<LogoutConfirmDialog variant="destructive" size="sm" class="flex-1" @confirm="handleLogout">
|
||||
<LogOut class="w-4 h-4 mr-2" />
|
||||
Logout
|
||||
</Button>
|
||||
</LogoutConfirmDialog>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import LanguageSwitcher from '@/components/LanguageSwitcher.vue'
|
|||
import LoginDialog from '@/components/auth/LoginDialog.vue'
|
||||
import ProfileDialog from '@/components/auth/ProfileDialog.vue'
|
||||
import CurrencyDisplay from '@/components/ui/CurrencyDisplay.vue'
|
||||
import { LogoutConfirmDialog } from '@/components/ui/LogoutConfirmDialog'
|
||||
import { auth } from '@/composables/useAuth'
|
||||
import { useMarketPreloader } from '@/composables/useMarketPreloader'
|
||||
import { nostrChat } from '@/composables/useNostrChat'
|
||||
|
|
@ -154,9 +155,11 @@ const handleLogout = async () => {
|
|||
Relay Hub Status
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem @click="handleLogout" class="gap-2 text-destructive">
|
||||
<DropdownMenuItem as-child class="text-destructive">
|
||||
<LogoutConfirmDialog variant="ghost" size="sm" class="w-full justify-start gap-2 h-auto p-2" @confirm="handleLogout">
|
||||
<LogOut class="h-4 w-4" />
|
||||
Logout
|
||||
</LogoutConfirmDialog>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
|
@ -239,11 +242,10 @@ const handleLogout = async () => {
|
|||
<Activity class="h-4 w-4" />
|
||||
Relay Hub Status
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" @click="handleLogout"
|
||||
class="w-full justify-start gap-2 text-destructive">
|
||||
<LogoutConfirmDialog variant="ghost" size="sm" class="w-full justify-start gap-2 text-destructive" @confirm="handleLogout">
|
||||
<LogOut class="h-4 w-4" />
|
||||
Logout
|
||||
</Button>
|
||||
</LogoutConfirmDialog>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
80
src/components/ui/LogoutConfirmDialog.vue
Normal file
80
src/components/ui/LogoutConfirmDialog.vue
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { LogOut, AlertTriangle } from 'lucide-vue-next'
|
||||
|
||||
// Define component name for better debugging
|
||||
defineOptions({
|
||||
name: 'LogoutConfirmDialog'
|
||||
})
|
||||
|
||||
interface Props {
|
||||
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'
|
||||
size?: 'default' | 'sm' | 'lg' | 'icon'
|
||||
class?: string
|
||||
children?: any
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'confirm'): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
variant: 'destructive',
|
||||
size: 'default'
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
const isOpen = ref(false)
|
||||
|
||||
const handleConfirm = () => {
|
||||
isOpen.value = false
|
||||
emit('confirm')
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
isOpen.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog v-model:open="isOpen">
|
||||
<DialogTrigger as-child>
|
||||
<slot>
|
||||
<Button :variant="variant" :size="size" :class="class">
|
||||
<LogOut class="h-4 w-4 mr-2" />
|
||||
Logout
|
||||
</Button>
|
||||
</slot>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent class="sm:max-w-md">
|
||||
<DialogHeader class="space-y-4">
|
||||
<div class="mx-auto w-12 h-12 rounded-full bg-gradient-to-br from-destructive to-destructive/80 p-0.5">
|
||||
<div class="w-full h-full rounded-full bg-background flex items-center justify-center">
|
||||
<AlertTriangle class="h-6 w-6 text-destructive" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center space-y-2">
|
||||
<DialogTitle class="text-xl font-semibold text-foreground">
|
||||
Confirm Logout
|
||||
</DialogTitle>
|
||||
<DialogDescription class="text-muted-foreground">
|
||||
Are you sure you want to logout? You will need to log in again to access your account.
|
||||
</DialogDescription>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
|
||||
<DialogFooter class="flex flex-col sm:flex-row gap-2 sm:gap-3">
|
||||
<Button variant="ghost" @click="handleCancel" class="flex-1 sm:flex-none">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="destructive" @click="handleConfirm" class="flex-1 sm:flex-none">
|
||||
<LogOut class="h-4 w-4 mr-2" />
|
||||
Logout
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { LogOut, AlertTriangle } from 'lucide-vue-next'
|
||||
|
||||
// Define component name for better debugging
|
||||
defineOptions({
|
||||
name: 'LogoutConfirmDialog'
|
||||
})
|
||||
|
||||
interface Props {
|
||||
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'
|
||||
size?: 'default' | 'sm' | 'lg' | 'icon'
|
||||
class?: string
|
||||
children?: any
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'confirm'): void
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
variant: 'outline',
|
||||
size: 'default'
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
const isOpen = ref(false)
|
||||
|
||||
const handleConfirm = () => {
|
||||
isOpen.value = false
|
||||
emit('confirm')
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
isOpen.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog v-model:open="isOpen">
|
||||
<DialogTrigger as-child>
|
||||
<slot>
|
||||
<Button :variant="variant" :size="size" :class="class">
|
||||
<LogOut class="h-4 w-4 mr-2" />
|
||||
Logout
|
||||
</Button>
|
||||
</slot>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent class="sm:max-w-md">
|
||||
<DialogHeader class="space-y-4">
|
||||
<div class="mx-auto w-12 h-12 rounded-full bg-gradient-to-br from-destructive to-destructive/80 p-0.5">
|
||||
<div class="w-full h-full rounded-full bg-background flex items-center justify-center">
|
||||
<AlertTriangle class="h-6 w-6 text-destructive" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center space-y-2">
|
||||
<DialogTitle class="text-xl font-semibold text-foreground">
|
||||
Confirm Logout
|
||||
</DialogTitle>
|
||||
<DialogDescription class="text-muted-foreground">
|
||||
Are you sure you want to logout? You will need to log in again to access your account.
|
||||
</DialogDescription>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
|
||||
<DialogFooter class="flex flex-col sm:flex-row gap-2 sm:gap-3">
|
||||
<Button variant="ghost" @click="handleCancel" class="flex-1 sm:flex-none">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="destructive" @click="handleConfirm" class="flex-1 sm:flex-none">
|
||||
<LogOut class="h-4 w-4 mr-2" />
|
||||
Logout
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
1
src/components/ui/LogoutConfirmDialog/index.ts
Normal file
1
src/components/ui/LogoutConfirmDialog/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default as LogoutConfirmDialog } from './LogoutConfirmDialog.vue'
|
||||
Loading…
Add table
Add a link
Reference in a new issue