Files
wooo 61878da91d
Some checks failed
2026 World Cup Quant Platform - Production Deployment / Code Quality, Security Gate & Testing (push) Failing after 3m48s
2026 World Cup Quant Platform - Production Deployment / Deploy to Production VM via Gitea CD (push) Has been skipped
fix: restore production build dependencies
2026-06-18 12:38:06 +08:00

94 lines
4.4 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.
'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
type NavItem = {
href: string;
label: string;
shortLabel: string;
description: string;
};
const PRIMARY_NAV_ITEMS: NavItem[] = [
{ href: '/', label: '主控總覽', shortLabel: '總覽', description: '首頁先看今日推薦與資料健康' },
{ href: '/daily-card', label: '每日作戰室', shortLabel: '作戰室', description: '依日期檢查賽事與推薦' },
{ href: '/matches', label: '完整賽程', shortLabel: '賽程', description: '所有賽事、比分與狀態' },
{ href: '/deep-bet', label: '投注研究', shortLabel: '投注', description: '單關、串關與組合評估' },
{ href: '/odds', label: '盤口監控', shortLabel: '盤口', description: '賠率覆蓋與即時變化' },
{ href: '/sharp-money', label: '聰明錢追蹤', shortLabel: '聰明錢', description: '資金流向與市場偏差' },
];
const RESEARCH_NAV_ITEMS: NavItem[] = [
{ href: '/rlm', label: '反向盤口雷達', shortLabel: '反向盤', description: '少數派資金與賠率逆行' },
{ href: '/models', label: '量化模型', shortLabel: '模型', description: '泊松、EV 與風險校準' },
{ href: '/ml-edge', label: '機器學習邊緣', shortLabel: 'ML', description: '模型機率與市場機率差' },
{ href: '/match-conditions', label: '裁判/天候模型', shortLabel: '天候', description: '場地、熱度與判罰風險' },
{ href: '/props', label: '球員道具盤', shortLabel: '道具', description: '射門、傳球與球員線' },
{ href: '/kelly', label: '凱利配置', shortLabel: '凱利', description: '注碼上限與風險控管' },
{ href: '/backtesting', label: '策略回測', shortLabel: '回測', description: '歷史命中率與收益曲線' },
{ href: '/proof-of-yield', label: '公開收益帳本', shortLabel: '帳本', description: '推薦賽後校準與命中率' },
{ href: '/portfolio', label: '個人組合', shortLabel: '組合', description: '追蹤清單與部位管理' },
];
function isActivePath(pathname: string, href: string): boolean {
if (href === '/') {
return pathname === '/';
}
return pathname === href || pathname.startsWith(`${href}/`);
}
function NavPill({ item, active }: { item: NavItem; active: boolean }) {
return (
<Link
href={item.href}
aria-current={active ? 'page' : undefined}
title={item.description}
className={[
'rounded-full px-3.5 py-2 text-sm font-semibold transition focus:outline-none focus:ring-2 focus:ring-[#d6492f]/40',
active
? 'bg-[#8f2d1c] text-[#fff8e6] shadow-[0_10px_24px_rgba(143,45,28,0.28)] ring-1 ring-[#f0cfa5]'
: 'bg-[#efe1c8] text-[#563c2d] ring-1 ring-[#cda77e] hover:-translate-y-0.5 hover:bg-[#fff4df] hover:text-[#8f2d1c]',
].join(' ')}
>
<span className="hidden md:inline">{item.label}</span>
<span className="md:hidden">{item.shortLabel}</span>
</Link>
);
}
export function HeaderNav() {
const pathname = usePathname() || '/';
const allItems = [...PRIMARY_NAV_ITEMS, ...RESEARCH_NAV_ITEMS];
const activeItem = allItems.find((item) => isActivePath(pathname, item.href)) ?? PRIMARY_NAV_ITEMS[0];
return (
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
<Link href="/" className="group min-w-[220px] rounded-2xl focus:outline-none focus:ring-2 focus:ring-[#d6492f]/40">
<p className="dot-matrix text-2xl leading-tight text-[#8f2d1c] transition group-hover:text-[#b83822] md:text-3xl">
2026 <br />
</p>
<p className="mt-2 text-xs font-semibold tracking-wide text-[#b6825c]">
(UTC+8)
</p>
<p className="mt-1 text-xs text-[#7d6049]">
<span className="font-bold text-[#8f2d1c]">{activeItem.label}</span>
</p>
</Link>
<nav className="flex-1 space-y-3" aria-label="主要功能導覽">
<div className="flex flex-wrap gap-2">
{PRIMARY_NAV_ITEMS.map((item) => (
<NavPill key={item.href} item={item} active={isActivePath(pathname, item.href)} />
))}
</div>
<div className="flex flex-wrap gap-2 border-t border-[#2b3548] pt-3">
{RESEARCH_NAV_ITEMS.map((item) => (
<NavPill key={item.href} item={item} active={isActivePath(pathname, item.href)} />
))}
</div>
</nav>
</div>
);
}