mirror of
https://github.com/caty21/forex-dashboard.git
synced 2026-08-18 23:18:06 +00:00
feat: persistance localStorage + stale-if-error serveur
Client — lib/localCache.ts (nouveau) :
- saveCache / loadCache : wrapper localStorage préfixé "forex_v1_"
- formatCacheDate : affiche l'âge du cache (< 1h, 2h, 3j…)
Client — CurrencyCard.tsx :
- Sauvegarde chaque réponse macro réussie dans localStorage (clé macro_{currency})
- Si l'API échoue → charge la dernière valeur connue depuis localStorage
- Badge 🗄 "cache Xh" visible dans le header de la carte quand données stale
Client — page.tsx :
- Même logique pour drivers, expectations, yields
- Badge cache affiché dans le header à côté de l'heure
- Footer : Bytez → Groq
Serveur — macro/route.ts :
- stale-if-error : si tous les indicateurs reviennent null (panne API)
et qu'un cache serveur précédent existe → le renvoie avec stale:true
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
ecb815619b
commit
3c6e3bb52a
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* localCache — persistance localStorage pour les données du dashboard.
|
||||
*
|
||||
* Les données économiques (CPI, GDP, PMI…) sont publiées mensuellement.
|
||||
* Si une API devient indisponible, on affiche la dernière valeur connue
|
||||
* plutôt qu'un tiret vide.
|
||||
*
|
||||
* Clés stockées :
|
||||
* forex_v1_macro_{USD|EUR|…} — indicateurs macro (CurrencyCard)
|
||||
* forex_v1_drivers — marchés globaux (DriversBar)
|
||||
* forex_v1_expectations — attentes taux
|
||||
* forex_v1_yields — rendements obligataires
|
||||
*/
|
||||
|
||||
const PREFIX = "forex_v1_";
|
||||
|
||||
export interface CacheEntry<T> {
|
||||
data: T;
|
||||
savedAt: number; // timestamp ms
|
||||
}
|
||||
|
||||
/** Enregistre dans localStorage. Silencieux si indisponible (SSR, storage plein…) */
|
||||
export function saveCache<T>(key: string, data: T): void {
|
||||
if (typeof window === "undefined") return;
|
||||
try {
|
||||
const entry: CacheEntry<T> = { data, savedAt: Date.now() };
|
||||
localStorage.setItem(PREFIX + key, JSON.stringify(entry));
|
||||
} catch {
|
||||
// QuotaExceededError ou autre — on ignore
|
||||
}
|
||||
}
|
||||
|
||||
/** Lit depuis localStorage. Retourne null si absent ou corrompu. */
|
||||
export function loadCache<T>(key: string): CacheEntry<T> | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
try {
|
||||
const raw = localStorage.getItem(PREFIX + key);
|
||||
if (!raw) return null;
|
||||
return JSON.parse(raw) as CacheEntry<T>;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Formatte une date de sauvegarde pour l'affichage */
|
||||
export function formatCacheDate(savedAt: number): string {
|
||||
const d = new Date(savedAt);
|
||||
const now = new Date();
|
||||
const diffH = Math.round((now.getTime() - d.getTime()) / 3_600_000);
|
||||
if (diffH < 1) return "< 1h";
|
||||
if (diffH < 24) return `${diffH}h`;
|
||||
const diffD = Math.round(diffH / 24);
|
||||
return `${diffD}j`;
|
||||
}
|
||||
Reference in New Issue
Block a user