initial commit

This commit is contained in:
YoRHa
2026-02-04 13:39:12 +08:00
commit 199940e958
175 changed files with 19963 additions and 0 deletions

56
app/alp/page.tsx Normal file
View File

@@ -0,0 +1,56 @@
"use client";
import Sidebar from "@/components/layout/Sidebar";
import TopBar from "@/components/layout/TopBar";
import ALPStatsCards from "@/components/alp/ALPStatsCards";
import PriceHistoryCard from "@/components/alp/PriceHistoryCard";
import TradePanel from "@/components/common/TradePanel";
import LiquidityAllocationTable from "@/components/alp/LiquidityAllocationTable";
import { useApp } from "@/contexts/AppContext";
export default function ALPPage() {
const { t } = useApp();
const breadcrumbItems = [
{ label: "ASSETX", href: "/" },
{ label: t("nav.alp") },
];
return (
<div className="min-h-screen bg-white dark:bg-gray-900 flex">
<Sidebar />
<div className="flex-1 flex flex-col ml-[222px]">
<div className="bg-[#F3F4F6] dark:bg-gray-800 border-b border-border-normal dark:border-gray-700 px-8 py-3">
<TopBar breadcrumbItems={breadcrumbItems} />
</div>
<div className="flex-1 px-8 py-8 bg-[#F3F4F6] dark:bg-gray-900">
{/* Page Title and Stats Cards Section */}
<div className="bg-white dark:bg-gray-900 rounded-3xl border border-border-gray dark:border-gray-700 px-6 py-8 mb-8">
<div className="mb-6">
<h1 className="text-[32px] font-bold leading-[130%] tracking-[-0.01em] text-text-primary dark:text-white">
{t("alp.title")}
</h1>
</div>
{/* Stats Cards */}
<div>
<ALPStatsCards />
</div>
</div>
{/* Main Content - Price History and Trade Panel */}
<div className="grid grid-cols-[32fr_24fr] gap-8 mb-8">
<PriceHistoryCard />
<TradePanel />
</div>
{/* Liquidity Allocation Table */}
<div>
<LiquidityAllocationTable />
</div>
</div>
</div>
</div>
);
}

81
app/globals.css Normal file
View File

@@ -0,0 +1,81 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--background: #f9fafb;
--foreground: #111827;
--font-inter: 'Inter', sans-serif;
--font-jetbrains: 'JetBrains Mono', monospace;
/* HeroUI Button 默认颜色覆盖 - 日间模式 */
--btn-default-bg: #272E40;
--btn-default-text: #ffffff;
}
.dark {
--background: #111827;
--foreground: #f9fafb;
/* HeroUI Button 默认颜色覆盖 - 夜间模式 */
--btn-default-bg: #111827;
--btn-default-text: #ffffff;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-inter);
-webkit-font-smoothing: antialiased;
color: var(--foreground);
background: var(--background);
}
a,
button,
input,
select,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
padding: 0;
border: none;
text-decoration: none;
background: none;
}
menu,
ol,
ul {
list-style-type: none;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInCard {
from {
opacity: 0;
transform: scale(0.95) translateY(20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}

35
app/layout.tsx Normal file
View File

@@ -0,0 +1,35 @@
import type { Metadata } from "next";
import { Inter, JetBrains_Mono } from "next/font/google";
import "./globals.css";
import { Providers } from "@/components/Providers";
const inter = Inter({
subsets: ["latin"],
weight: ["400", "500", "700", "800"],
variable: "--font-inter",
});
const jetbrainsMono = JetBrains_Mono({
subsets: ["latin"],
weight: ["500", "700", "800"],
variable: "--font-jetbrains",
});
export const metadata: Metadata = {
title: "AssetX Dashboard",
description: "DeFi Asset Management Platform",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en" suppressHydrationWarning>
<body className={`${inter.variable} ${jetbrainsMono.variable} ${inter.className}`}>
<Providers>{children}</Providers>
</body>
</html>
);
}

38
app/lending/page.tsx Normal file
View File

@@ -0,0 +1,38 @@
"use client";
import Sidebar from "@/components/layout/Sidebar";
import TopBar from "@/components/layout/TopBar";
import LendingHeader from "@/components/lending/LendingHeader";
import LendingStats from "@/components/lending/LendingStats";
import LendingPlaceholder from "@/components/lending/LendingPlaceholder";
import BorrowMarket from "@/components/lending/BorrowMarket";
import { useApp } from "@/contexts/AppContext";
export default function LendingPage() {
const { t } = useApp();
const breadcrumbItems = [
{ label: "ASSETX", href: "/" },
{ label: t("nav.lending") },
];
return (
<div className="min-h-screen bg-white dark:bg-gray-900 flex">
<Sidebar />
<div className="flex-1 flex flex-col ml-[222px]">
<div className="bg-[#F3F4F6] dark:bg-gray-800 border-b border-border-normal dark:border-gray-700 px-8 py-3">
<TopBar breadcrumbItems={breadcrumbItems} />
</div>
<div className="flex-1 px-8 py-8 bg-[#F3F4F6] dark:bg-gray-900 flex flex-col gap-8">
<LendingHeader />
<div className="grid gap-8 grid-cols-[760fr_362fr]">
<LendingStats />
<LendingPlaceholder />
</div>
<BorrowMarket />
</div>
</div>
</div>
);
}

129
app/page.tsx Normal file
View File

@@ -0,0 +1,129 @@
"use client";
import { useRouter } from "next/navigation";
import { useState } from "react";
import Sidebar from "@/components/layout/Sidebar";
import TopBar from "@/components/layout/TopBar";
import PageTitle from "@/components/layout/PageTitle";
import SectionHeader from "@/components/layout/SectionHeader";
import ViewToggle from "@/components/fundmarket/ViewToggle";
import StatsCards from "@/components/fundmarket/StatsCards";
import ProductCard from "@/components/fundmarket/ProductCard";
import ProductCardList from "@/components/fundmarket/ProductCardList";
import { fundMarketStats, fundMarketProducts } from "@/data/fundMarket";
export default function Home() {
const router = useRouter();
const [viewMode, setViewMode] = useState<"grid" | "list">("grid");
const [isTransitioning, setIsTransitioning] = useState(false);
const breadcrumbItems = [
{ label: "ASSETX", href: "/" },
{ label: "Fund Market" },
];
const handleViewChange = (newMode: "grid" | "list") => {
if (newMode === viewMode) return;
setIsTransitioning(true);
setTimeout(() => {
setViewMode(newMode);
setIsTransitioning(false);
}, 200);
};
const handleInvest = (productId: number) => {
router.push(`/product/${productId}`);
};
return (
<div className="min-h-screen bg-white dark:bg-gray-900 flex">
<Sidebar />
<div className="flex-1 flex flex-col ml-[222px]">
{/* Top Bar */}
<div className="bg-gray-100 dark:bg-gray-800 border-b border-border-normal dark:border-gray-700 px-8 py-3">
<TopBar breadcrumbItems={breadcrumbItems} />
</div>
{/* Main Content */}
<div className="flex-1 px-8 py-8 bg-gray-100 dark:bg-gray-800">
{/* Page Title */}
<PageTitle title="AssetX Fund Market" />
{/* Stats Cards */}
<div className="mb-8">
<StatsCards stats={fundMarketStats} />
</div>
{/* Assets Section */}
<div className="flex flex-col gap-6">
{/* Section Header with View Toggle */}
<SectionHeader title="Assets">
<ViewToggle value={viewMode} onChange={handleViewChange} />
</SectionHeader>
{/* Product Cards - Grid or List View */}
<div
className="transition-opacity duration-200"
style={{ opacity: isTransitioning ? 0 : 1 }}
>
{viewMode === "grid" ? (
<div className="flex flex-row gap-6">
{fundMarketProducts.map((product, index) => (
<div
key={product.id}
className="flex-1 animate-fade-in"
style={{
animationDelay: `${index * 0.1}s`,
animationFillMode: "backwards",
}}
>
<ProductCard
name={product.name}
category={product.category}
categoryColor={product.categoryColor}
iconType={product.iconType}
yieldAPY={product.yieldAPY}
poolCap={product.poolCap}
maturity={product.maturity}
risk={product.risk}
riskLevel={product.riskLevel}
lockUp={product.lockUp}
circulatingSupply={product.circulatingSupply}
poolCapacityPercent={product.poolCapacityPercent}
onInvest={() => handleInvest(product.id)}
/>
</div>
))}
</div>
) : (
<div className="flex flex-col gap-4">
{fundMarketProducts.map((product, index) => (
<div
key={product.id}
className="animate-fade-in"
style={{
animationDelay: `${index * 0.08}s`,
animationFillMode: "backwards",
}}
>
<ProductCardList
name={product.name}
category={product.category}
categoryColor={product.categoryColor}
iconType={product.iconType}
poolCap={product.poolCap}
lockUp={product.lockUp}
poolCapacityPercent={product.poolCapacityPercent}
onInvest={() => handleInvest(product.id)}
/>
</div>
))}
</div>
)}
</div>
</div>
</div>
</div>
</div>
);
}

32
app/points/page.tsx Normal file
View File

@@ -0,0 +1,32 @@
"use client";
import Sidebar from "@/components/layout/Sidebar";
import TopBar from "@/components/layout/TopBar";
import PointsDashboard from "@/components/points/PointsDashboard";
import PointsCards from "@/components/points/PointsCards";
import { useApp } from "@/contexts/AppContext";
export default function PointsPage() {
const { t } = useApp();
const breadcrumbItems = [
{ label: "ASSETX", href: "/" },
{ label: t("nav.points") },
];
return (
<div className="min-h-screen bg-white dark:bg-gray-900 flex">
<Sidebar />
<div className="flex-1 flex flex-col ml-[222px]">
<div className="bg-[#F3F4F6] dark:bg-gray-800 border-b border-border-normal dark:border-gray-700 px-8 py-3">
<TopBar breadcrumbItems={breadcrumbItems} />
</div>
<div className="flex-1 px-8 py-8 bg-[#F3F4F6] dark:bg-gray-900 flex flex-col gap-6">
<PointsDashboard />
<PointsCards />
</div>
</div>
</div>
);
}

27
app/product/[id]/page.tsx Normal file
View File

@@ -0,0 +1,27 @@
import Sidebar from "@/components/layout/Sidebar";
import TopBar from "@/components/layout/TopBar";
import ContentSection from "@/components/product/ContentSection";
export default function ProductDetailPage() {
const breadcrumbItems = [
{ label: "ASSETX", href: "/" },
{ label: "Fund Market", href: "/" },
{ label: "High-Yield US Equity" },
];
return (
<div className="min-h-screen bg-bg-subtle dark:bg-gray-900 flex">
<Sidebar />
<div className="flex-1 flex flex-col ml-[222px]">
<div className="bg-[#F3F4F6] dark:bg-gray-800 border-b border-border-normal dark:border-gray-700 px-8 py-6">
<TopBar breadcrumbItems={breadcrumbItems} />
</div>
<div className="flex-1 px-8 py-8 bg-[#F3F4F6] dark:bg-gray-900">
{/* Tab Navigation and Content */}
<ContentSection />
</div>
</div>
</div>
);
}

75
app/repay/page.tsx Normal file
View File

@@ -0,0 +1,75 @@
"use client";
import { useState } from "react";
import Sidebar from "@/components/layout/Sidebar";
import TopBar from "@/components/layout/TopBar";
import RepayHeader from "@/components/lending/repay/RepayHeader";
import RepaySupplyCollateral from "@/components/lending/repay/RepaySupplyCollateral";
import RepayBorrowDebt from "@/components/lending/repay/RepayBorrowDebt";
import RepayStats from "@/components/lending/repay/RepayStats";
import RepayPoolStats from "@/components/lending/repay/RepayPoolStats";
import { useRouter } from "next/navigation";
import { useApp } from "@/contexts/AppContext";
export default function RepayPage() {
const { t } = useApp();
const router = useRouter();
const handleBackToLending = () => {
router.push("/lending");
};
const breadcrumbItems = [
{ label: "ASSETX", href: "/" },
{ label: t("nav.lending"), href: "/lending" },
{ label: t("repay.repay") },
];
return (
<div className="min-h-screen bg-white dark:bg-gray-900 flex">
<Sidebar />
<div className="flex-1 flex flex-col ml-[222px]">
<div className="bg-[#F3F4F6] dark:bg-gray-800 border-b border-border-normal dark:border-gray-700 px-8 py-3">
<TopBar breadcrumbItems={breadcrumbItems} />
</div>
<div className="flex-1 px-8 py-8 bg-[#F3F4F6] dark:bg-gray-900 flex flex-col gap-8">
{/* Back to lending link */}
<button
onClick={handleBackToLending}
className="flex items-center gap-2 self-start group"
>
<svg
width="20"
height="20"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="group-hover:translate-x-[-2px] transition-transform"
>
<path
d="M10 12L6 8L10 4"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="text-text-tertiary dark:text-gray-400"
/>
</svg>
<span className="text-body-large font-semibold text-text-tertiary dark:text-gray-400 group-hover:text-text-primary dark:group-hover:text-white transition-colors">
{t("repay.backToLending")}
</span>
</button>
<RepayHeader />
<div className="flex gap-8">
<RepaySupplyCollateral />
<RepayBorrowDebt />
<RepayStats />
</div>
<RepayPoolStats />
</div>
</div>
</div>
);
}

131
app/supply/page.tsx Normal file
View File

@@ -0,0 +1,131 @@
"use client";
import { useState } from "react";
import Sidebar from "@/components/layout/Sidebar";
import TopBar from "@/components/layout/TopBar";
import SupplyContent from "@/components/lending/supply/SupplyContent";
import SupplyPanel from "@/components/lending/supply/SupplyPanel";
import WithdrawPanel from "@/components/lending/supply/WithdrawPanel";
import { useRouter } from "next/navigation";
import { useApp } from "@/contexts/AppContext";
export default function SupplyPage() {
const { t } = useApp();
const router = useRouter();
const [activeTab, setActiveTab] = useState<"supply" | "withdraw">("supply");
const handleBackToLending = () => {
router.push("/lending");
};
const breadcrumbItems = [
{ label: "ASSETX", href: "/" },
{ label: t("nav.lending"), href: "/lending" },
{ label: t("supply.supply") },
];
return (
<div className="min-h-screen bg-white dark:bg-gray-900 flex">
<Sidebar />
<div className="flex-1 flex flex-col ml-[222px]">
<div className="bg-[#F3F4F6] dark:bg-gray-800 border-b border-border-normal dark:border-gray-700 px-8 py-3">
<TopBar breadcrumbItems={breadcrumbItems} />
</div>
<div className="flex-1 px-8 py-8 bg-[#F3F4F6] dark:bg-gray-900 flex flex-col gap-8">
{/* Back to lending link */}
<button
onClick={handleBackToLending}
className="flex items-center gap-2 self-start group"
>
<svg
width="20"
height="20"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="group-hover:translate-x-[-2px] transition-transform"
>
<path
d="M10 12L6 8L10 4"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="text-text-tertiary dark:text-gray-400"
/>
</svg>
<span className="text-body-large font-semibold text-text-tertiary dark:text-gray-400 group-hover:text-text-primary dark:group-hover:text-white transition-colors">
{t("repay.backToLending")}
</span>
</button>
<div className="flex gap-8 w-full flex-1 items-stretch">
<div className="flex-[2]">
<SupplyContent />
</div>
<div className="flex-[1] flex flex-col bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 min-w-[360px] max-w-[460px]">
{/* Supply/Withdraw Buttons */}
<div className="flex gap-0 px-4 pt-4 relative">
{/* Supply Button */}
<button
onClick={() => setActiveTab("supply")}
className={`flex items-center justify-center gap-2 py-4 flex-1 transition-all duration-300 ease-in-out relative z-10 ${
activeTab === "supply"
? ""
: "hover:bg-bg-subtle/50 dark:hover:bg-gray-700/30"
}`}
>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 3.75V14.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" className={activeTab === "supply" ? "text-text-primary dark:text-white" : "text-text-tertiary dark:text-gray-400"}/>
<path d="M14.25 9L9 14.25L3.75 9" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" className={activeTab === "supply" ? "text-text-primary dark:text-white" : "text-text-tertiary dark:text-gray-400"}/>
</svg>
<span className={`text-body-small font-bold leading-[20px] tracking-[-0.15px] transition-colors duration-300 ${
activeTab === "supply" ? "text-text-primary dark:text-white" : "text-text-tertiary dark:text-gray-400"
}`}>
{t("supply.supply")}
</span>
</button>
{/* Withdraw Button */}
<button
onClick={() => setActiveTab("withdraw")}
className={`flex items-center justify-center gap-2 py-4 flex-1 transition-all duration-300 ease-in-out relative z-10 ${
activeTab === "withdraw"
? ""
: "hover:bg-bg-subtle/50 dark:hover:bg-gray-700/30"
}`}
>
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 14.25V3.75" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" className={activeTab === "withdraw" ? "text-text-primary dark:text-white" : "text-text-tertiary dark:text-gray-400"}/>
<path d="M3.75 9L9 3.75L14.25 9" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" className={activeTab === "withdraw" ? "text-text-primary dark:text-white" : "text-text-tertiary dark:text-gray-400"}/>
</svg>
<span className={`text-body-small font-bold leading-[20px] tracking-[-0.15px] transition-colors duration-300 ${
activeTab === "withdraw" ? "text-text-primary dark:text-white" : "text-text-tertiary dark:text-gray-400"
}`}>
{t("supply.withdraw")}
</span>
</button>
{/* Sliding indicator line */}
<div
className={`absolute bottom-0 h-[3px] bg-text-primary dark:bg-white transition-all duration-300 ease-in-out ${
activeTab === "supply" ? "left-0 w-1/2" : "left-1/2 w-1/2"
}`}
/>
</div>
{/* Divider Line */}
<div className="h-px bg-border-gray dark:bg-gray-700 mx-4" />
{/* Panel Content */}
<div className="flex-1">
{activeTab === "supply" ? <SupplyPanel /> : <WithdrawPanel />}
</div>
</div>
</div>
</div>
</div>
</div>
);
}

36
app/swap/page.tsx Normal file
View File

@@ -0,0 +1,36 @@
"use client";
import Sidebar from "@/components/layout/Sidebar";
import TopBar from "@/components/layout/TopBar";
import TradePanel from "@/components/common/TradePanel";
import { useApp } from "@/contexts/AppContext";
export default function SwapPage() {
const { t } = useApp();
const breadcrumbItems = [
{ label: "ASSETX", href: "/" },
{ label: t("nav.swap") },
];
return (
<div className="min-h-screen bg-[#F3F4F6] dark:bg-gray-900 flex">
<Sidebar />
<div className="flex-1 flex flex-col ml-[222px]">
<div className="bg-[#F3F4F6] dark:bg-gray-800 border-b border-border-normal dark:border-gray-700 px-8 py-3">
<TopBar breadcrumbItems={breadcrumbItems} />
</div>
<div className="flex-1 p-8 bg-[#F3F4F6] dark:bg-gray-900">
<div className="h-full bg-white dark:bg-gray-800 rounded-3xl flex items-center justify-center">
<TradePanel
showHeader={true}
title={t("swap.title")}
subtitle={t("swap.subtitle")}
/>
</div>
</div>
</div>
</div>
);
}

53
app/transparency/page.tsx Normal file
View File

@@ -0,0 +1,53 @@
"use client";
import Sidebar from "@/components/layout/Sidebar";
import TopBar from "@/components/layout/TopBar";
import TransparencyStats from "@/components/transparency/TransparencyStats";
import HoldingsTable from "@/components/transparency/HoldingsTable";
import AssetDistribution from "@/components/transparency/AssetDistribution";
import GeographicAllocation from "@/components/transparency/GeographicAllocation";
import { useApp } from "@/contexts/AppContext";
export default function TransparencyPage() {
const { t } = useApp();
const breadcrumbItems = [
{ label: "ASSETX", href: "/" },
{ label: t("nav.transparency") },
];
return (
<div className="min-h-screen bg-white dark:bg-gray-900 flex">
<Sidebar />
<div className="flex-1 flex flex-col ml-[222px]">
<div className="bg-[#F3F4F6] dark:bg-gray-800 border-b border-border-normal dark:border-gray-700 px-8 py-3">
<TopBar breadcrumbItems={breadcrumbItems} />
</div>
<div className="flex-1 px-8 py-8 bg-[#F3F4F6] dark:bg-gray-900 flex flex-col gap-6">
{/* Header */}
<div className="flex items-center justify-between">
<h1 className="text-heading-h2 font-bold text-text-primary dark:text-white leading-[130%] tracking-[-0.01em]">
{t("transparency.title")}
</h1>
<p className="text-caption-tiny font-regular text-text-tertiary dark:text-gray-400 leading-[150%] tracking-[0.01em]">
{t("transparency.subtitle")}
</p>
</div>
{/* Stats Section */}
<TransparencyStats />
{/* Holdings Table */}
<HoldingsTable />
{/* Distribution Section */}
<div className="flex gap-8">
<AssetDistribution />
<GeographicAllocation />
</div>
</div>
</div>
</div>
);
}