fix: cache no-store sur routes API, EUR emploi, NZD OIS (RBNZ), ReportTab charts 750px

- app/api/*/route.ts : export const dynamic = force-dynamic + Cache-Control no-store
- lib/tecpi.ts : regex emploi EUR (grew/rose/fell), fallback body HTML, unite %/percent
- lib/rateprobability.ts : RBNZ_MEETINGS 2026-2027, buildRBNZPath (mirrors buildSNBPath)
  CHF/NZD skippes du loop Investing.com (SNB/RBNZ rate monitor = 404)
  Taux RBNZ 2.25% (etait 3.25%)
- components/ReportTab.tsx : hauteur charts 400 -> 750px (SPX, VIX, DXY, Gold)
- lib/tradingeconomics.ts, lib/types.ts, lib/newsfeed.ts : correctifs associes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
caty21
2026-06-29 12:17:11 +02:00
co-authored by Claude Sonnet 4.6
parent eca5800372
commit f5e5d51c72
13 changed files with 250 additions and 74 deletions
+9 -9
View File
@@ -333,9 +333,15 @@ function NewsCard({ item, secondary = false }: { item: NewsItem; secondary?: boo
return (
<div className={`rounded-xl border ${borderCls} ${bgCls} overflow-hidden ${isPriority ? "ring-1 ring-offset-0 ring-amber-500/20" : ""}`}>
<div className="p-3">
{/* Heure · Source · Catégorie · Prioritaire */}
{/* Heure · Analyse · Source · Catégorie · Prioritaire */}
<div className="flex items-center gap-2 mb-1.5 flex-wrap">
<span className="text-[9px] text-white whitespace-nowrap shrink-0">{formatRelativeTime(item.publishedAt)}</span>
{!secondary && (
<button onClick={() => setExpanded(e => !e)}
className="text-[9px] text-slate-400 hover:text-white px-1.5 py-0.5 rounded-full border border-slate-600/50 transition-colors whitespace-nowrap shrink-0">
{expanded ? "▲" : "▼ Analyse"}
</button>
)}
<span className={`text-[9px] font-semibold px-2 py-0.5 rounded-full ${SOURCE_COLORS[item.source] ?? "bg-slate-700/40 text-slate-400"}`}>
{item.source}
</span>
@@ -350,8 +356,8 @@ function NewsCard({ item, secondary = false }: { item: NewsItem; secondary?: boo
)}
</div>
{/* Titre + Analyse (bouton en fin de ligne) */}
<div className="flex items-start gap-2 mb-1.5">
{/* Titre seul */}
<div className="flex items-start gap-1 mb-1.5">
<a href={item.url} target="_blank" rel="noopener noreferrer"
className="group flex items-start gap-1 flex-1 min-w-0">
<span className={`text-[12px] leading-snug font-medium group-hover:text-white transition-colors ${secondary ? "text-slate-400" : "text-slate-200"}`}>
@@ -359,12 +365,6 @@ function NewsCard({ item, secondary = false }: { item: NewsItem; secondary?: boo
</span>
<ExternalLink size={10} className="text-slate-600 group-hover:text-slate-400 shrink-0 mt-0.5" />
</a>
{!secondary && (
<button onClick={() => setExpanded(e => !e)}
className="text-[9px] text-slate-400 hover:text-white px-1.5 py-0.5 rounded-full border border-slate-600/50 transition-colors whitespace-nowrap shrink-0 mt-0.5">
{expanded ? "▲" : "▼ Analyse"}
</button>
)}
</div>
{item.summary && !secondary && (
+6 -6
View File
@@ -7,7 +7,7 @@ import type { DriverData } from "@/lib/types";
import type { FxWeeklyEntry } from "@/app/api/fx-weekly/route";
import type { CotHistory } from "@/app/api/cot-history/route";
import { CURRENCY_META } from "@/lib/constants";
import { TvMiniChart, TvAdvancedChart } from "@/components/TvChart";
import { TvAdvancedChart } from "@/components/TvChart";
interface Props {
calEvents: CalendarEvent[];
@@ -509,12 +509,12 @@ export default function ReportTab({ calEvents, drivers, cotHistory }: Props) {
<div className="h-px flex-1 bg-sky-500/30" />
</div>
{/* Macro overview : S&P, VIX, DXY, Or */}
{/* Macro overview : S&P, VIX, DXY, Or — bougies interactives avec outils de dessin */}
<div className="grid grid-cols-2 gap-4">
<TvMiniChart symbol="SP:SPX" label="S&P 500 · Daily" dateRange="12M" height={200} />
<TvMiniChart symbol="TVC:VIX" label="VIX · Daily" dateRange="12M" height={200} />
<TvMiniChart symbol="TVC:DXY" label="DXY Dollar Index · Weekly" dateRange="60M" height={200} />
<TvMiniChart symbol="TVC:GOLD" label="Or (XAU/USD) · Weekly" dateRange="60M" height={200} />
<TvAdvancedChart symbol="SP:SPX" label="S&P 500" interval="D" height={750} />
<TvAdvancedChart symbol="TVC:VIX" label="VIX" interval="D" height={750} />
<TvAdvancedChart symbol="TVC:DXY" label="DXY Dollar Index" interval="W" height={750} />
<TvAdvancedChart symbol="TVC:GOLD" label="Or (XAU/USD)" interval="W" height={750} />
</div>
<div className="flex items-center gap-4 pt-2">
+32 -23
View File
@@ -178,6 +178,7 @@ function PairRow({ pairName, sym, base, quote, showVol }: {
// ── Main ──────────────────────────────────────────────────────────────────────
export default function SentimentPairsTab({ symbols }: Props) {
const [showVol, setShowVol] = useState(true);
const [showContrariens, setShowContrariens] = useState(false);
const symMap: Record<string, MyfxSymbol> = {};
for (const s of symbols ?? []) symMap[s.name] = s;
@@ -221,30 +222,38 @@ export default function SentimentPairsTab({ symbols }: Props) {
</label>
</div>
{/* Résumé signaux contrarien */}
{/* Résumé signaux contrariens — compact / déroulant */}
{contrarians.length > 0 && (
<div className="bg-amber-500/8 border border-amber-500/20 rounded-xl p-3">
<p className="text-[10px] font-semibold text-amber-400 uppercase tracking-wider mb-2">
{contrarians.length} signal{contrarians.length > 1 ? "s" : ""} contrarien{contrarians.length > 1 ? "s" : ""}
</p>
<div className="flex flex-wrap gap-2">
{contrarians.slice(0, 8).map(p => {
const dir = p.sym!.longPercentage >= 70 ? "short" : "long";
return (
<div key={p.std} className={`flex items-center gap-1.5 px-2.5 py-1 rounded-lg border text-[11px] font-medium ${
dir === "short"
? "bg-red-500/10 border-red-500/20 text-red-300"
: "bg-emerald-500/10 border-emerald-500/20 text-emerald-300"
}`}>
<span>{CURRENCY_META[p.base]?.flag}{CURRENCY_META[p.quote]?.flag}</span>
<span className="font-bold">{p.std}</span>
<span className="text-[10px] opacity-70">
{p.sym!.longPercentage}%L signal {dir === "short" ? "↓ SELL" : "↑ BUY"}
</span>
</div>
);
})}
</div>
<div className="border border-amber-500/20 rounded-xl overflow-hidden">
<button
onClick={() => setShowContrariens(s => !s)}
className="w-full flex items-center justify-between px-3 py-2 bg-amber-500/8 hover:bg-amber-500/12 transition-colors"
>
<span className="text-[10px] font-semibold text-amber-400 uppercase tracking-wider">
{contrarians.length} signal{contrarians.length > 1 ? "s" : ""} contrarien{contrarians.length > 1 ? "s" : ""}
</span>
<span className="text-amber-400/60 text-[10px]">{showContrariens ? "▲" : "▼"}</span>
</button>
{showContrariens && (
<div className="flex flex-wrap gap-2 px-3 py-2.5 bg-amber-500/5">
{contrarians.slice(0, 12).map(p => {
const dir = p.sym!.longPercentage >= 70 ? "short" : "long";
return (
<div key={p.std} className={`flex items-center gap-1.5 px-2.5 py-1 rounded-lg border text-[11px] font-medium ${
dir === "short"
? "bg-red-500/10 border-red-500/20 text-red-300"
: "bg-emerald-500/10 border-emerald-500/20 text-emerald-300"
}`}>
<span>{CURRENCY_META[p.base]?.flag}{CURRENCY_META[p.quote]?.flag}</span>
<span className="font-bold">{p.std}</span>
<span className="text-[10px] opacity-70">
{p.sym!.longPercentage}%L {dir === "short" ? "↓ SELL" : "↑ BUY"}
</span>
</div>
);
})}
</div>
)}
</div>
)}
+2 -2
View File
@@ -87,10 +87,10 @@ export function TvAdvancedChart({
hide_legend: false,
allow_symbol_change: false,
calendar: false,
hide_volume: true,
hide_volume: false,
isTransparent: true,
save_image: true,
drawings_access: { type: "all", tools: [{ name: "Regression Trend" }] },
drawings_access: { type: "all" },
container_id: containerId,
},
height