Showing
5 changed files
with
121 additions
and
1 deletions
src/lib/components/ui/Spinner.svelte
0 → 100644
| 1 | +<script> | ||
| 2 | + let { size = 40, color = 'currentColor' } = $props(); | ||
| 3 | +</script> | ||
| 4 | + | ||
| 5 | +<svg | ||
| 6 | + fill={color} | ||
| 7 | + viewBox="0 -10 40 28" | ||
| 8 | + xmlns="http://www.w3.org/2000/svg" | ||
| 9 | + width={size} | ||
| 10 | + height={size * 0.7} | ||
| 11 | + class="spinner" | ||
| 12 | +> | ||
| 13 | + <circle cx="8" cy="12" r="3" class="first"/> | ||
| 14 | + <circle cx="16" cy="12" r="3"/> | ||
| 15 | + <circle cx="24" cy="12" r="3"/> | ||
| 16 | + <circle cx="32" cy="12" r="3" class="second"/> | ||
| 17 | +</svg> | ||
| 18 | + | ||
| 19 | +<style> | ||
| 20 | + @keyframes swing { | ||
| 21 | + 0% { | ||
| 22 | + transform: rotate(0deg); | ||
| 23 | + animation-timing-function: ease-out; | ||
| 24 | + } | ||
| 25 | + 25% { | ||
| 26 | + transform: rotate(50deg); | ||
| 27 | + animation-timing-function: ease-in; | ||
| 28 | + } | ||
| 29 | + 50% { | ||
| 30 | + transform: rotate(0deg); | ||
| 31 | + animation-timing-function: linear; | ||
| 32 | + } | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + @keyframes swing2 { | ||
| 36 | + 0% { | ||
| 37 | + transform: rotate(0deg); | ||
| 38 | + animation-timing-function: linear; | ||
| 39 | + } | ||
| 40 | + 50% { | ||
| 41 | + transform: rotate(0deg); | ||
| 42 | + animation-timing-function: ease-out; | ||
| 43 | + } | ||
| 44 | + 75% { | ||
| 45 | + transform: rotate(-50deg); | ||
| 46 | + animation-timing-function: ease-in; | ||
| 47 | + } | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + .spinner .first { | ||
| 51 | + animation: swing 1.2s linear infinite; | ||
| 52 | + transform-origin: center top; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + .spinner .second { | ||
| 56 | + animation: swing2 1.2s linear infinite; | ||
| 57 | + transform-origin: center top; | ||
| 58 | + } | ||
| 59 | +</style> |
src/lib/stores/clasificadorCache.js
0 → 100644
| 1 | +/** | ||
| 2 | + * Cache store for clasificador data | ||
| 3 | + * Persists data in memory to avoid reloading on navigation | ||
| 4 | + */ | ||
| 5 | +import { writable } from 'svelte/store'; | ||
| 6 | + | ||
| 7 | +// Cache structure | ||
| 8 | +const createClasificadorCache = () => { | ||
| 9 | + const { subscribe, set, update } = writable({ | ||
| 10 | + objetoGasto: { | ||
| 11 | + allItems: null, | ||
| 12 | + grupos: null, | ||
| 13 | + entities: null, | ||
| 14 | + availableYears: null, | ||
| 15 | + lastFetched: null | ||
| 16 | + } | ||
| 17 | + }); | ||
| 18 | + | ||
| 19 | + return { | ||
| 20 | + subscribe, | ||
| 21 | + | ||
| 22 | + // Set all clasificador objeto data | ||
| 23 | + setObjetoGasto: (data) => { | ||
| 24 | + update(cache => ({ | ||
| 25 | + ...cache, | ||
| 26 | + objetoGasto: { | ||
| 27 | + ...data, | ||
| 28 | + lastFetched: Date.now() | ||
| 29 | + } | ||
| 30 | + })); | ||
| 31 | + }, | ||
| 32 | + | ||
| 33 | + // Get cached data if valid (less than 5 minutes old) | ||
| 34 | + getObjetoGasto: (currentCache) => { | ||
| 35 | + const cache = currentCache.objetoGasto; | ||
| 36 | + if (!cache.lastFetched) return null; | ||
| 37 | + | ||
| 38 | + // Cache valid for 5 minutes | ||
| 39 | + const isValid = Date.now() - cache.lastFetched < 5 * 60 * 1000; | ||
| 40 | + if (!isValid) return null; | ||
| 41 | + | ||
| 42 | + return cache; | ||
| 43 | + }, | ||
| 44 | + | ||
| 45 | + // Clear cache | ||
| 46 | + clear: () => { | ||
| 47 | + set({ | ||
| 48 | + objetoGasto: { | ||
| 49 | + allItems: null, | ||
| 50 | + grupos: null, | ||
| 51 | + entities: null, | ||
| 52 | + availableYears: null, | ||
| 53 | + lastFetched: null | ||
| 54 | + } | ||
| 55 | + }); | ||
| 56 | + } | ||
| 57 | + }; | ||
| 58 | +}; | ||
| 59 | + | ||
| 60 | +export const clasificadorCache = createClasificadorCache(); |
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
| ... | @@ -3,6 +3,7 @@ | ... | @@ -3,6 +3,7 @@ |
| 3 | import { tweened } from 'svelte/motion'; | 3 | import { tweened } from 'svelte/motion'; |
| 4 | import { cubicOut } from 'svelte/easing'; | 4 | import { cubicOut } from 'svelte/easing'; |
| 5 | import { supabase } from '$lib/supabase'; | 5 | import { supabase } from '$lib/supabase'; |
| 6 | + import Spinner from '$lib/components/ui/Spinner.svelte'; | ||
| 6 | 7 | ||
| 7 | // ══════════════════════════════════════════════════════════════ | 8 | // ══════════════════════════════════════════════════════════════ |
| 8 | // ESTADO GENERAL | 9 | // ESTADO GENERAL |
| ... | @@ -886,7 +887,7 @@ | ... | @@ -886,7 +887,7 @@ |
| 886 | </div> | 887 | </div> |
| 887 | {:else if loadingComparacion} | 888 | {:else if loadingComparacion} |
| 888 | <div class="comparador-placeholder"> | 889 | <div class="comparador-placeholder"> |
| 889 | - <p>Cargando datos...</p> | 890 | + <Spinner size={48} color="var(--theme-texto, #5A5650)" /> |
| 890 | </div> | 891 | </div> |
| 891 | {:else if añosComparacion.length === 0} | 892 | {:else if añosComparacion.length === 0} |
| 892 | <div class="comparador-placeholder"> | 893 | <div class="comparador-placeholder"> | ... | ... |
-
Please register or login to post a comment