Rafael Lopez

landing

...@@ -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;
......
1 +import { writable } from 'svelte/store';
2 +
3 +export const isNavigating = writable(false);
4 +export const navigationTarget = writable(null);
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}
......
...@@ -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.