Feature: Mejorar UX del tema y persistencia
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
- Mover botón editar al lado del nombre (siempre visible, sutil) - Quitar efecto hover del header - Detectar tema del sistema operativo automáticamente - Actualizar theme-color dinámicamente (azul cielo día / oscuro noche) - Usar cookies para persistir tema y filtros (1 año) - Sincronizar filtros de apps con cookies
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="user-header">
|
||||
<!-- Header principal clickable -->
|
||||
<div class="header-content" @click="openEditProfile">
|
||||
<!-- Header principal -->
|
||||
<div class="header-content">
|
||||
<!-- Avatar -->
|
||||
<div class="avatar-section">
|
||||
<UAvatar
|
||||
@@ -15,7 +15,12 @@
|
||||
|
||||
<!-- Info del usuario -->
|
||||
<div class="user-info">
|
||||
<h1 class="user-name">{{ user?.name || user?.username }}</h1>
|
||||
<div class="user-name-row">
|
||||
<h1 class="user-name">{{ user?.name || user?.username }}</h1>
|
||||
<button class="edit-button" @click.stop="openEditProfile" title="Editar perfil">
|
||||
<UIcon name="i-heroicons-pencil-square" class="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
<p class="user-email">{{ user?.email }}</p>
|
||||
<div class="user-badges">
|
||||
<UBadge
|
||||
@@ -39,12 +44,6 @@
|
||||
</UBadge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ícono de edición -->
|
||||
<div class="edit-hint">
|
||||
<UIcon name="i-heroicons-pencil-square" class="w-5 h-5" />
|
||||
<span class="edit-text">Editar perfil</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Botón de tema -->
|
||||
@@ -202,20 +201,10 @@ const handleSubmit = async () => {
|
||||
box-shadow:
|
||||
0 8px 32px 0 rgba(31, 38, 135, 0.15),
|
||||
inset 0 1px 1px 0 rgba(255, 255, 255, 0.3);
|
||||
cursor: pointer;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.header-content:hover {
|
||||
transform: translateY(-4px) scale(1.01);
|
||||
box-shadow:
|
||||
0 12px 40px 0 rgba(31, 38, 135, 0.25),
|
||||
0 0 0 1px rgba(var(--color-primary-500), 0.4),
|
||||
inset 0 1px 1px 0 rgba(255, 255, 255, 0.4);
|
||||
border-color: rgba(var(--color-primary-500), 0.5);
|
||||
}
|
||||
|
||||
.avatar-section {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -225,15 +214,17 @@ const handleSubmit = async () => {
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.header-content:hover .avatar-glow {
|
||||
box-shadow: 0 0 30px rgba(var(--color-primary-500), 0.6);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.user-name-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
@@ -271,28 +262,22 @@ const handleSubmit = async () => {
|
||||
inset 0 1px 1px 0 rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.edit-hint {
|
||||
.edit-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
background: rgba(var(--color-primary-500), 0.1);
|
||||
justify-content: center;
|
||||
padding: 0.25rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--color-gray-400);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.edit-button:hover {
|
||||
color: rgb(var(--color-primary-500));
|
||||
opacity: 0;
|
||||
transform: translateX(-10px);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.header-content:hover .edit-hint {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.edit-text {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
background: rgba(var(--color-primary-500), 0.1);
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
@@ -348,15 +333,6 @@ const handleSubmit = async () => {
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
|
||||
.edit-hint {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.edit-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
|
||||
@@ -122,8 +122,15 @@ const { data: applications, pending, error, refresh } = await useFetch<Applicati
|
||||
default: () => []
|
||||
})
|
||||
|
||||
// Estado de filtros
|
||||
const selectedGroups = ref<string[]>([])
|
||||
// Cookie para persistir filtros seleccionados
|
||||
const filtersCookie = useCookie<string[]>('app-filters', {
|
||||
maxAge: 60 * 60 * 24 * 365, // 1 año
|
||||
sameSite: 'lax',
|
||||
default: () => []
|
||||
})
|
||||
|
||||
// Estado de filtros (inicializado desde cookie)
|
||||
const selectedGroups = ref<string[]>(filtersCookie.value || [])
|
||||
|
||||
// Estado para iconos fallidos (para no intentar cargarlos de nuevo)
|
||||
const failedIcons = ref<Set<string>>(new Set())
|
||||
@@ -222,8 +229,15 @@ const toggleGroup = (group: string) => {
|
||||
} else {
|
||||
selectedGroups.value.splice(index, 1)
|
||||
}
|
||||
// Guardar en cookie
|
||||
filtersCookie.value = selectedGroups.value
|
||||
}
|
||||
|
||||
// Sincronizar con cookie cuando cambian los filtros
|
||||
watch(selectedGroups, (newFilters) => {
|
||||
filtersCookie.value = newFilters
|
||||
})
|
||||
|
||||
// Refrescar automáticamente cada 5 minutos
|
||||
const refreshInterval = setInterval(() => {
|
||||
refresh()
|
||||
|
||||
Reference in New Issue
Block a user