refactor: Simplify Logout Confirmation Handling in Navbar

- Remove the showLogoutConfirm state variable from Navbar.vue to streamline the logout confirmation process.
- Update the LogoutConfirmDialog component to manage its own visibility state internally, enhancing encapsulation.
- Refactor the logout button to directly trigger the confirmation dialog, improving code clarity and user experience.

feat: Enhance Logout Confirmation Handling Across Components

- Introduce a showLogoutConfirm state variable in ProfileDialog.vue, UserProfile.vue, and Navbar.vue to manage the visibility of the Logout Confirmation dialog.
- Refactor logout buttons in these components to trigger the confirmation dialog, improving user experience and preventing accidental logouts.
- Update the LogoutConfirmDialog component to accept an isOpen prop for better control of its visibility, ensuring consistent functionality across the application.
This commit is contained in:
padreug 2025-08-10 23:45:12 +02:00
parent 1631c23717
commit 9e9137e6b0
4 changed files with 53 additions and 43 deletions

View file

@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed } from 'vue'
import { computed, ref } 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'
@ -21,6 +21,7 @@ defineProps<Props>()
const emit = defineEmits<Emits>()
const userDisplay = computed(() => auth.userDisplay.value)
const showLogoutConfirm = ref(false)
// Get the API base URL from environment variables
const apiBaseUrl = import.meta.env.VITE_LNBITS_BASE_URL || ''
@ -126,10 +127,10 @@ function handleClose() {
Change Password
</Button>
<LogoutConfirmDialog variant="destructive" class="w-full justify-start gap-2" @confirm="handleLogout">
<Button variant="ghost" @click="showLogoutConfirm = true" class="w-full justify-start gap-2 text-destructive hover:text-destructive/90 hover:bg-destructive/10">
<LogOut class="w-4 h-4" />
Logout
</LogoutConfirmDialog>
</Button>
</div>
</CardContent>
</Card>
@ -141,4 +142,10 @@ function handleClose() {
</div>
</DialogContent>
</Dialog>
<!-- Logout Confirm Dialog -->
<LogoutConfirmDialog v-model:is-open="showLogoutConfirm" variant="ghost" class="w-full justify-start gap-2" @confirm="handleLogout">
<LogOut class="w-4 h-4" />
Logout
</LogoutConfirmDialog>
</template>

View file

@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed } from 'vue'
import { computed, ref } from 'vue'
import { useRouter } from 'vue-router'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
@ -11,6 +11,7 @@ import { toast } from 'vue-sonner'
const router = useRouter()
const userDisplay = computed(() => auth.userDisplay.value)
const showLogoutConfirm = ref(false)
function handleLogout() {
auth.logout()
@ -59,12 +60,18 @@ function handleLogout() {
<Settings class="w-4 h-4 mr-2" />
Settings
</Button>
<LogoutConfirmDialog variant="destructive" size="sm" class="flex-1" @confirm="handleLogout">
<Button variant="ghost" size="sm" @click="showLogoutConfirm = true" class="flex-1 text-destructive hover:text-destructive/90 hover:bg-destructive/10">
<LogOut class="w-4 h-4 mr-2" />
Logout
</LogoutConfirmDialog>
</Button>
</div>
</CardContent>
</Card>
</div>
<!-- Logout Confirm Dialog -->
<LogoutConfirmDialog v-model:is-open="showLogoutConfirm" variant="ghost" size="sm" class="flex-1 text-destructive hover:text-destructive/90 hover:bg-destructive/10" @confirm="handleLogout">
<LogOut class="w-4 h-4 mr-2" />
Logout
</LogoutConfirmDialog>
</template>

View file

@ -156,11 +156,9 @@ const handleLogout = async () => {
Relay Hub Status
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem @click="() => {}" class="text-destructive p-0">
<LogoutConfirmDialog variant="destructive" size="sm" class="w-full justify-start" @confirm="handleLogout">
<DropdownMenuItem @click="showLogoutConfirm = true" class="gap-2 text-destructive">
<LogOut class="h-4 w-4" />
Logout
</LogoutConfirmDialog>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@ -243,7 +241,7 @@ const handleLogout = async () => {
<Activity class="h-4 w-4" />
Relay Hub Status
</Button>
<Button variant="destructive" size="sm" class="w-full justify-start gap-2" @click="() => showLogoutConfirm = true">
<Button variant="ghost" size="sm" @click="showLogoutConfirm = true" class="w-full justify-start gap-2 text-destructive hover:text-destructive/90 hover:bg-destructive/10">
<LogOut class="h-4 w-4" />
Logout
</Button>
@ -280,7 +278,10 @@ const handleLogout = async () => {
<!-- Profile Dialog -->
<ProfileDialog v-model:is-open="showProfileDialog" />
<!-- Logout Confirmation Dialog -->
<LogoutConfirmDialog v-model:is-open="showLogoutConfirm" @confirm="handleLogout" />
<!-- Logout Confirm Dialog -->
<LogoutConfirmDialog v-model:is-open="showLogoutConfirm" variant="ghost" size="sm" class="w-full justify-start gap-2" @confirm="handleLogout">
<LogOut class="h-4 w-4" />
Logout
</LogoutConfirmDialog>
</nav>
</template>

View file

@ -1,19 +1,13 @@
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ref, computed, watch } 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
isOpen?: boolean
}
@ -23,27 +17,28 @@ interface Emits {
}
const props = withDefaults(defineProps<Props>(), {
variant: 'destructive',
variant: 'ghost',
size: 'default'
})
// Default classes for the logout button styling
const defaultClasses = computed(() => {
const baseClasses = 'gap-2 text-destructive hover:text-destructive/90 hover:bg-destructive/10'
const emit = defineEmits<Emits>()
const internalIsOpen = ref(false)
if (props.class) {
// Merge custom classes with base classes, ensuring red styling is preserved
return `${props.class} ${baseClasses}`
// Use external control if provided, otherwise use internal state
const isOpen = computed({
get: () => props.isOpen !== undefined ? props.isOpen : internalIsOpen.value,
set: (value: boolean) => {
if (props.isOpen !== undefined) {
emit('update:isOpen', value)
} else {
internalIsOpen.value = value
}
}
// Default styling that matches the original red logout button
return baseClasses
})
const emit = defineEmits<Emits>()
const isOpen = computed({
get: () => props.isOpen ?? false,
set: (value: boolean) => emit('update:isOpen', value)
const buttonClasses = computed(() => {
const baseClasses = 'text-destructive hover:text-destructive/90 hover:bg-destructive/10'
return props.class ? `${baseClasses} ${props.class}` : baseClasses
})
const handleConfirm = () => {
@ -58,10 +53,10 @@ const handleCancel = () => {
<template>
<Dialog v-model:open="isOpen">
<DialogTrigger v-if="props.isOpen === undefined" as-child>
<DialogTrigger as-child v-if="props.isOpen === undefined">
<slot>
<Button :variant="variant" :size="size" :class="defaultClasses">
<LogOut class="h-4 w-4" />
<Button :variant="variant" :size="size" :class="buttonClasses">
<LogOut class="h-4 w-4 mr-2" />
Logout
</Button>
</slot>