All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 47s
- Crear formulario completo de configuración de colores - Agregar 8 campos editables: bg, surface, border, primary, primaryStrong, accent, text, textMuted - Implementar color pickers + inputs de texto hexadecimal - Agregar vista previa en tiempo real (toggle opcional) - Guardar configuración en localStorage - Aplicar cambios dinámicamente con CSS variables - Incluir botón para restaurar colores por defecto - Colores default: tema café actual de la aplicación
361 lines
13 KiB
Vue
361 lines
13 KiB
Vue
<script setup lang="ts">
|
|
import { ref, watch } from 'vue'
|
|
|
|
definePageMeta({
|
|
layout: 'dashboard',
|
|
title: 'Configuración'
|
|
})
|
|
|
|
// Colores por defecto (actuales del tema café)
|
|
const defaultTheme = {
|
|
bg: '#14100b',
|
|
surface: '#1f180f',
|
|
border: '#3a2a16',
|
|
primary: '#e0c080',
|
|
primaryStrong: '#c08040',
|
|
accent: '#ffe0a0',
|
|
text: '#fef9f0',
|
|
textMuted: '#d8c7a6'
|
|
}
|
|
|
|
// Estado del tema (inicializar con valores actuales del CSS)
|
|
const theme = ref({ ...defaultTheme })
|
|
|
|
// Cargar tema guardado en localStorage
|
|
onMounted(() => {
|
|
const savedTheme = localStorage.getItem('custom-theme')
|
|
if (savedTheme) {
|
|
try {
|
|
theme.value = JSON.parse(savedTheme)
|
|
applyTheme(theme.value)
|
|
} catch (e) {
|
|
console.error('Error al cargar tema guardado', e)
|
|
}
|
|
}
|
|
})
|
|
|
|
// Aplicar tema al documento
|
|
const applyTheme = (themeColors: typeof defaultTheme) => {
|
|
const root = document.documentElement
|
|
root.style.setProperty('--brand-bg', themeColors.bg)
|
|
root.style.setProperty('--brand-surface', themeColors.surface)
|
|
root.style.setProperty('--brand-border', themeColors.border)
|
|
root.style.setProperty('--brand-primary', themeColors.primary)
|
|
root.style.setProperty('--brand-primary-strong', themeColors.primaryStrong)
|
|
root.style.setProperty('--brand-accent', themeColors.accent)
|
|
root.style.setProperty('--brand-text', themeColors.text)
|
|
root.style.setProperty('--brand-text-muted', themeColors.textMuted)
|
|
}
|
|
|
|
// Guardar tema
|
|
const saveTheme = () => {
|
|
localStorage.setItem('custom-theme', JSON.stringify(theme.value))
|
|
applyTheme(theme.value)
|
|
useToast().add({
|
|
title: 'Tema guardado',
|
|
description: 'Los cambios se aplicaron correctamente',
|
|
color: 'green'
|
|
})
|
|
}
|
|
|
|
// Resetear al tema por defecto
|
|
const resetTheme = () => {
|
|
theme.value = { ...defaultTheme }
|
|
localStorage.removeItem('custom-theme')
|
|
applyTheme(theme.value)
|
|
useToast().add({
|
|
title: 'Tema reseteado',
|
|
description: 'Se restauraron los colores por defecto',
|
|
color: 'blue'
|
|
})
|
|
}
|
|
|
|
// Vista previa en tiempo real (opcional)
|
|
const livePreview = ref(false)
|
|
watch(() => theme.value, (newTheme) => {
|
|
if (livePreview.value) {
|
|
applyTheme(newTheme)
|
|
}
|
|
}, { deep: true })
|
|
</script>
|
|
|
|
<template>
|
|
<UDashboardLayout>
|
|
<UDashboardPanel grow>
|
|
<UDashboardNavbar
|
|
title="Configuración"
|
|
description="Personaliza tu experiencia y preferencias del sistema"
|
|
/>
|
|
|
|
<UDashboardPanelContent>
|
|
<div class="max-w-4xl mx-auto space-y-8">
|
|
<!-- Configuración de Tema -->
|
|
<UCard>
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 rounded-lg bg-[var(--brand-primary)]/20 flex items-center justify-center">
|
|
<UIcon name="i-lucide-palette" class="size-5 text-[var(--brand-primary)]" />
|
|
</div>
|
|
<div>
|
|
<h3 class="font-semibold text-[var(--brand-text)]">
|
|
Tema y Apariencia
|
|
</h3>
|
|
<p class="text-sm text-[var(--brand-text-muted)]">
|
|
Personaliza los colores de la interfaz
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<UToggle v-model="livePreview" label="Vista previa en vivo" />
|
|
</div>
|
|
</template>
|
|
|
|
<div class="space-y-6">
|
|
<!-- Fondo Principal -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-[var(--brand-text)] mb-2">
|
|
Fondo Principal
|
|
</label>
|
|
<div class="flex gap-2">
|
|
<input
|
|
v-model="theme.bg"
|
|
type="color"
|
|
class="h-10 w-20 rounded-lg border border-[var(--brand-border)] cursor-pointer"
|
|
/>
|
|
<UInput
|
|
v-model="theme.bg"
|
|
placeholder="#14100b"
|
|
class="flex-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-[var(--brand-text)] mb-2">
|
|
Fondo Secundario (Surface)
|
|
</label>
|
|
<div class="flex gap-2">
|
|
<input
|
|
v-model="theme.surface"
|
|
type="color"
|
|
class="h-10 w-20 rounded-lg border border-[var(--brand-border)] cursor-pointer"
|
|
/>
|
|
<UInput
|
|
v-model="theme.surface"
|
|
placeholder="#1f180f"
|
|
class="flex-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bordes y Primario -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-[var(--brand-text)] mb-2">
|
|
Color de Bordes
|
|
</label>
|
|
<div class="flex gap-2">
|
|
<input
|
|
v-model="theme.border"
|
|
type="color"
|
|
class="h-10 w-20 rounded-lg border border-[var(--brand-border)] cursor-pointer"
|
|
/>
|
|
<UInput
|
|
v-model="theme.border"
|
|
placeholder="#3a2a16"
|
|
class="flex-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-[var(--brand-text)] mb-2">
|
|
Color Primario
|
|
</label>
|
|
<div class="flex gap-2">
|
|
<input
|
|
v-model="theme.primary"
|
|
type="color"
|
|
class="h-10 w-20 rounded-lg border border-[var(--brand-border)] cursor-pointer"
|
|
/>
|
|
<UInput
|
|
v-model="theme.primary"
|
|
placeholder="#e0c080"
|
|
class="flex-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Primario Fuerte y Acento -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-[var(--brand-text)] mb-2">
|
|
Color Primario Fuerte
|
|
</label>
|
|
<div class="flex gap-2">
|
|
<input
|
|
v-model="theme.primaryStrong"
|
|
type="color"
|
|
class="h-10 w-20 rounded-lg border border-[var(--brand-border)] cursor-pointer"
|
|
/>
|
|
<UInput
|
|
v-model="theme.primaryStrong"
|
|
placeholder="#c08040"
|
|
class="flex-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-[var(--brand-text)] mb-2">
|
|
Color de Acento
|
|
</label>
|
|
<div class="flex gap-2">
|
|
<input
|
|
v-model="theme.accent"
|
|
type="color"
|
|
class="h-10 w-20 rounded-lg border border-[var(--brand-border)] cursor-pointer"
|
|
/>
|
|
<UInput
|
|
v-model="theme.accent"
|
|
placeholder="#ffe0a0"
|
|
class="flex-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Texto y Texto Muted -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-[var(--brand-text)] mb-2">
|
|
Color de Texto
|
|
</label>
|
|
<div class="flex gap-2">
|
|
<input
|
|
v-model="theme.text"
|
|
type="color"
|
|
class="h-10 w-20 rounded-lg border border-[var(--brand-border)] cursor-pointer"
|
|
/>
|
|
<UInput
|
|
v-model="theme.text"
|
|
placeholder="#fef9f0"
|
|
class="flex-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-[var(--brand-text)] mb-2">
|
|
Color de Texto Secundario
|
|
</label>
|
|
<div class="flex gap-2">
|
|
<input
|
|
v-model="theme.textMuted"
|
|
type="color"
|
|
class="h-10 w-20 rounded-lg border border-[var(--brand-border)] cursor-pointer"
|
|
/>
|
|
<UInput
|
|
v-model="theme.textMuted"
|
|
placeholder="#d8c7a6"
|
|
class="flex-1"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Acciones -->
|
|
<div class="flex justify-between items-center pt-4 border-t border-[var(--brand-border)]">
|
|
<UButton
|
|
color="neutral"
|
|
variant="ghost"
|
|
icon="i-lucide-rotate-ccw"
|
|
@click="resetTheme"
|
|
>
|
|
Restaurar por defecto
|
|
</UButton>
|
|
<UButton
|
|
color="primary"
|
|
icon="i-lucide-save"
|
|
@click="saveTheme"
|
|
>
|
|
Guardar cambios
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<!-- Vista previa de colores -->
|
|
<UCard>
|
|
<template #header>
|
|
<div class="flex items-center gap-3">
|
|
<UIcon name="i-lucide-eye" class="size-5 text-[var(--brand-primary)]" />
|
|
<h3 class="font-semibold text-[var(--brand-text)]">
|
|
Vista Previa
|
|
</h3>
|
|
</div>
|
|
</template>
|
|
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
<div class="space-y-2">
|
|
<div class="h-20 rounded-lg border-2 border-[var(--brand-border)]" :style="{ backgroundColor: theme.bg }" />
|
|
<p class="text-xs text-center text-[var(--brand-text-muted)]">Fondo Principal</p>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<div class="h-20 rounded-lg border-2 border-[var(--brand-border)]" :style="{ backgroundColor: theme.surface }" />
|
|
<p class="text-xs text-center text-[var(--brand-text-muted)]">Surface</p>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<div class="h-20 rounded-lg border-2 border-[var(--brand-border)]" :style="{ backgroundColor: theme.primary }" />
|
|
<p class="text-xs text-center text-[var(--brand-text-muted)]">Primario</p>
|
|
</div>
|
|
<div class="space-y-2">
|
|
<div class="h-20 rounded-lg border-2 border-[var(--brand-border)]" :style="{ backgroundColor: theme.accent }" />
|
|
<p class="text-xs text-center text-[var(--brand-text-muted)]">Acento</p>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<!-- Otras configuraciones (placeholder) -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<UCard>
|
|
<template #header>
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 rounded-lg bg-amber-50 dark:bg-amber-950/30 flex items-center justify-center">
|
|
<UIcon name="i-lucide-bell" class="size-5 text-amber-600 dark:text-amber-400" />
|
|
</div>
|
|
<h3 class="font-semibold text-[var(--brand-text)]">
|
|
Notificaciones
|
|
</h3>
|
|
</div>
|
|
</template>
|
|
<p class="text-sm text-[var(--brand-text-muted)]">
|
|
Gestión de alertas y comunicaciones (Próximamente)
|
|
</p>
|
|
</UCard>
|
|
|
|
<UCard>
|
|
<template #header>
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 rounded-lg bg-green-50 dark:bg-green-950/30 flex items-center justify-center">
|
|
<UIcon name="i-lucide-shield" class="size-5 text-green-600 dark:text-green-400" />
|
|
</div>
|
|
<h3 class="font-semibold text-[var(--brand-text)]">
|
|
Privacidad
|
|
</h3>
|
|
</div>
|
|
</template>
|
|
<p class="text-sm text-[var(--brand-text-muted)]">
|
|
Control de datos y seguridad (Próximamente)
|
|
</p>
|
|
</UCard>
|
|
</div>
|
|
</div>
|
|
</UDashboardPanelContent>
|
|
</UDashboardPanel>
|
|
</UDashboardLayout>
|
|
</template>
|