Agregar modal personalizado de confirmación de salida
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 55s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 55s
- Reemplazar prompt del navegador por modal personalizado - Modal muestra lista de campos modificados con estilo de la app - Distingue entre almacenamiento local y servidor - Incluye soporte para modo oscuro - Mejora UX con diseño responsive
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
icon="i-heroicons-x-mark"
|
||||
@click="$emit('close')"
|
||||
@click="handleClose"
|
||||
>
|
||||
Cancelar
|
||||
</UButton>
|
||||
@@ -262,7 +262,7 @@
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
size="lg"
|
||||
@click="$emit('close')"
|
||||
@click="handleClose"
|
||||
:disabled="isSubmitting"
|
||||
>
|
||||
Cancelar
|
||||
@@ -311,6 +311,63 @@
|
||||
</div>
|
||||
</template>
|
||||
</UModal>
|
||||
|
||||
<!-- Modal de confirmación de salida -->
|
||||
<UModal v-model:open="showExitConfirm">
|
||||
<template #content>
|
||||
<div class="exit-confirm-modal">
|
||||
<div class="exit-modal-header">
|
||||
<UIcon name="i-heroicons-exclamation-triangle" class="w-12 h-12 text-warning-500" />
|
||||
<h3 class="exit-modal-title">¿Estás seguro de salir?</h3>
|
||||
</div>
|
||||
|
||||
<div class="exit-modal-body">
|
||||
<p class="exit-modal-message">
|
||||
Tienes cambios pendientes en:
|
||||
</p>
|
||||
<ul class="exit-modal-fields">
|
||||
<li v-for="field in modifiedFieldsList" :key="field" class="exit-field-item">
|
||||
<UIcon name="i-heroicons-pencil-square" class="w-4 h-4 text-warning-500" />
|
||||
{{ field }}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="exit-modal-notice">
|
||||
<UIcon name="i-heroicons-information-circle" class="w-5 h-5" />
|
||||
<p>
|
||||
Estos cambios quedarán guardados <strong>localmente en tu navegador</strong>,
|
||||
pero <strong class="text-error-600 dark:text-error-400">NO han sido enviados al servidor</strong>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="exit-modal-help">
|
||||
<UIcon name="i-heroicons-light-bulb" class="w-5 h-5 text-primary-500" />
|
||||
<p>
|
||||
Para guardar definitivamente, haz clic en <strong>"Guardar cambios"</strong>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="exit-modal-actions">
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="soft"
|
||||
size="lg"
|
||||
@click="showExitConfirm = false"
|
||||
>
|
||||
<UIcon name="i-heroicons-arrow-left" class="w-5 h-5" />
|
||||
Continuar editando
|
||||
</UButton>
|
||||
<UButton
|
||||
color="error"
|
||||
size="lg"
|
||||
@click="confirmExit"
|
||||
>
|
||||
<UIcon name="i-heroicons-arrow-right-on-rectangle" class="w-5 h-5" />
|
||||
Salir sin guardar
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</UModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -319,12 +376,13 @@ const { user } = useAuthentik()
|
||||
const toast = useToast()
|
||||
|
||||
// Emits
|
||||
defineEmits(['close'])
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
// Estado del formulario
|
||||
const isSubmitting = ref(false)
|
||||
const isUploading = ref(false)
|
||||
const showCamera = ref(false)
|
||||
const showExitConfirm = ref(false)
|
||||
|
||||
// Cookie para persistir cambios del formulario
|
||||
const formCookie = useCookie<Record<string, string>>('profile-form-draft', {
|
||||
@@ -536,6 +594,23 @@ const handleAvatarCapture = async (imageBlob: Blob) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Manejar intento de cerrar formulario
|
||||
const handleClose = () => {
|
||||
if (hasChanges.value) {
|
||||
// Si hay cambios, mostrar modal de confirmación
|
||||
showExitConfirm.value = true
|
||||
} else {
|
||||
// Si no hay cambios, cerrar directamente
|
||||
emit('close')
|
||||
}
|
||||
}
|
||||
|
||||
// Confirmar salida sin guardar
|
||||
const confirmExit = () => {
|
||||
showExitConfirm.value = false
|
||||
emit('close')
|
||||
}
|
||||
|
||||
// Reiniciar formulario a valores originales
|
||||
const resetForm = () => {
|
||||
if (!confirm('¿Estás seguro de descartar todos los cambios?\n\nSe restaurarán los valores originales y se eliminará el borrador guardado.')) {
|
||||
@@ -746,6 +821,120 @@ const removeAvatar = async () => {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* Modal de confirmación de salida */
|
||||
.exit-confirm-modal {
|
||||
padding: 2rem;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.exit-modal-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.exit-modal-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-gray-900);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.exit-modal-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.exit-modal-message {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-gray-800);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.exit-modal-fields {
|
||||
list-style: none;
|
||||
padding: 1rem;
|
||||
margin: 0;
|
||||
background: rgba(var(--color-warning-500), 0.1);
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid rgba(var(--color-warning-500), 0.3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.exit-field-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
|
||||
.exit-modal-notice {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
background: rgba(var(--color-error-500), 0.1);
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid rgba(var(--color-error-500), 0.3);
|
||||
}
|
||||
|
||||
.exit-modal-notice p {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
|
||||
.exit-modal-help {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
background: rgba(var(--color-primary-500), 0.1);
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid rgba(var(--color-primary-500), 0.3);
|
||||
}
|
||||
|
||||
.exit-modal-help p {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
|
||||
.exit-modal-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.exit-confirm-modal {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.exit-modal-title {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.exit-modal-actions {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.exit-modal-actions button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@@ -827,4 +1016,39 @@ const removeAvatar = async () => {
|
||||
.dark .modal-title {
|
||||
color: var(--color-gray-100) !important;
|
||||
}
|
||||
|
||||
.dark .exit-modal-title {
|
||||
color: var(--color-gray-100) !important;
|
||||
}
|
||||
|
||||
.dark .exit-modal-message {
|
||||
color: var(--color-gray-200) !important;
|
||||
}
|
||||
|
||||
.dark .exit-field-item {
|
||||
color: var(--color-gray-300) !important;
|
||||
}
|
||||
|
||||
.dark .exit-modal-fields {
|
||||
background: rgba(var(--color-warning-500), 0.15) !important;
|
||||
border-color: rgba(var(--color-warning-500), 0.4) !important;
|
||||
}
|
||||
|
||||
.dark .exit-modal-notice {
|
||||
background: rgba(var(--color-error-500), 0.15) !important;
|
||||
border-color: rgba(var(--color-error-500), 0.4) !important;
|
||||
}
|
||||
|
||||
.dark .exit-modal-notice p {
|
||||
color: var(--color-gray-300) !important;
|
||||
}
|
||||
|
||||
.dark .exit-modal-help {
|
||||
background: rgba(var(--color-primary-500), 0.15) !important;
|
||||
border-color: rgba(var(--color-primary-500), 0.4) !important;
|
||||
}
|
||||
|
||||
.dark .exit-modal-help p {
|
||||
color: var(--color-gray-300) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user