Showing
9 changed files
with
120 additions
and
26 deletions
| ... | @@ -111,35 +111,35 @@ | ... | @@ -111,35 +111,35 @@ |
| 111 | ============================================ */ | 111 | ============================================ */ |
| 112 | 112 | ||
| 113 | :root { | 113 | :root { |
| 114 | - /* Modo claro (default) - Slate/White */ | 114 | + /* Modo claro (default) - Estilo Apple, limpio y con contraste */ |
| 115 | - --theme-body: #fafafa; | 115 | + --theme-body: #ffffff; |
| 116 | - --theme-background: #f8fafc; | 116 | + --theme-background: #ffffff; |
| 117 | - --theme-titulo: #1a1a1a; | 117 | + --theme-titulo: #1d1d1f; |
| 118 | - --theme-texto: #64748b; | 118 | + --theme-texto: #424245; |
| 119 | - --theme-fill: #f1f5f9; | 119 | + --theme-fill: #ffffff; |
| 120 | - --theme-borde: rgba(0, 0, 0, 0.08); | 120 | + --theme-borde: rgba(0, 0, 0, 0.04); |
| 121 | - --theme-accent: #c9a751; | 121 | + --theme-accent: #9A8550; |
| 122 | - --theme-accent-hover: #b8963f; | 122 | + --theme-accent-hover: #857040; |
| 123 | 123 | ||
| 124 | /* Superficies alternativas */ | 124 | /* Superficies alternativas */ |
| 125 | --theme-surface: #ffffff; | 125 | --theme-surface: #ffffff; |
| 126 | - --theme-surface-hover: #f8fafc; | 126 | + --theme-surface-hover: #f5f5f7; |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | :root.dark { | 129 | :root.dark { |
| 130 | - /* Modo oscuro */ | 130 | + /* Modo oscuro - Cálido como el landing (#1C1C1A base) */ |
| 131 | - --theme-body: #0d0d0d; | 131 | + --theme-body: #1C1C1A; |
| 132 | - --theme-background: #1a1a1a; | 132 | + --theme-background: #242422; |
| 133 | - --theme-titulo: #e8e4d8; | 133 | + --theme-titulo: #F5F0E8; |
| 134 | - --theme-texto: #b8b3a3; | 134 | + --theme-texto: #B8B5AD; |
| 135 | - --theme-fill: #2d2d2d; | 135 | + --theme-fill: #2A2A27; |
| 136 | - --theme-borde: rgba(255, 255, 255, 0.1); | 136 | + --theme-borde: rgba(255, 255, 255, 0.08); |
| 137 | - --theme-accent: #c9a751; | 137 | + --theme-accent: #C9A751; |
| 138 | - --theme-accent-hover: #d4b76a; | 138 | + --theme-accent-hover: #D4B76A; |
| 139 | 139 | ||
| 140 | /* Superficies alternativas */ | 140 | /* Superficies alternativas */ |
| 141 | - --theme-surface: #1a1a1a; | 141 | + --theme-surface: #232321; |
| 142 | - --theme-surface-hover: #2d2d2d; | 142 | + --theme-surface-hover: #2A2A27; |
| 143 | } | 143 | } |
| 144 | 144 | ||
| 145 | /* ============================================ | 145 | /* ============================================ | ... | ... |
| ... | @@ -49,6 +49,7 @@ | ... | @@ -49,6 +49,7 @@ |
| 49 | <div class="relative group/home"> | 49 | <div class="relative group/home"> |
| 50 | <a | 50 | <a |
| 51 | href="/" | 51 | href="/" |
| 52 | + data-sveltekit-preload-data="off" | ||
| 52 | class="nav-icon p-2 rounded-full transition-all block" | 53 | class="nav-icon p-2 rounded-full transition-all block" |
| 53 | > | 54 | > |
| 54 | <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"> | 55 | <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"> | ... | ... |
| ... | @@ -35,8 +35,8 @@ const createClasificadorCache = () => { | ... | @@ -35,8 +35,8 @@ const createClasificadorCache = () => { |
| 35 | const cache = currentCache.objetoGasto; | 35 | const cache = currentCache.objetoGasto; |
| 36 | if (!cache.lastFetched) return null; | 36 | if (!cache.lastFetched) return null; |
| 37 | 37 | ||
| 38 | - // Cache valid for 5 minutes | 38 | + // Cache valid for 30 minutes |
| 39 | - const isValid = Date.now() - cache.lastFetched < 5 * 60 * 1000; | 39 | + const isValid = Date.now() - cache.lastFetched < 30 * 60 * 1000; |
| 40 | if (!isValid) return null; | 40 | if (!isValid) return null; |
| 41 | 41 | ||
| 42 | return cache; | 42 | return cache; | ... | ... |
src/lib/stores/navigating.js
0 → 100644
| 1 | <script> | 1 | <script> |
| 2 | import '../app.css'; | 2 | import '../app.css'; |
| 3 | - import { page } from '$app/stores'; | 3 | + import { page, navigating } from '$app/stores'; |
| 4 | import favicon from '$lib/assets/favicon.svg'; | 4 | import favicon from '$lib/assets/favicon.svg'; |
| 5 | import Navbar from '$lib/components/ui/Navbar.svelte'; | 5 | import Navbar from '$lib/components/ui/Navbar.svelte'; |
| 6 | import NavigationDrawer from '$lib/components/layout/NavigationDrawer.svelte'; | 6 | import NavigationDrawer from '$lib/components/layout/NavigationDrawer.svelte'; |
| 7 | import { openDrawer } from '$lib/stores/drawer.js'; | 7 | import { openDrawer } from '$lib/stores/drawer.js'; |
| 8 | + import Spinner from '$lib/components/ui/Spinner.svelte'; | ||
| 8 | 9 | ||
| 9 | let { children } = $props(); | 10 | let { children } = $props(); |
| 10 | 11 | ||
| 11 | // Don't show navbar on landing page | 12 | // Don't show navbar on landing page |
| 12 | let isLanding = $derived($page.url.pathname === '/'); | 13 | let isLanding = $derived($page.url.pathname === '/'); |
| 14 | + | ||
| 15 | + // Navigation loading state | ||
| 16 | + let showLoader = $state(false); | ||
| 17 | + let loaderTimeout = null; | ||
| 18 | + let navStartTime = null; | ||
| 19 | + | ||
| 20 | + // Show loader after 150ms delay (avoid flash for fast navigations) | ||
| 21 | + $effect(() => { | ||
| 22 | + if ($navigating) { | ||
| 23 | + navStartTime = performance.now(); | ||
| 24 | + console.log(`[NAV] Navigation started → ${$navigating.to?.url?.pathname}`); | ||
| 25 | + loaderTimeout = setTimeout(() => { | ||
| 26 | + showLoader = true; | ||
| 27 | + }, 150); | ||
| 28 | + } else { | ||
| 29 | + if (loaderTimeout) clearTimeout(loaderTimeout); | ||
| 30 | + if (navStartTime) { | ||
| 31 | + console.log(`[NAV] Navigation complete: ${(performance.now() - navStartTime).toFixed(0)}ms`); | ||
| 32 | + navStartTime = null; | ||
| 33 | + } | ||
| 34 | + showLoader = false; | ||
| 35 | + } | ||
| 36 | + }); | ||
| 13 | </script> | 37 | </script> |
| 14 | 38 | ||
| 15 | <svelte:head> | 39 | <svelte:head> |
| ... | @@ -26,8 +50,55 @@ | ... | @@ -26,8 +50,55 @@ |
| 26 | <Navbar onOpenDrawer={openDrawer} /> | 50 | <Navbar onOpenDrawer={openDrawer} /> |
| 27 | {/if} | 51 | {/if} |
| 28 | 52 | ||
| 53 | +<!-- Navigation loading overlay --> | ||
| 54 | +{#if showLoader} | ||
| 55 | + <div class="nav-loader"> | ||
| 56 | + <div class="nav-loader-content"> | ||
| 57 | + <Spinner size={32} color="#C9A751" /> | ||
| 58 | + <span class="nav-loader-text">Cargando...</span> | ||
| 59 | + </div> | ||
| 60 | + </div> | ||
| 61 | +{/if} | ||
| 62 | + | ||
| 29 | <div class="min-h-screen" style="background-color: var(--theme-body);"> | 63 | <div class="min-h-screen" style="background-color: var(--theme-body);"> |
| 30 | <main> | 64 | <main> |
| 31 | {@render children()} | 65 | {@render children()} |
| 32 | </main> | 66 | </main> |
| 33 | </div> | 67 | </div> |
| 68 | + | ||
| 69 | +<style> | ||
| 70 | + .nav-loader { | ||
| 71 | + position: fixed; | ||
| 72 | + top: 0; | ||
| 73 | + left: 0; | ||
| 74 | + right: 0; | ||
| 75 | + bottom: 0; | ||
| 76 | + background: rgba(28, 28, 26, 0.85); | ||
| 77 | + backdrop-filter: blur(8px); | ||
| 78 | + -webkit-backdrop-filter: blur(8px); | ||
| 79 | + z-index: 9999; | ||
| 80 | + display: flex; | ||
| 81 | + align-items: center; | ||
| 82 | + justify-content: center; | ||
| 83 | + animation: fadeIn 0.15s ease-out; | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + .nav-loader-content { | ||
| 87 | + display: flex; | ||
| 88 | + flex-direction: column; | ||
| 89 | + align-items: center; | ||
| 90 | + gap: 16px; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + .nav-loader-text { | ||
| 94 | + font-family: 'DM Mono', monospace; | ||
| 95 | + font-size: 13px; | ||
| 96 | + color: #B8B5AD; | ||
| 97 | + letter-spacing: 0.05em; | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + @keyframes fadeIn { | ||
| 101 | + from { opacity: 0; } | ||
| 102 | + to { opacity: 1; } | ||
| 103 | + } | ||
| 104 | +</style> | ... | ... |
| 1 | <script> | 1 | <script> |
| 2 | import { onMount } from 'svelte'; | 2 | import { onMount } from 'svelte'; |
| 3 | import { goto } from '$app/navigation'; | 3 | import { goto } from '$app/navigation'; |
| 4 | + import { supabase } from '$lib/supabase'; | ||
| 4 | import { | 5 | import { |
| 5 | query, | 6 | query, |
| 6 | results, | 7 | results, |
| ... | @@ -15,6 +16,13 @@ | ... | @@ -15,6 +16,13 @@ |
| 15 | navigateResults | 16 | navigateResults |
| 16 | } from '$lib/stores/searchStore'; | 17 | } from '$lib/stores/searchStore'; |
| 17 | 18 | ||
| 19 | + // Pre-warm Supabase connection (evita cold start al navegar) | ||
| 20 | + async function warmupSupabase() { | ||
| 21 | + console.time('[WARMUP] Supabase'); | ||
| 22 | + await supabase.schema('ppto').from('vista_objeto_estado').select('objeto').limit(1); | ||
| 23 | + console.timeEnd('[WARMUP] Supabase'); | ||
| 24 | + } | ||
| 25 | + | ||
| 18 | let mounted = false; | 26 | let mounted = false; |
| 19 | let mode = 'archivo'; | 27 | let mode = 'archivo'; |
| 20 | let searchFocused = false; | 28 | let searchFocused = false; |
| ... | @@ -56,6 +64,8 @@ | ... | @@ -56,6 +64,8 @@ |
| 56 | onMount(() => { | 64 | onMount(() => { |
| 57 | setTimeout(() => { mounted = true; }, 80); | 65 | setTimeout(() => { mounted = true; }, 80); |
| 58 | initIndex(); | 66 | initIndex(); |
| 67 | + // Pre-calentar Supabase en background para evitar cold start | ||
| 68 | + warmupSupabase(); | ||
| 59 | }); | 69 | }); |
| 60 | 70 | ||
| 61 | function toggleMobileSettings() { | 71 | function toggleMobileSettings() { |
| ... | @@ -791,7 +801,7 @@ | ... | @@ -791,7 +801,7 @@ |
| 791 | <div class="descargas-formats"> | 801 | <div class="descargas-formats"> |
| 792 | <span class="format-pill">.csv</span> | 802 | <span class="format-pill">.csv</span> |
| 793 | <span class="format-pill">.parquet</span> | 803 | <span class="format-pill">.parquet</span> |
| 794 | - <span class="format-pill">.json</span> | 804 | + <span class="format-pill">.gpkg</span> |
| 795 | </div> | 805 | </div> |
| 796 | <a href="/descargas" class="descargas-link"> | 806 | <a href="/descargas" class="descargas-link"> |
| 797 | <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 4v12"/><polyline points="8 12 12 16 16 12"/><path d="M4 18h16"/></svg> | 807 | <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 4v12"/><polyline points="8 12 12 16 16 12"/><path d="M4 18h16"/></svg> |
| ... | @@ -872,7 +882,7 @@ | ... | @@ -872,7 +882,7 @@ |
| 872 | .hero-search{display:flex;justify-content:center;position:relative;z-index:50} | 882 | .hero-search{display:flex;justify-content:center;position:relative;z-index:50} |
| 873 | .hero-tag{font-family:var(--mono);font-size:12px;letter-spacing:0.25em;color:#B8B5AD} | 883 | .hero-tag{font-family:var(--mono);font-size:12px;letter-spacing:0.25em;color:#B8B5AD} |
| 874 | .hero-title{font-size:clamp(44px,5.5vw,72px);font-weight:900;line-height:1.02;letter-spacing:-0.035em;max-width:720px;margin:14px auto 0} | 884 | .hero-title{font-size:clamp(44px,5.5vw,72px);font-weight:900;line-height:1.02;letter-spacing:-0.035em;max-width:720px;margin:14px auto 0} |
| 875 | - .hero-sub{font-family:var(--sans);font-size:clamp(16px,1.6vw,18px);color:#9B9890;max-width:100%;margin-top:14px;line-height:1.6} | 885 | + .hero-sub{font-family:var(--sans);font-size:clamp(16px,1.6vw,18px);color:#A5A29A;max-width:100%;margin-top:14px;line-height:1.6} |
| 876 | .hero-stats{font-family:var(--mono);font-size:13px;letter-spacing:0.04em;color:#B8B5AD;margin-top:16px;line-height:1} | 886 | .hero-stats{font-family:var(--mono);font-size:13px;letter-spacing:0.04em;color:#B8B5AD;margin-top:16px;line-height:1} |
| 877 | .stat-num{color:#F5F0E8} | 887 | .stat-num{color:#F5F0E8} |
| 878 | .stat-dot{margin:0 10px;opacity:0.3} | 888 | .stat-dot{margin:0 10px;opacity:0.3} | ... | ... |
This diff is collapsed. Click to expand it.
| ... | @@ -2,13 +2,16 @@ import { supabase } from '$lib/supabase'; | ... | @@ -2,13 +2,16 @@ import { supabase } from '$lib/supabase'; |
| 2 | import { error } from '@sveltejs/kit'; | 2 | import { error } from '@sveltejs/kit'; |
| 3 | 3 | ||
| 4 | export async function load({ params }) { | 4 | export async function load({ params }) { |
| 5 | + console.time('[SERVER] Total load objeto'); | ||
| 5 | const { codigo } = params; | 6 | const { codigo } = params; |
| 6 | 7 | ||
| 8 | + console.time('[SERVER] Query clas_objetos'); | ||
| 7 | const { data, error: dbError } = await supabase | 9 | const { data, error: dbError } = await supabase |
| 8 | .schema('ppto') | 10 | .schema('ppto') |
| 9 | .from('clas_objetos') | 11 | .from('clas_objetos') |
| 10 | .select('*') | 12 | .select('*') |
| 11 | .eq('objeto', codigo); | 13 | .eq('objeto', codigo); |
| 14 | + console.timeEnd('[SERVER] Query clas_objetos'); | ||
| 12 | 15 | ||
| 13 | if (dbError || !data || data.length === 0) { | 16 | if (dbError || !data || data.length === 0) { |
| 14 | throw error(404, 'Objeto no encontrado'); | 17 | throw error(404, 'Objeto no encontrado'); |
| ... | @@ -21,6 +24,7 @@ export async function load({ params }) { | ... | @@ -21,6 +24,7 @@ export async function load({ params }) { |
| 21 | let padres = []; | 24 | let padres = []; |
| 22 | let hijos = []; | 25 | let hijos = []; |
| 23 | 26 | ||
| 27 | + console.time('[SERVER] Query padres'); | ||
| 24 | // Buscar padre según nivel | 28 | // Buscar padre según nivel |
| 25 | if (objeto.nivel === 'subpartida') { | 29 | if (objeto.nivel === 'subpartida') { |
| 26 | // Padre es partida | 30 | // Padre es partida |
| ... | @@ -58,7 +62,9 @@ export async function load({ params }) { | ... | @@ -58,7 +62,9 @@ export async function load({ params }) { |
| 58 | .eq('objeto', grupoCodigo); | 62 | .eq('objeto', grupoCodigo); |
| 59 | if (padreData?.length) padres.unshift(padreData[0]); | 63 | if (padreData?.length) padres.unshift(padreData[0]); |
| 60 | } | 64 | } |
| 65 | + console.timeEnd('[SERVER] Query padres'); | ||
| 61 | 66 | ||
| 67 | + console.time('[SERVER] Query hijos'); | ||
| 62 | // Buscar hijos según nivel | 68 | // Buscar hijos según nivel |
| 63 | if (objeto.nivel === 'grupo') { | 69 | if (objeto.nivel === 'grupo') { |
| 64 | const { data: hijosData } = await supabase | 70 | const { data: hijosData } = await supabase |
| ... | @@ -100,7 +106,9 @@ export async function load({ params }) { | ... | @@ -100,7 +106,9 @@ export async function load({ params }) { |
| 100 | return acc; | 106 | return acc; |
| 101 | }, []) || []; | 107 | }, []) || []; |
| 102 | } | 108 | } |
| 109 | + console.timeEnd('[SERVER] Query hijos'); | ||
| 103 | 110 | ||
| 111 | + console.timeEnd('[SERVER] Total load objeto'); | ||
| 104 | return { | 112 | return { |
| 105 | objeto, | 113 | objeto, |
| 106 | padres, | 114 | padres, | ... | ... |
This diff is collapsed. Click to expand it.
-
Please register or login to post a comment