Files
wooo 1e082333af
All checks were successful
2026 World Cup Quant Platform - Production Deployment / Code Quality, Security Gate & Testing (push) Successful in 4m25s
2026 World Cup Quant Platform - Production Deployment / Deploy to Production VM via Gitea CD (push) Successful in 5m22s
fix: type missing product pages
2026-06-18 15:28:30 +08:00

33 lines
3.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from 'next/link';
const ANALYTICS_BACKEND = process.env.ANALYTICS_BACKEND_URL || 'http://127.0.0.1:8000';
type MarketBucket = { market_type: string; settled_count?: number; hit_count?: number; hit_rate_percent?: number };
type PerformanceResponse = { settled_recommendation_count?: number; hit_count?: number; miss_count?: number; hit_rate_percent?: number; summary?: string; by_market_type?: MarketBucket[]; improvement_actions?: string[] };
async function getPerformance(): Promise<PerformanceResponse> {
const response = await fetch(`${ANALYTICS_BACKEND}/analytics/recommendation-performance`, { cache: 'no-store' });
if (!response.ok) throw new Error('賽後校準 API 暫時無法回應');
return response.json() as Promise<PerformanceResponse>;
}
export default async function RecommendationPerformancePage() {
let data: PerformanceResponse | null = null;
let error = '';
try { data = await getPerformance(); } catch (err) { error = err instanceof Error ? err.message : '賽後校準暫時無法讀取'; }
const buckets = data?.by_market_type ?? [];
const actions = data?.improvement_actions ?? [];
return (
<div className="space-y-6">
<section className="rounded-[2rem] border border-[#e7c89b] bg-[#fff8e6]/95 p-6 md:p-8"><p className="dot-matrix text-sm font-bold text-[#b83822]"></p><h1 className="mt-3 text-4xl font-black text-[#3f2f25]"></h1><p className="mt-3 max-w-3xl text-sm leading-7 text-[#6f4f3c]">調</p></section>
{error ? <p className="rounded-2xl border border-[#e7a49a] bg-[#fff0e8] p-4 text-sm font-bold text-[#b83822]">{error}</p> : null}
<section className="grid gap-4 md:grid-cols-4">{[['已結算', data?.settled_recommendation_count ?? '-', '可判定推薦'], ['命中', data?.hit_count ?? '-', '命中筆數'], ['未中', data?.miss_count ?? '-', '未中筆數'], ['命中率', typeof data?.hit_rate_percent === 'number' ? `${data.hit_rate_percent.toFixed(2)}%` : '-', '近端表現']].map(([label, value, helper]) => <article key={label} className="panel-glow rounded-2xl p-5"><p className="text-xs font-semibold tracking-[0.2em] text-[#8a6b58]">{helper}</p><p className="mt-3 text-3xl font-black text-[#7d2a15]">{value}</p><p className="mt-1 text-sm text-[#6f4f3c]">{label}</p></article>)}</section>
<p className="rounded-3xl border border-[#eadcb9] bg-white/75 p-5 text-sm leading-7 text-[#6f4f3c]">{data?.summary ?? '尚無摘要。'}</p>
<section className="rounded-3xl border border-[#eadcb9] bg-white/75 p-5"><p className="dot-matrix text-sm font-bold text-[#7d2a15]"></p><div className="mt-3 grid gap-3 md:grid-cols-2 lg:grid-cols-3">{buckets.map((item) => <article key={item.market_type} className="rounded-2xl bg-[#fff8e6] p-4 text-sm text-[#7a5b46]"><b className="text-[#3f2f25]">{item.market_type}</b><br /> {item.settled_count ?? 0} {item.hit_count ?? 0} {item.hit_rate_percent ?? 0}%</article>)}</div></section>
<section className="rounded-3xl border border-[#eadcb9] bg-white/75 p-5"><p className="dot-matrix text-sm font-bold text-[#7d2a15]">調</p><ul className="mt-3 space-y-2 text-sm leading-6 text-[#7a5b46]">{actions.map((item) => <li key={item}>{item}</li>)}</ul></section>
<Link href="/proof-of-yield" className="inline-flex rounded-full bg-[#d1432d] px-5 py-3 text-sm font-black text-white"></Link>
</div>
);
}