feat: integrate HeroUI component library
Implemented HeroUI migration plan with the following changes: Stage 0: Environment Setup - Installed @heroui/react@2.8.7, @heroui/theme@2.4.26, framer-motion@12.29.2 - Configured Tailwind with HeroUI plugin - Added HeroUI content paths to Tailwind config Stage 1: Provider Architecture - Created Providers.tsx wrapper combining HeroUIProvider and AppProvider - Updated app/layout.tsx to use new Providers component - Preserved all AppContext functionality (theme, language, translations) Stage 2: Component Migrations - TabNavigation: Migrated to HeroUI Tabs with keyboard navigation support - TopBar: Migrated buttons to HeroUI Button components - LanguageSwitch: Migrated to HeroUI Dropdown for better UX - ThemeSwitch: Migrated to HeroUI Button (isIconOnly variant) - MintSwapPanel: Migrated to HeroUI Tabs and Button components Benefits: - Enhanced accessibility with ARIA attributes and keyboard navigation - Smooth animations and transitions via Framer Motion - Consistent component API across the application - Maintained all existing design tokens and color system - Preserved dark mode functionality Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,35 +1,35 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Inter, JetBrains_Mono } from "next/font/google";
|
import { Inter, JetBrains_Mono } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import { AppProvider } from "@/contexts/AppContext";
|
import { Providers } from "@/components/Providers";
|
||||||
|
|
||||||
const inter = Inter({
|
const inter = Inter({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
weight: ["400", "500", "700", "800"],
|
weight: ["400", "500", "700", "800"],
|
||||||
variable: "--font-inter",
|
variable: "--font-inter",
|
||||||
});
|
});
|
||||||
|
|
||||||
const jetbrainsMono = JetBrains_Mono({
|
const jetbrainsMono = JetBrains_Mono({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
weight: ["500", "700", "800"],
|
weight: ["500", "700", "800"],
|
||||||
variable: "--font-jetbrains",
|
variable: "--font-jetbrains",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "AssetX Dashboard",
|
title: "AssetX Dashboard",
|
||||||
description: "DeFi Asset Management Platform",
|
description: "DeFi Asset Management Platform",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="en" suppressHydrationWarning>
|
||||||
<body className={`${inter.variable} ${jetbrainsMono.variable} ${inter.className}`}>
|
<body className={`${inter.variable} ${jetbrainsMono.variable} ${inter.className}`}>
|
||||||
<AppProvider>{children}</AppProvider>
|
<Providers>{children}</Providers>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,101 +1,101 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
|
||||||
export default function APYHistoryCard() {
|
export default function APYHistoryCard() {
|
||||||
const { t } = useApp();
|
const { t } = useApp();
|
||||||
const [activeTab, setActiveTab] = useState<"apy" | "price">("apy");
|
const [activeTab, setActiveTab] = useState<"apy" | "price">("apy");
|
||||||
|
|
||||||
// 橙色渐变条形图数据
|
// 橙色渐变条形图数据
|
||||||
const chartData = [
|
const chartData = [
|
||||||
{ height: 85, color: "#ffe9dc" },
|
{ height: 85, color: "#ffe9dc" },
|
||||||
{ height: 93, color: "#ffc9ad" },
|
{ height: 93, color: "#ffc9ad" },
|
||||||
{ height: 100, color: "#ffc9ad" },
|
{ height: 100, color: "#ffc9ad" },
|
||||||
{ height: 105, color: "#ffba96" },
|
{ height: 105, color: "#ffba96" },
|
||||||
{ height: 108, color: "#ffa67e" },
|
{ height: 108, color: "#ffa67e" },
|
||||||
{ height: 116, color: "#f48d5f" },
|
{ height: 116, color: "#f48d5f" },
|
||||||
{ height: 124, color: "#ff6900" },
|
{ height: 124, color: "#ff6900" },
|
||||||
{ height: 127, color: "#f35b00" },
|
{ height: 127, color: "#f35b00" },
|
||||||
{ height: 139, color: "#d64700" },
|
{ height: 139, color: "#d64700" },
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 p-6 flex flex-col gap-6">
|
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 p-6 flex flex-col gap-6">
|
||||||
{/* Tabs */}
|
{/* Tabs */}
|
||||||
<div className="flex gap-6 border-b border-border-gray dark:border-gray-700">
|
<div className="flex gap-6 border-b border-border-gray dark:border-gray-700">
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab("apy")}
|
onClick={() => setActiveTab("apy")}
|
||||||
className={`pb-3 px-1 text-body-small font-bold transition-colors ${
|
className={`pb-3 px-1 text-body-small font-bold transition-colors ${
|
||||||
activeTab === "apy"
|
activeTab === "apy"
|
||||||
? "text-text-primary dark:text-white border-b-2 border-text-primary dark:border-white -mb-[1px]"
|
? "text-text-primary dark:text-white border-b-2 border-text-primary dark:border-white -mb-[1px]"
|
||||||
: "text-text-tertiary dark:text-gray-400"
|
: "text-text-tertiary dark:text-gray-400"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{t("apy.apyHistory")}
|
{t("apy.apyHistory")}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab("price")}
|
onClick={() => setActiveTab("price")}
|
||||||
className={`pb-3 px-1 text-body-small font-bold transition-colors ${
|
className={`pb-3 px-1 text-body-small font-bold transition-colors ${
|
||||||
activeTab === "price"
|
activeTab === "price"
|
||||||
? "text-text-primary dark:text-white border-b-2 border-text-primary dark:border-white -mb-[1px]"
|
? "text-text-primary dark:text-white border-b-2 border-text-primary dark:border-white -mb-[1px]"
|
||||||
: "text-text-tertiary dark:text-gray-400"
|
: "text-text-tertiary dark:text-gray-400"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{t("apy.priceHistory")}
|
{t("apy.priceHistory")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Chart Area */}
|
{/* Chart Area */}
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
||||||
{t("apy.lastDays")}
|
{t("apy.lastDays")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Orange Gradient Chart */}
|
{/* Orange Gradient Chart */}
|
||||||
<div
|
<div
|
||||||
className="flex items-end gap-4 w-full border-b border-border-gray dark:border-gray-700"
|
className="flex items-end gap-4 w-full border-b border-border-gray dark:border-gray-700"
|
||||||
style={{
|
style={{
|
||||||
height: "140px",
|
height: "140px",
|
||||||
background:
|
background:
|
||||||
"linear-gradient(0deg, rgba(255, 247, 237, 0.5) 0%, rgba(255, 247, 237, 0) 100%)",
|
"linear-gradient(0deg, rgba(255, 247, 237, 0.5) 0%, rgba(255, 247, 237, 0) 100%)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{chartData.map((bar, index) => (
|
{chartData.map((bar, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="flex-1"
|
className="flex-1"
|
||||||
style={{
|
style={{
|
||||||
height: `${bar.height}px`,
|
height: `${bar.height}px`,
|
||||||
backgroundColor: bar.color,
|
backgroundColor: bar.color,
|
||||||
borderRadius: "2px 2px 0px 0px",
|
borderRadius: "2px 2px 0px 0px",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stats */}
|
{/* Stats */}
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
||||||
{t("apy.highest")}
|
{t("apy.highest")}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||||
24.8%
|
24.8%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
||||||
{t("apy.lowest")}
|
{t("apy.lowest")}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||||
18.2%
|
18.2%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,237 +1,237 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
|
||||||
interface VerificationCardProps {
|
interface VerificationCardProps {
|
||||||
icon: string;
|
icon: string;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
buttonText: string;
|
buttonText: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function VerificationCard({ icon, title, description, buttonText }: VerificationCardProps) {
|
function VerificationCard({ icon, title, description, buttonText }: VerificationCardProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 bg-bg-surface dark:bg-gray-700 rounded-2xl border border-border-normal dark:border-gray-600 p-6 flex flex-col justify-between">
|
<div className="flex-1 bg-bg-surface dark:bg-gray-700 rounded-2xl border border-border-normal dark:border-gray-600 p-6 flex flex-col justify-between">
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-4">
|
||||||
<div className="w-5 h-5 flex-shrink-0">
|
<div className="w-5 h-5 flex-shrink-0">
|
||||||
<Image src={icon} alt={title} width={20} height={20} />
|
<Image src={icon} alt={title} width={20} height={20} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<h4 className="text-body-default font-bold text-text-primary dark:text-white">
|
<h4 className="text-body-default font-bold text-text-primary dark:text-white">
|
||||||
{title}
|
{title}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
<p className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||||
{description}
|
{description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button className="flex items-center gap-2 hover:opacity-80 transition-opacity mt-[118px]">
|
<button className="flex items-center gap-2 hover:opacity-80 transition-opacity mt-[118px]">
|
||||||
<span className="text-[10px] font-bold leading-[150%] tracking-[0.01em] text-[#9ca1af] dark:text-gray-400">
|
<span className="text-[10px] font-bold leading-[150%] tracking-[0.01em] text-[#9ca1af] dark:text-gray-400">
|
||||||
{buttonText}
|
{buttonText}
|
||||||
</span>
|
</span>
|
||||||
<Image src="/component-118.svg" alt="" width={16} height={16} />
|
<Image src="/component-118.svg" alt="" width={16} height={16} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AssetCustodyVerification() {
|
export default function AssetCustodyVerification() {
|
||||||
const { t } = useApp();
|
const { t } = useApp();
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-8 w-full">
|
<div className="flex flex-col gap-8 w-full">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h2 className="text-body-large font-bold text-text-primary dark:text-white">
|
<h2 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||||
{t("custody.title")}
|
{t("custody.title")}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-body-small font-regular text-[#9ca1af] dark:text-gray-400">
|
<p className="text-body-small font-regular text-[#9ca1af] dark:text-gray-400">
|
||||||
{t("custody.description")}
|
{t("custody.description")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Holdings Table Card */}
|
{/* Holdings Table Card */}
|
||||||
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 p-8 flex flex-col gap-6">
|
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 p-8 flex flex-col gap-6">
|
||||||
{/* Table Header */}
|
{/* Table Header */}
|
||||||
<div className="flex flex-col gap-6 pb-6 border-b border-border-gray dark:border-gray-700">
|
<div className="flex flex-col gap-6 pb-6 border-b border-border-gray dark:border-gray-700">
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<h3 className="text-body-default font-bold text-text-primary dark:text-white">
|
<h3 className="text-body-default font-bold text-text-primary dark:text-white">
|
||||||
{t("custody.underlyingHoldings")}
|
{t("custody.underlyingHoldings")}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
<p className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||||
{t("custody.verifiedBy")}
|
{t("custody.verifiedBy")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Image src="/component-115.svg" alt="" width={16} height={16} />
|
<Image src="/component-115.svg" alt="" width={16} height={16} />
|
||||||
<span className="text-caption-tiny font-medium text-[#9ca1af] dark:text-gray-400">
|
<span className="text-caption-tiny font-medium text-[#9ca1af] dark:text-gray-400">
|
||||||
{t("custody.lastUpdated")}: 2 {t("custody.minutesAgo")}
|
{t("custody.lastUpdated")}: 2 {t("custody.minutesAgo")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Table */}
|
{/* Table */}
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{/* Table Header Row */}
|
{/* Table Header Row */}
|
||||||
<div className="grid grid-cols-5 gap-4 pb-4 border-b border-border-gray dark:border-gray-700">
|
<div className="grid grid-cols-5 gap-4 pb-4 border-b border-border-gray dark:border-gray-700">
|
||||||
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
||||||
{t("custody.custodian")}
|
{t("custody.custodian")}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
||||||
{t("custody.assetType")}
|
{t("custody.assetType")}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
||||||
{t("custody.maturity")}
|
{t("custody.maturity")}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
||||||
{t("custody.valueUSD")}
|
{t("custody.valueUSD")}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
||||||
{t("custody.status")}
|
{t("custody.status")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Table Body Row */}
|
{/* Table Body Row */}
|
||||||
<div className="grid grid-cols-5 gap-4 py-6">
|
<div className="grid grid-cols-5 gap-4 py-6">
|
||||||
{/* Custodian */}
|
{/* Custodian */}
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div
|
<div
|
||||||
className="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0"
|
className="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0"
|
||||||
style={{
|
style={{
|
||||||
background: "linear-gradient(135deg, rgba(255, 137, 4, 1) 0%, rgba(245, 73, 0, 1) 100%)",
|
background: "linear-gradient(135deg, rgba(255, 137, 4, 1) 0%, rgba(245, 73, 0, 1) 100%)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="text-[13.5px] font-bold leading-[19px] text-white tracking-tight">
|
<span className="text-[13.5px] font-bold leading-[19px] text-white tracking-tight">
|
||||||
GY
|
GY
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||||
{t("custody.morganStanley")}
|
{t("custody.morganStanley")}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||||
{t("custody.primeBroker")}
|
{t("custody.primeBroker")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Asset Type */}
|
{/* Asset Type */}
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
||||||
{t("custody.usEquityPortfolio")}
|
{t("custody.usEquityPortfolio")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Maturity */}
|
{/* Maturity */}
|
||||||
<div className="flex flex-col justify-center">
|
<div className="flex flex-col justify-center">
|
||||||
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
||||||
05 Feb 2026
|
05 Feb 2026
|
||||||
</span>
|
</span>
|
||||||
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||||
(77 {t("custody.days")})
|
(77 {t("custody.days")})
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Value */}
|
{/* Value */}
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
||||||
$12,500,000.00
|
$12,500,000.00
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Status */}
|
{/* Status */}
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="rounded-full px-3 py-1.5 flex items-center gap-2 bg-[#f2fcf7] dark:bg-green-900/20">
|
<div className="rounded-full px-3 py-1.5 flex items-center gap-2 bg-[#f2fcf7] dark:bg-green-900/20">
|
||||||
<Image src="/component-116.svg" alt="" width={12} height={12} />
|
<Image src="/component-116.svg" alt="" width={12} height={12} />
|
||||||
<span className="text-[10px] font-bold leading-[150%] text-[#10b981] dark:text-green-400">
|
<span className="text-[10px] font-bold leading-[150%] text-[#10b981] dark:text-green-400">
|
||||||
{t("custody.verified")}
|
{t("custody.verified")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Table Footer Row */}
|
{/* Table Footer Row */}
|
||||||
<div className="grid grid-cols-5 gap-4 pt-6 border-t border-border-gray dark:border-gray-700">
|
<div className="grid grid-cols-5 gap-4 pt-6 border-t border-border-gray dark:border-gray-700">
|
||||||
<div className="col-span-3 flex items-center">
|
<div className="col-span-3 flex items-center">
|
||||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||||
{t("custody.totalValue")}
|
{t("custody.totalValue")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2 flex items-center">
|
<div className="col-span-2 flex items-center">
|
||||||
<span className="text-body-default font-bold text-text-primary dark:text-white">
|
<span className="text-body-default font-bold text-text-primary dark:text-white">
|
||||||
$12,500,000.00
|
$12,500,000.00
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Verification Cards Row */}
|
{/* Verification Cards Row */}
|
||||||
<div className="flex flex-row gap-6 pt-6">
|
<div className="flex flex-row gap-6 pt-6">
|
||||||
<VerificationCard
|
<VerificationCard
|
||||||
icon="/component-117.svg"
|
icon="/component-117.svg"
|
||||||
title={t("custody.smartContract")}
|
title={t("custody.smartContract")}
|
||||||
description={t("custody.smartContractDesc")}
|
description={t("custody.smartContractDesc")}
|
||||||
buttonText={t("custody.viewReports")}
|
buttonText={t("custody.viewReports")}
|
||||||
/>
|
/>
|
||||||
<VerificationCard
|
<VerificationCard
|
||||||
icon="/component-119.svg"
|
icon="/component-119.svg"
|
||||||
title={t("custody.compliance")}
|
title={t("custody.compliance")}
|
||||||
description={t("custody.complianceDesc")}
|
description={t("custody.complianceDesc")}
|
||||||
buttonText={t("custody.viewReports")}
|
buttonText={t("custody.viewReports")}
|
||||||
/>
|
/>
|
||||||
<VerificationCard
|
<VerificationCard
|
||||||
icon="/component-121.svg"
|
icon="/component-121.svg"
|
||||||
title={t("custody.proofOfReserves")}
|
title={t("custody.proofOfReserves")}
|
||||||
description={t("custody.proofDesc")}
|
description={t("custody.proofDesc")}
|
||||||
buttonText={t("custody.viewReports")}
|
buttonText={t("custody.viewReports")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Independent Verifications */}
|
{/* Independent Verifications */}
|
||||||
<div className="flex-1 rounded-2xl border bg-[#f9fafb] dark:bg-gray-700 border-[#e5e7eb] dark:border-gray-600 p-6 flex flex-col">
|
<div className="flex-1 rounded-2xl border bg-[#f9fafb] dark:bg-gray-700 border-[#e5e7eb] dark:border-gray-600 p-6 flex flex-col">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h3 className="text-body-default font-bold text-text-primary dark:text-white">
|
<h3 className="text-body-default font-bold text-text-primary dark:text-white">
|
||||||
{t("custody.independentVerifications")}
|
{t("custody.independentVerifications")}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-body-small font-regular text-[#9ca1af] dark:text-gray-400">
|
<p className="text-body-small font-regular text-[#9ca1af] dark:text-gray-400">
|
||||||
{t("custody.independentDesc")}
|
{t("custody.independentDesc")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-2 mt-6">
|
<div className="flex flex-col gap-2 mt-6">
|
||||||
<div className="rounded-2xl border bg-white dark:bg-gray-800 border-[#e5e7eb] dark:border-gray-600 p-4 flex items-center justify-between">
|
<div className="rounded-2xl border bg-white dark:bg-gray-800 border-[#e5e7eb] dark:border-gray-600 p-4 flex items-center justify-between">
|
||||||
<div className="flex flex-col gap-0.5">
|
<div className="flex flex-col gap-0.5">
|
||||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||||
{t("custody.attestationReport")}
|
{t("custody.attestationReport")}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||||
November 2025
|
November 2025
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Image src="/component-123.svg" alt="" width={24} height={24} />
|
<Image src="/component-123.svg" alt="" width={24} height={24} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-2xl border bg-white dark:bg-gray-800 border-[#e5e7eb] dark:border-gray-600 p-4 flex items-center justify-between">
|
<div className="rounded-2xl border bg-white dark:bg-gray-800 border-[#e5e7eb] dark:border-gray-600 p-4 flex items-center justify-between">
|
||||||
<div className="flex flex-col gap-0.5">
|
<div className="flex flex-col gap-0.5">
|
||||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||||
{t("custody.attestationReport")}
|
{t("custody.attestationReport")}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||||
October 2025
|
October 2025
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Image src="/component-124.svg" alt="" width={24} height={24} />
|
<Image src="/component-124.svg" alt="" width={24} height={24} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button className="flex items-center gap-2 hover:opacity-80 transition-opacity mt-4">
|
<button className="flex items-center gap-2 hover:opacity-80 transition-opacity mt-4">
|
||||||
<span className="text-[10px] font-bold leading-[150%] tracking-[0.01em] text-[#9ca1af] dark:text-gray-400">
|
<span className="text-[10px] font-bold leading-[150%] tracking-[0.01em] text-[#9ca1af] dark:text-gray-400">
|
||||||
{t("custody.viewAllArchive")}
|
{t("custody.viewAllArchive")}
|
||||||
</span>
|
</span>
|
||||||
<Image src="/component-125.svg" alt="" width={16} height={16} />
|
<Image src="/component-125.svg" alt="" width={16} height={16} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import AssetCustodyVerification from "./AssetCustodyVerification";
|
import AssetCustodyVerification from "./AssetCustodyVerification";
|
||||||
|
|
||||||
export default function AssetCustodyVerificationTab() {
|
export default function AssetCustodyVerificationTab() {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-8 w-full">
|
<div className="flex flex-col gap-8 w-full">
|
||||||
<AssetCustodyVerification />
|
<AssetCustodyVerification />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
|
||||||
export default function AssetDescriptionCard() {
|
export default function AssetDescriptionCard() {
|
||||||
const { t } = useApp();
|
const { t } = useApp();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 p-8 flex flex-col gap-4 h-[300px]">
|
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 p-8 flex flex-col gap-4 h-[300px]">
|
||||||
<h3 className="text-body-large font-bold text-text-primary dark:text-white">
|
<h3 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||||
{t("description.title")}
|
{t("description.title")}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="text-body-default font-regular text-text-primary dark:text-gray-300 leading-relaxed whitespace-pre-line flex-1 overflow-y-auto">
|
<div className="text-body-default font-regular text-text-primary dark:text-gray-300 leading-relaxed whitespace-pre-line flex-1 overflow-y-auto">
|
||||||
{t("description.content")}
|
{t("description.content")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import AssetDescriptionCard from "./AssetDescriptionCard";
|
import AssetDescriptionCard from "./AssetDescriptionCard";
|
||||||
|
|
||||||
export default function AssetDescriptionTab() {
|
export default function AssetDescriptionTab() {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-8 w-full">
|
<div className="flex flex-col gap-8 w-full">
|
||||||
<AssetDescriptionCard />
|
<AssetDescriptionCard />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,116 +1,116 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
|
||||||
interface OverviewItemProps {
|
interface OverviewItemProps {
|
||||||
icon: string;
|
icon: string;
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function OverviewItem({ icon, label, value }: OverviewItemProps) {
|
function OverviewItem({ icon, label, value }: OverviewItemProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between w-full">
|
<div className="flex items-center justify-between w-full">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<div className="w-5 h-6 flex-shrink-0">
|
<div className="w-5 h-6 flex-shrink-0">
|
||||||
<Image src={icon} alt={label} width={20} height={24} />
|
<Image src={icon} alt={label} width={20} height={24} />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-body-small font-medium text-text-tertiary dark:text-gray-400">
|
<span className="text-body-small font-medium text-text-tertiary dark:text-gray-400">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
||||||
{value}
|
{value}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AssetOverviewCard() {
|
export default function AssetOverviewCard() {
|
||||||
const { t } = useApp();
|
const { t } = useApp();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 p-8 flex flex-col gap-6">
|
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 p-8 flex flex-col gap-6">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h3 className="text-body-large font-bold text-text-primary dark:text-white">
|
<h3 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||||
{t("assetOverview.title")}
|
{t("assetOverview.title")}
|
||||||
</h3>
|
</h3>
|
||||||
<div
|
<div
|
||||||
className="rounded-full border flex items-center gap-2 px-3 py-1.5"
|
className="rounded-full border flex items-center gap-2 px-3 py-1.5"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: "#fffbf5",
|
backgroundColor: "#fffbf5",
|
||||||
borderColor: "#ffedd5",
|
borderColor: "#ffedd5",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="w-1.5 h-1.5 rounded-full flex-shrink-0"
|
className="w-1.5 h-1.5 rounded-full flex-shrink-0"
|
||||||
style={{ backgroundColor: "#ffb933" }}
|
style={{ backgroundColor: "#ffb933" }}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className="text-xs font-semibold leading-4"
|
className="text-xs font-semibold leading-4"
|
||||||
style={{ color: "#ffb933" }}
|
style={{ color: "#ffb933" }}
|
||||||
>
|
>
|
||||||
{t("assetOverview.mediumRisk")}
|
{t("assetOverview.mediumRisk")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Overview Items */}
|
{/* Overview Items */}
|
||||||
<div className="flex gap-12 w-full">
|
<div className="flex gap-12 w-full">
|
||||||
<div className="flex-1 flex flex-col gap-5">
|
<div className="flex-1 flex flex-col gap-5">
|
||||||
<OverviewItem
|
<OverviewItem
|
||||||
icon="/component-11.svg"
|
icon="/component-11.svg"
|
||||||
label={t("assetOverview.underlyingAssets")}
|
label={t("assetOverview.underlyingAssets")}
|
||||||
value={t("assetOverview.usEquityIndex")}
|
value={t("assetOverview.usEquityIndex")}
|
||||||
/>
|
/>
|
||||||
<OverviewItem
|
<OverviewItem
|
||||||
icon="/component-12.svg"
|
icon="/component-12.svg"
|
||||||
label={t("assetOverview.maturityRange")}
|
label={t("assetOverview.maturityRange")}
|
||||||
value="05 Feb 2026"
|
value="05 Feb 2026"
|
||||||
/>
|
/>
|
||||||
<OverviewItem
|
<OverviewItem
|
||||||
icon="/component-13.svg"
|
icon="/component-13.svg"
|
||||||
label={t("assetOverview.cap")}
|
label={t("assetOverview.cap")}
|
||||||
value="$50,000,000"
|
value="$50,000,000"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 flex flex-col gap-5">
|
<div className="flex-1 flex flex-col gap-5">
|
||||||
<OverviewItem
|
<OverviewItem
|
||||||
icon="/component-14.svg"
|
icon="/component-14.svg"
|
||||||
label={t("assetOverview.minInvestment")}
|
label={t("assetOverview.minInvestment")}
|
||||||
value="100 USDC"
|
value="100 USDC"
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col gap-3 w-full">
|
<div className="flex flex-col gap-3 w-full">
|
||||||
<OverviewItem
|
<OverviewItem
|
||||||
icon="/component-15.svg"
|
icon="/component-15.svg"
|
||||||
label={t("assetOverview.poolCapacity")}
|
label={t("assetOverview.poolCapacity")}
|
||||||
value="75%"
|
value="75%"
|
||||||
/>
|
/>
|
||||||
<div className="w-full h-2 bg-gray-200 dark:bg-gray-600 rounded-full overflow-hidden">
|
<div className="w-full h-2 bg-gray-200 dark:bg-gray-600 rounded-full overflow-hidden">
|
||||||
<div className="h-full bg-blue-500 rounded-full" style={{ width: "75%" }} />
|
<div className="h-full bg-blue-500 rounded-full" style={{ width: "75%" }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Divider */}
|
{/* Divider */}
|
||||||
<div className="border-t border-border-gray dark:border-gray-700" />
|
<div className="border-t border-border-gray dark:border-gray-700" />
|
||||||
|
|
||||||
{/* Current Price */}
|
{/* Current Price */}
|
||||||
<div className="bg-bg-subtle dark:bg-gray-700 rounded-xl p-4 flex items-center justify-between">
|
<div className="bg-bg-subtle dark:bg-gray-700 rounded-xl p-4 flex items-center justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Image src="/component-16.svg" alt="Price" width={24} height={24} />
|
<Image src="/component-16.svg" alt="Price" width={24} height={24} />
|
||||||
<span className="text-body-small font-medium text-text-tertiary dark:text-gray-400">
|
<span className="text-body-small font-medium text-text-tertiary dark:text-gray-400">
|
||||||
{t("assetOverview.currentPrice")}
|
{t("assetOverview.currentPrice")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-[20px] font-bold leading-[140%]">
|
<div className="text-[20px] font-bold leading-[140%]">
|
||||||
<span className="text-text-primary dark:text-white">1 GY-US = </span>
|
<span className="text-text-primary dark:text-white">1 GY-US = </span>
|
||||||
<span style={{ color: "#10b981" }}>1.04 USDC</span>
|
<span style={{ color: "#10b981" }}>1.04 USDC</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
interface BreadcrumbItem {
|
interface BreadcrumbItem {
|
||||||
label: string;
|
label: string;
|
||||||
href?: string;
|
href?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BreadcrumbProps {
|
interface BreadcrumbProps {
|
||||||
items: BreadcrumbItem[];
|
items: BreadcrumbItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Breadcrumb({ items }: BreadcrumbProps) {
|
export default function Breadcrumb({ items }: BreadcrumbProps) {
|
||||||
return (
|
return (
|
||||||
<nav className="flex items-center gap-[3px] h-5">
|
<nav className="flex items-center gap-[3px] h-5">
|
||||||
{items.map((item, index) => (
|
{items.map((item, index) => (
|
||||||
<div key={index} className="flex items-center gap-[3px]">
|
<div key={index} className="flex items-center gap-[3px]">
|
||||||
<span
|
<span
|
||||||
className={`text-sm font-medium leading-[150%] ${
|
className={`text-sm font-medium leading-[150%] ${
|
||||||
index === items.length - 1
|
index === items.length - 1
|
||||||
? "text-text-primary font-bold"
|
? "text-text-primary font-bold"
|
||||||
: "text-text-tertiary"
|
: "text-text-tertiary"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{item.label}
|
{item.label}
|
||||||
</span>
|
</span>
|
||||||
{index < items.length - 1 && (
|
{index < items.length - 1 && (
|
||||||
<Image
|
<Image
|
||||||
src="/icon-chevron-right.svg"
|
src="/icon-chevron-right.svg"
|
||||||
alt="›"
|
alt="›"
|
||||||
width={14}
|
width={14}
|
||||||
height={14}
|
height={14}
|
||||||
className="flex-shrink-0"
|
className="flex-shrink-0"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,63 +1,63 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import TabNavigation from "./TabNavigation";
|
import TabNavigation from "./TabNavigation";
|
||||||
import OverviewTab from "./OverviewTab";
|
import OverviewTab from "./OverviewTab";
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
|
||||||
export default function ContentSection() {
|
export default function ContentSection() {
|
||||||
const { t } = useApp();
|
const { t } = useApp();
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ id: "overview", label: t("tabs.overview") },
|
{ id: "overview", label: t("tabs.overview") },
|
||||||
{ id: "asset-description", label: t("tabs.assetDescription") },
|
{ id: "asset-description", label: t("tabs.assetDescription") },
|
||||||
{ id: "analytics", label: t("tabs.analytics") },
|
{ id: "analytics", label: t("tabs.analytics") },
|
||||||
{ id: "performance-analysis", label: t("tabs.performanceAnalysis") },
|
{ id: "performance-analysis", label: t("tabs.performanceAnalysis") },
|
||||||
{ id: "asset-custody", label: t("tabs.assetCustody") },
|
{ id: "asset-custody", label: t("tabs.assetCustody") },
|
||||||
];
|
];
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState("overview");
|
const [activeTab, setActiveTab] = useState("overview");
|
||||||
|
|
||||||
const handleTabChange = (tabId: string) => {
|
const handleTabChange = (tabId: string) => {
|
||||||
// If clicking asset-description, performance-analysis, or asset-custody, scroll to that section
|
// If clicking asset-description, performance-analysis, or asset-custody, scroll to that section
|
||||||
if (tabId === "asset-description" || tabId === "performance-analysis" || tabId === "asset-custody") {
|
if (tabId === "asset-description" || tabId === "performance-analysis" || tabId === "asset-custody") {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const element = document.getElementById(tabId);
|
const element = document.getElementById(tabId);
|
||||||
if (element) {
|
if (element) {
|
||||||
element.scrollIntoView({ behavior: "smooth", block: "start" });
|
element.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
// Keep active tab as overview
|
// Keep active tab as overview
|
||||||
setActiveTab("overview");
|
setActiveTab("overview");
|
||||||
} else {
|
} else {
|
||||||
setActiveTab(tabId);
|
setActiveTab(tabId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Show OverviewTab for overview, asset-description, analytics, performance-analysis, and asset-custody
|
// Show OverviewTab for overview, asset-description, analytics, performance-analysis, and asset-custody
|
||||||
const showOverview = ["overview", "asset-description", "analytics", "performance-analysis", "asset-custody"].includes(activeTab);
|
const showOverview = ["overview", "asset-description", "analytics", "performance-analysis", "asset-custody"].includes(activeTab);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 w-full">
|
<div className="flex flex-col gap-6 w-full">
|
||||||
{/* Tab Navigation */}
|
{/* Tab Navigation */}
|
||||||
<TabNavigation
|
<TabNavigation
|
||||||
tabs={tabs}
|
tabs={tabs}
|
||||||
defaultActiveId="overview"
|
defaultActiveId="overview"
|
||||||
onTabChange={handleTabChange}
|
onTabChange={handleTabChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Content Area */}
|
{/* Content Area */}
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{showOverview && <OverviewTab />}
|
{showOverview && <OverviewTab />}
|
||||||
{!showOverview && (
|
{!showOverview && (
|
||||||
<div className="bg-bg-surface rounded-2xl border border-border-normal p-6">
|
<div className="bg-bg-surface rounded-2xl border border-border-normal p-6">
|
||||||
<p className="text-text-tertiary">
|
<p className="text-text-tertiary">
|
||||||
{tabs.find((t) => t.id === activeTab)?.label} content will be
|
{tabs.find((t) => t.id === activeTab)?.label} content will be
|
||||||
displayed here...
|
displayed here...
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,45 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
import { Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Button } from "@heroui/react";
|
||||||
export default function LanguageSwitch() {
|
|
||||||
const { language, setLanguage } = useApp();
|
export default function LanguageSwitch() {
|
||||||
|
const { language, setLanguage } = useApp();
|
||||||
const toggleLanguage = () => {
|
|
||||||
setLanguage(language === "zh" ? "en" : "zh");
|
const languages = [
|
||||||
};
|
{ key: "zh", label: "中文" },
|
||||||
|
{ key: "en", label: "English" },
|
||||||
return (
|
];
|
||||||
<button
|
|
||||||
onClick={toggleLanguage}
|
const handleSelectionChange = (key: React.Key) => {
|
||||||
className="bg-bg-surface dark:bg-gray-800 rounded-lg border border-border-normal dark:border-gray-700 px-3 py-2 flex items-center justify-center h-10 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
setLanguage(key as "zh" | "en");
|
||||||
>
|
};
|
||||||
<span className="text-sm font-medium text-text-primary dark:text-white">
|
|
||||||
{language === "zh" ? "中" : "EN"}
|
return (
|
||||||
</span>
|
<Dropdown>
|
||||||
</button>
|
<DropdownTrigger>
|
||||||
);
|
<Button
|
||||||
}
|
variant="bordered"
|
||||||
|
className="bg-bg-surface dark:bg-gray-800 border-border-normal dark:border-gray-700 min-w-10 h-10 px-3 rounded-lg"
|
||||||
|
>
|
||||||
|
<span className="text-sm font-medium text-text-primary dark:text-white">
|
||||||
|
{language === "zh" ? "中" : "EN"}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</DropdownTrigger>
|
||||||
|
<DropdownMenu
|
||||||
|
aria-label="Language selection"
|
||||||
|
selectedKeys={new Set([language])}
|
||||||
|
selectionMode="single"
|
||||||
|
onSelectionChange={(keys) => {
|
||||||
|
const key = Array.from(keys)[0];
|
||||||
|
if (key) handleSelectionChange(key);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{languages.map((lang) => (
|
||||||
|
<DropdownItem key={lang.key}>{lang.label}</DropdownItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,199 +1,184 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
import { Tabs, Tab, Button } from "@heroui/react";
|
||||||
export default function MintSwapPanel() {
|
|
||||||
const { t } = useApp();
|
export default function MintSwapPanel() {
|
||||||
const [activeMode, setActiveMode] = useState<"mint" | "swap">("mint");
|
const { t } = useApp();
|
||||||
const [activeAction, setActiveAction] = useState<"deposit" | "withdraw">("deposit");
|
const [activeMode, setActiveMode] = useState<"mint" | "swap">("mint");
|
||||||
|
const [activeAction, setActiveAction] = useState<"deposit" | "withdraw">("deposit");
|
||||||
return (
|
|
||||||
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 flex flex-col">
|
return (
|
||||||
{/* Mint/Swap Tabs */}
|
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 flex flex-col overflow-hidden">
|
||||||
<div className="flex border-b border-border-gray dark:border-gray-700">
|
{/* Mint/Swap Tabs */}
|
||||||
<button
|
<Tabs
|
||||||
onClick={() => setActiveMode("mint")}
|
selectedKey={activeMode}
|
||||||
className={`flex-1 h-[53px] flex items-center justify-center text-body-small font-bold rounded-tl-3xl transition-colors ${
|
onSelectionChange={(key) => setActiveMode(key as "mint" | "swap")}
|
||||||
activeMode === "mint"
|
variant="underlined"
|
||||||
? "bg-bg-subtle dark:bg-gray-700 border-b-2 border-text-primary dark:border-blue-500 text-[#0f172b] dark:text-white"
|
classNames={{
|
||||||
: "text-text-tertiary dark:text-gray-400"
|
base: "w-full",
|
||||||
}`}
|
tabList: "w-full gap-0 p-0 rounded-none border-b border-border-gray dark:border-gray-700",
|
||||||
>
|
cursor: "bg-text-primary dark:bg-blue-500 h-[2px]",
|
||||||
{t("mintSwap.mint")}
|
tab: "h-[53px] flex-1 rounded-none data-[selected=true]:bg-bg-subtle dark:data-[selected=true]:bg-gray-700",
|
||||||
</button>
|
tabContent: "text-body-small font-bold text-text-tertiary dark:text-gray-400 group-data-[selected=true]:text-[#0f172b] dark:group-data-[selected=true]:text-white",
|
||||||
<button
|
}}
|
||||||
onClick={() => setActiveMode("swap")}
|
>
|
||||||
className={`flex-1 h-[53px] flex items-center justify-center text-body-small font-bold transition-colors ${
|
<Tab key="mint" title={t("mintSwap.mint")} />
|
||||||
activeMode === "swap"
|
<Tab key="swap" title={t("mintSwap.swap")} />
|
||||||
? "bg-bg-subtle dark:bg-gray-700 border-b-2 border-text-primary dark:border-blue-500 text-[#0f172b] dark:text-white"
|
</Tabs>
|
||||||
: "text-text-tertiary dark:text-gray-400"
|
|
||||||
}`}
|
{/* Content */}
|
||||||
>
|
<div className="flex flex-col gap-6 p-6">
|
||||||
{t("mintSwap.swap")}
|
{/* Deposit/Withdraw Toggle */}
|
||||||
</button>
|
<Tabs
|
||||||
</div>
|
selectedKey={activeAction}
|
||||||
|
onSelectionChange={(key) => setActiveAction(key as "deposit" | "withdraw")}
|
||||||
{/* Content */}
|
variant="solid"
|
||||||
<div className="flex flex-col gap-6 p-6">
|
classNames={{
|
||||||
{/* Deposit/Withdraw Toggle */}
|
base: "w-full",
|
||||||
<div className="bg-[#f9fafb] dark:bg-gray-700 rounded-xl p-1 flex gap-0">
|
tabList: "bg-[#f9fafb] dark:bg-gray-700 rounded-xl p-1 gap-0 w-full",
|
||||||
<button
|
cursor: "bg-bg-surface dark:bg-gray-600 shadow-sm",
|
||||||
onClick={() => setActiveAction("deposit")}
|
tab: "h-8 px-4",
|
||||||
className={`flex-1 h-8 px-4 rounded-lg text-body-small transition-all ${
|
tabContent: "text-body-small font-medium text-text-tertiary dark:text-gray-400 group-data-[selected=true]:font-bold group-data-[selected=true]:text-text-primary dark:group-data-[selected=true]:text-white",
|
||||||
activeAction === "deposit"
|
}}
|
||||||
? "bg-bg-surface dark:bg-gray-600 font-bold text-text-primary dark:text-white shadow-sm"
|
>
|
||||||
: "font-medium text-text-tertiary dark:text-gray-400"
|
<Tab key="deposit" title={t("mintSwap.deposit")} />
|
||||||
}`}
|
<Tab key="withdraw" title={t("mintSwap.withdraw")} />
|
||||||
>
|
</Tabs>
|
||||||
{t("mintSwap.deposit")}
|
|
||||||
</button>
|
{/* Input Area */}
|
||||||
<button
|
<div className="flex flex-col gap-2">
|
||||||
onClick={() => setActiveAction("withdraw")}
|
<div className="bg-bg-subtle dark:bg-gray-700 rounded-xl border border-border-gray dark:border-gray-600 p-4 flex flex-col gap-3">
|
||||||
className={`flex-1 h-8 px-4 rounded-lg text-body-small transition-all ${
|
{/* Label and Balance */}
|
||||||
activeAction === "withdraw"
|
<div className="flex items-center justify-between">
|
||||||
? "bg-bg-surface dark:bg-gray-600 font-bold text-text-primary dark:text-white shadow-sm"
|
<span className="text-caption-tiny font-medium text-[#4b5563] dark:text-gray-400">
|
||||||
: "font-medium text-text-tertiary dark:text-gray-400"
|
{t("mintSwap.deposit")}
|
||||||
}`}
|
</span>
|
||||||
>
|
<div className="flex items-center gap-1">
|
||||||
{t("mintSwap.withdraw")}
|
<Image src="/icon7.svg" alt="" width={12} height={12} />
|
||||||
</button>
|
<span className="text-caption-tiny font-medium text-[#4b5563] dark:text-gray-400">
|
||||||
</div>
|
{t("mintSwap.balance")}: $12,500.00
|
||||||
|
</span>
|
||||||
{/* Input Area */}
|
<button className="rounded-full px-3 h-[22px] text-[10px] font-medium bg-[#e5e7eb] dark:bg-gray-600 text-[#111827] dark:text-white">
|
||||||
<div className="flex flex-col gap-2">
|
{t("mintSwap.max")}
|
||||||
<div className="bg-bg-subtle dark:bg-gray-700 rounded-xl border border-border-gray dark:border-gray-600 p-4 flex flex-col gap-3">
|
</button>
|
||||||
{/* Label and Balance */}
|
</div>
|
||||||
<div className="flex items-center justify-between">
|
</div>
|
||||||
<span className="text-caption-tiny font-medium text-[#4b5563] dark:text-gray-400">
|
|
||||||
{t("mintSwap.deposit")}
|
{/* Input Row */}
|
||||||
</span>
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-1">
|
<button className="bg-bg-surface dark:bg-gray-600 rounded-full border border-border-normal dark:border-gray-500 px-2 h-[46px] flex items-center gap-2">
|
||||||
<Image src="/icon7.svg" alt="" width={12} height={12} />
|
<Image
|
||||||
<span className="text-caption-tiny font-medium text-[#4b5563] dark:text-gray-400">
|
src="/usd-coin-usdc-logo-10.svg"
|
||||||
{t("mintSwap.balance")}: $12,500.00
|
alt="USDC"
|
||||||
</span>
|
width={32}
|
||||||
<button className="rounded-full px-3 h-[22px] text-[10px] font-medium bg-[#e5e7eb] dark:bg-gray-600 text-[#111827] dark:text-white">
|
height={32}
|
||||||
{t("mintSwap.max")}
|
/>
|
||||||
</button>
|
<span className="text-body-default font-bold text-text-primary dark:text-white">USDC</span>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
<div className="flex flex-col items-end">
|
||||||
|
<span className="text-heading-h3 font-bold text-[#d1d5db] dark:text-gray-500">
|
||||||
{/* Input Row */}
|
0.00
|
||||||
<div className="flex items-center justify-between">
|
</span>
|
||||||
<button className="bg-bg-surface dark:bg-gray-600 rounded-full border border-border-normal dark:border-gray-500 px-2 h-[46px] flex items-center gap-2">
|
<span className="text-caption-tiny font-regular text-text-tertiary dark:text-gray-400">--</span>
|
||||||
<Image
|
</div>
|
||||||
src="/usd-coin-usdc-logo-10.svg"
|
</div>
|
||||||
alt="USDC"
|
</div>
|
||||||
width={32}
|
</div>
|
||||||
height={32}
|
|
||||||
/>
|
{/* Estimated Returns */}
|
||||||
<span className="text-body-default font-bold text-text-primary dark:text-white">USDC</span>
|
<div className="bg-bg-subtle dark:bg-gray-700 rounded-xl border border-border-gray dark:border-gray-600 p-4 flex flex-col gap-2">
|
||||||
</button>
|
<div className="flex items-center justify-between h-5">
|
||||||
<div className="flex flex-col items-end">
|
<span className="text-body-small font-bold text-[#4b5563] dark:text-gray-300">
|
||||||
<span className="text-heading-h3 font-bold text-[#d1d5db] dark:text-gray-500">
|
{t("mintSwap.estimatedReturns")}
|
||||||
0.00
|
</span>
|
||||||
</span>
|
</div>
|
||||||
<span className="text-caption-tiny font-regular text-text-tertiary dark:text-gray-400">--</span>
|
<div className="flex items-center justify-between h-5">
|
||||||
</div>
|
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||||
</div>
|
{t("mintSwap.estAPY")}
|
||||||
</div>
|
</span>
|
||||||
</div>
|
<span className="text-body-small font-bold text-[#ff6900] dark:text-orange-400">
|
||||||
|
22%
|
||||||
{/* Estimated Returns */}
|
</span>
|
||||||
<div className="bg-bg-subtle dark:bg-gray-700 rounded-xl border border-border-gray dark:border-gray-600 p-4 flex flex-col gap-2">
|
</div>
|
||||||
<div className="flex items-center justify-between h-5">
|
<div className="flex items-center justify-between h-5">
|
||||||
<span className="text-body-small font-bold text-[#4b5563] dark:text-gray-300">
|
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||||
{t("mintSwap.estimatedReturns")}
|
{t("mintSwap.estReturns")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
<span className="text-body-small font-bold text-[#10b981] dark:text-green-400">
|
||||||
<div className="flex items-center justify-between h-5">
|
~ $0.50
|
||||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
</span>
|
||||||
{t("mintSwap.estAPY")}
|
</div>
|
||||||
</span>
|
</div>
|
||||||
<span className="text-body-small font-bold text-[#ff6900] dark:text-orange-400">
|
|
||||||
22%
|
{/* Transaction Summary */}
|
||||||
</span>
|
<div className="bg-bg-subtle dark:bg-gray-700 rounded-xl border border-border-gray dark:border-gray-600 p-4 flex flex-col gap-2">
|
||||||
</div>
|
<div className="flex items-center justify-between h-5">
|
||||||
<div className="flex items-center justify-between h-5">
|
<span className="text-body-small font-bold text-[#4b5563] dark:text-gray-300">
|
||||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
{t("mintSwap.transactionSummary")}
|
||||||
{t("mintSwap.estReturns")}
|
</span>
|
||||||
</span>
|
</div>
|
||||||
<span className="text-body-small font-bold text-[#10b981] dark:text-green-400">
|
<div className="flex items-center justify-between h-5">
|
||||||
~ $0.50
|
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||||
</span>
|
{t("mintSwap.youGet")}
|
||||||
</div>
|
</span>
|
||||||
</div>
|
<span className="text-body-small font-bold text-[#10b981] dark:text-green-400">
|
||||||
|
9852.21 GYUS
|
||||||
{/* Transaction Summary */}
|
</span>
|
||||||
<div className="bg-bg-subtle dark:bg-gray-700 rounded-xl border border-border-gray dark:border-gray-600 p-4 flex flex-col gap-2">
|
</div>
|
||||||
<div className="flex items-center justify-between h-5">
|
<div className="flex items-center justify-between h-5">
|
||||||
<span className="text-body-small font-bold text-[#4b5563] dark:text-gray-300">
|
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||||
{t("mintSwap.transactionSummary")}
|
{t("mintSwap.salesPrice")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
<span className="text-body-small font-bold text-text-primary dark:text-white">$1.04 USDC</span>
|
||||||
<div className="flex items-center justify-between h-5">
|
</div>
|
||||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
<div className="flex items-center justify-between h-5">
|
||||||
{t("mintSwap.youGet")}
|
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||||
</span>
|
{t("mintSwap.fee")}
|
||||||
<span className="text-body-small font-bold text-[#10b981] dark:text-green-400">
|
</span>
|
||||||
9852.21 GYUS
|
<span className="text-body-small font-bold text-[#dc2626] dark:text-red-400">
|
||||||
</span>
|
-$50 (0.5%)
|
||||||
</div>
|
</span>
|
||||||
<div className="flex items-center justify-between h-5">
|
</div>
|
||||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
<div className="flex items-center justify-between h-5">
|
||||||
{t("mintSwap.salesPrice")}
|
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||||
</span>
|
{t("mintSwap.gas")}
|
||||||
<span className="text-body-small font-bold text-text-primary dark:text-white">$1.04 USDC</span>
|
</span>
|
||||||
</div>
|
<span className="text-body-small font-bold text-[#dc2626] dark:text-red-400">
|
||||||
<div className="flex items-center justify-between h-5">
|
-$0.09
|
||||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
</span>
|
||||||
{t("mintSwap.fee")}
|
</div>
|
||||||
</span>
|
</div>
|
||||||
<span className="text-body-small font-bold text-[#dc2626] dark:text-red-400">
|
|
||||||
-$50 (0.5%)
|
{/* Submit Button */}
|
||||||
</span>
|
<Button
|
||||||
</div>
|
isDisabled
|
||||||
<div className="flex items-center justify-between h-5">
|
className="rounded-xl h-12 bg-[#9ca1af] dark:bg-gray-600 text-lg font-bold text-white"
|
||||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
endContent={<Image src="/icon8.svg" alt="" width={20} height={20} />}
|
||||||
{t("mintSwap.gas")}
|
>
|
||||||
</span>
|
{t("mintSwap.approveDeposit")}
|
||||||
<span className="text-body-small font-bold text-[#dc2626] dark:text-red-400">
|
</Button>
|
||||||
-$0.09
|
|
||||||
</span>
|
{/* Terms */}
|
||||||
</div>
|
<div className="flex flex-col gap-0 text-center">
|
||||||
</div>
|
<div className="text-caption-tiny font-regular">
|
||||||
|
<span className="text-[#9ca1af] dark:text-gray-400">
|
||||||
{/* Submit Button */}
|
{t("mintSwap.termsText")}{" "}
|
||||||
<button
|
</span>
|
||||||
className="rounded-xl h-12 flex items-center justify-center gap-2 bg-[#9ca1af] dark:bg-gray-600"
|
<span className="text-[#10b981] dark:text-green-400">
|
||||||
disabled
|
{t("mintSwap.termsOfService")}
|
||||||
>
|
</span>
|
||||||
<span className="text-lg font-bold text-white leading-7">
|
<span className="text-[#9ca1af] dark:text-gray-400">
|
||||||
{t("mintSwap.approveDeposit")}
|
{" "}{t("mintSwap.and")}
|
||||||
</span>
|
</span>
|
||||||
<Image src="/icon8.svg" alt="" width={20} height={20} />
|
</div>
|
||||||
</button>
|
<span className="text-caption-tiny font-regular text-[#10b981] dark:text-green-400">
|
||||||
|
{t("mintSwap.privacyPolicy")}
|
||||||
{/* Terms */}
|
</span>
|
||||||
<div className="flex flex-col gap-0 text-center">
|
</div>
|
||||||
<div className="text-caption-tiny font-regular">
|
</div>
|
||||||
<span className="text-[#9ca1af] dark:text-gray-400">
|
</div>
|
||||||
{t("mintSwap.termsText")}{" "}
|
);
|
||||||
</span>
|
}
|
||||||
<span className="text-[#10b981] dark:text-green-400">
|
|
||||||
{t("mintSwap.termsOfService")}
|
|
||||||
</span>
|
|
||||||
<span className="text-[#9ca1af] dark:text-gray-400">
|
|
||||||
{" "}{t("mintSwap.and")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<span className="text-caption-tiny font-regular text-[#10b981] dark:text-green-400">
|
|
||||||
{t("mintSwap.privacyPolicy")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,53 +1,53 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
interface NavItemProps {
|
interface NavItemProps {
|
||||||
icon: string;
|
icon: string;
|
||||||
label: string;
|
label: string;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function NavItem({ icon, label, isActive, onClick }: NavItemProps) {
|
export default function NavItem({ icon, label, isActive, onClick }: NavItemProps) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={`
|
className={`
|
||||||
rounded-xl
|
rounded-xl
|
||||||
pl-4
|
pl-4
|
||||||
flex
|
flex
|
||||||
items-center
|
items-center
|
||||||
gap-2
|
gap-2
|
||||||
h-[42px]
|
h-[42px]
|
||||||
w-full
|
w-full
|
||||||
overflow-hidden
|
overflow-hidden
|
||||||
transition-colors
|
transition-colors
|
||||||
${isActive
|
${isActive
|
||||||
? 'bg-fill-secondary-click'
|
? 'bg-fill-secondary-click'
|
||||||
: 'hover:bg-gray-50'
|
: 'hover:bg-gray-50'
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<div className="w-[22px] h-[22px] flex-shrink-0 relative">
|
<div className="w-[22px] h-[22px] flex-shrink-0 relative">
|
||||||
<Image
|
<Image
|
||||||
src={icon}
|
src={icon}
|
||||||
alt={label}
|
alt={label}
|
||||||
width={22}
|
width={22}
|
||||||
height={22}
|
height={22}
|
||||||
className="w-full h-full"
|
className="w-full h-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
className={`
|
className={`
|
||||||
text-sm
|
text-sm
|
||||||
leading-[150%]
|
leading-[150%]
|
||||||
${isActive
|
${isActive
|
||||||
? 'text-text-primary font-bold'
|
? 'text-text-primary font-bold'
|
||||||
: 'text-text-tertiary font-medium'
|
: 'text-text-tertiary font-medium'
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,55 @@
|
|||||||
import ProductHeader from "./ProductHeader";
|
import ProductHeader from "./ProductHeader";
|
||||||
import StatsCards from "./StatsCards";
|
import StatsCards from "./StatsCards";
|
||||||
import AssetOverviewCard from "./AssetOverviewCard";
|
import AssetOverviewCard from "./AssetOverviewCard";
|
||||||
import APYHistoryCard from "./APYHistoryCard";
|
import APYHistoryCard from "./APYHistoryCard";
|
||||||
import AssetDescriptionCard from "./AssetDescriptionCard";
|
import AssetDescriptionCard from "./AssetDescriptionCard";
|
||||||
import MintSwapPanel from "./MintSwapPanel";
|
import MintSwapPanel from "./MintSwapPanel";
|
||||||
import ProtocolInformation from "./ProtocolInformation";
|
import ProtocolInformation from "./ProtocolInformation";
|
||||||
import PerformanceAnalysis from "./PerformanceAnalysis";
|
import PerformanceAnalysis from "./PerformanceAnalysis";
|
||||||
import Season1Rewards from "./Season1Rewards";
|
import Season1Rewards from "./Season1Rewards";
|
||||||
import AssetCustodyVerification from "./AssetCustodyVerification";
|
import AssetCustodyVerification from "./AssetCustodyVerification";
|
||||||
|
|
||||||
export default function OverviewTab() {
|
export default function OverviewTab() {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-8 w-full">
|
<div className="flex flex-col gap-8 w-full">
|
||||||
{/* Product Header */}
|
{/* Product Header */}
|
||||||
<ProductHeader />
|
<ProductHeader />
|
||||||
|
|
||||||
{/* Stats Cards */}
|
{/* Stats Cards */}
|
||||||
<StatsCards />
|
<StatsCards />
|
||||||
|
|
||||||
{/* Main Content Grid */}
|
{/* Main Content Grid */}
|
||||||
<div className="grid grid-cols-3 gap-8">
|
<div className="grid grid-cols-3 gap-8">
|
||||||
{/* Left Column - 2/3 width */}
|
{/* Left Column - 2/3 width */}
|
||||||
<div className="col-span-2 flex flex-col gap-8">
|
<div className="col-span-2 flex flex-col gap-8">
|
||||||
<AssetOverviewCard />
|
<AssetOverviewCard />
|
||||||
<APYHistoryCard />
|
<APYHistoryCard />
|
||||||
<div id="asset-description">
|
<div id="asset-description">
|
||||||
<AssetDescriptionCard />
|
<AssetDescriptionCard />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Column - 1/3 width */}
|
{/* Right Column - 1/3 width */}
|
||||||
<div className="col-span-1">
|
<div className="col-span-1">
|
||||||
<div className="sticky top-8 flex flex-col gap-8">
|
<div className="sticky top-8 flex flex-col gap-8">
|
||||||
<MintSwapPanel />
|
<MintSwapPanel />
|
||||||
<ProtocolInformation />
|
<ProtocolInformation />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Season 1 Rewards */}
|
{/* Season 1 Rewards */}
|
||||||
<Season1Rewards />
|
<Season1Rewards />
|
||||||
|
|
||||||
{/* Performance Analysis */}
|
{/* Performance Analysis */}
|
||||||
<div id="performance-analysis">
|
<div id="performance-analysis">
|
||||||
<PerformanceAnalysis />
|
<PerformanceAnalysis />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Asset Custody & Verification */}
|
{/* Asset Custody & Verification */}
|
||||||
<div id="asset-custody">
|
<div id="asset-custody">
|
||||||
<AssetCustodyVerification />
|
<AssetCustodyVerification />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,207 +1,207 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
|
||||||
interface CalendarDayProps {
|
interface CalendarDayProps {
|
||||||
day: number | null;
|
day: number | null;
|
||||||
value: string;
|
value: string;
|
||||||
type: "positive" | "negative" | "neutral" | "current";
|
type: "positive" | "negative" | "neutral" | "current";
|
||||||
}
|
}
|
||||||
|
|
||||||
function CalendarDay({ day, value, type }: CalendarDayProps) {
|
function CalendarDay({ day, value, type }: CalendarDayProps) {
|
||||||
// Empty cell
|
// Empty cell
|
||||||
if (day === null) {
|
if (day === null) {
|
||||||
return <div className="flex-1" />;
|
return <div className="flex-1" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const typeStyles = {
|
const typeStyles = {
|
||||||
positive: "bg-[#f2fcf7] dark:bg-green-900/20 border-[#cef3e0] dark:border-green-700/30",
|
positive: "bg-[#f2fcf7] dark:bg-green-900/20 border-[#cef3e0] dark:border-green-700/30",
|
||||||
negative: "bg-[#fff8f7] dark:bg-red-900/20 border-[#ffdbd5] dark:border-red-700/30",
|
negative: "bg-[#fff8f7] dark:bg-red-900/20 border-[#ffdbd5] dark:border-red-700/30",
|
||||||
neutral: "bg-[#f9fafb] dark:bg-gray-700 border-[#f3f4f6] dark:border-gray-600",
|
neutral: "bg-[#f9fafb] dark:bg-gray-700 border-[#f3f4f6] dark:border-gray-600",
|
||||||
current: "bg-[#111827] dark:bg-blue-600 border-[#111827] dark:border-blue-600",
|
current: "bg-[#111827] dark:bg-blue-600 border-[#111827] dark:border-blue-600",
|
||||||
};
|
};
|
||||||
|
|
||||||
const isCurrent = type === "current";
|
const isCurrent = type === "current";
|
||||||
const dayTextStyle = isCurrent
|
const dayTextStyle = isCurrent
|
||||||
? "text-[#fcfcfd]"
|
? "text-[#fcfcfd]"
|
||||||
: "text-[#9ca1af] dark:text-gray-400";
|
: "text-[#9ca1af] dark:text-gray-400";
|
||||||
|
|
||||||
// Value text color should match the type
|
// Value text color should match the type
|
||||||
let valueTextStyle = "";
|
let valueTextStyle = "";
|
||||||
if (isCurrent) {
|
if (isCurrent) {
|
||||||
valueTextStyle = "text-[#fcfcfd]";
|
valueTextStyle = "text-[#fcfcfd]";
|
||||||
} else if (type === "positive") {
|
} else if (type === "positive") {
|
||||||
valueTextStyle = "text-[#10b981] dark:text-green-400";
|
valueTextStyle = "text-[#10b981] dark:text-green-400";
|
||||||
} else if (type === "negative") {
|
} else if (type === "negative") {
|
||||||
valueTextStyle = "text-[#dc2626] dark:text-red-400";
|
valueTextStyle = "text-[#dc2626] dark:text-red-400";
|
||||||
} else {
|
} else {
|
||||||
valueTextStyle = "text-[#9ca1af] dark:text-gray-400";
|
valueTextStyle = "text-[#9ca1af] dark:text-gray-400";
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`rounded border flex flex-col items-center justify-center flex-1 p-3 gap-6 ${typeStyles[type]}`}
|
className={`rounded border flex flex-col items-center justify-center flex-1 p-3 gap-6 ${typeStyles[type]}`}
|
||||||
>
|
>
|
||||||
<div className="w-full flex items-start">
|
<div className="w-full flex items-start">
|
||||||
<span className={`text-[10px] font-bold leading-[150%] ${dayTextStyle}`}>
|
<span className={`text-[10px] font-bold leading-[150%] ${dayTextStyle}`}>
|
||||||
{day}
|
{day}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex items-end justify-end">
|
<div className="w-full flex items-end justify-end">
|
||||||
<span className={`text-body-small font-bold leading-[150%] ${valueTextStyle}`}>
|
<span className={`text-body-small font-bold leading-[150%] ${valueTextStyle}`}>
|
||||||
{value}
|
{value}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StatCardProps {
|
interface StatCardProps {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function StatCard({ label, value }: StatCardProps) {
|
function StatCard({ label, value }: StatCardProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center gap-1">
|
<div className="flex flex-col items-center gap-1">
|
||||||
<span className="text-[10px] font-bold leading-[150%] tracking-[0.01em] text-[#9ca1af] dark:text-gray-400">
|
<span className="text-[10px] font-bold leading-[150%] tracking-[0.01em] text-[#9ca1af] dark:text-gray-400">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-body-large font-bold text-[#10b981] dark:text-green-400">
|
<span className="text-body-large font-bold text-[#10b981] dark:text-green-400">
|
||||||
{value}
|
{value}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PerformanceAnalysis() {
|
export default function PerformanceAnalysis() {
|
||||||
const { t } = useApp();
|
const { t } = useApp();
|
||||||
const [currentMonth] = useState("November 2025");
|
const [currentMonth] = useState("November 2025");
|
||||||
|
|
||||||
// 模拟日历数据 - 5周数据
|
// 模拟日历数据 - 5周数据
|
||||||
const weekData = [
|
const weekData = [
|
||||||
[
|
[
|
||||||
{ day: 31, value: "0.00%", type: "neutral" as const },
|
{ day: 31, value: "0.00%", type: "neutral" as const },
|
||||||
{ day: 1, value: "+0.12%", type: "positive" as const },
|
{ day: 1, value: "+0.12%", type: "positive" as const },
|
||||||
{ day: 2, value: "+0.08%", type: "positive" as const },
|
{ day: 2, value: "+0.08%", type: "positive" as const },
|
||||||
{ day: 3, value: "-0.03%", type: "negative" as const },
|
{ day: 3, value: "-0.03%", type: "negative" as const },
|
||||||
{ day: 4, value: "+0.15%", type: "positive" as const },
|
{ day: 4, value: "+0.15%", type: "positive" as const },
|
||||||
{ day: 5, value: "+0.21%", type: "positive" as const },
|
{ day: 5, value: "+0.21%", type: "positive" as const },
|
||||||
{ day: 6, value: "0.00%", type: "neutral" as const },
|
{ day: 6, value: "0.00%", type: "neutral" as const },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{ day: 7, value: "+0.12%", type: "positive" as const },
|
{ day: 7, value: "+0.12%", type: "positive" as const },
|
||||||
{ day: 8, value: "+0.12%", type: "positive" as const },
|
{ day: 8, value: "+0.12%", type: "positive" as const },
|
||||||
{ day: 9, value: "-0.03%", type: "negative" as const },
|
{ day: 9, value: "-0.03%", type: "negative" as const },
|
||||||
{ day: 10, value: "+0.08%", type: "positive" as const },
|
{ day: 10, value: "+0.08%", type: "positive" as const },
|
||||||
{ day: 11, value: "-0.03%", type: "negative" as const },
|
{ day: 11, value: "-0.03%", type: "negative" as const },
|
||||||
{ day: 12, value: "+0.21%", type: "positive" as const },
|
{ day: 12, value: "+0.21%", type: "positive" as const },
|
||||||
{ day: 13, value: "0.00%", type: "neutral" as const },
|
{ day: 13, value: "0.00%", type: "neutral" as const },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{ day: 14, value: "-0.03%", type: "negative" as const },
|
{ day: 14, value: "-0.03%", type: "negative" as const },
|
||||||
{ day: 15, value: "-0.03%", type: "negative" as const },
|
{ day: 15, value: "-0.03%", type: "negative" as const },
|
||||||
{ day: 16, value: "+0.15%", type: "positive" as const },
|
{ day: 16, value: "+0.15%", type: "positive" as const },
|
||||||
{ day: 17, value: "+0.21%", type: "positive" as const },
|
{ day: 17, value: "+0.21%", type: "positive" as const },
|
||||||
{ day: 18, value: "+0.08%", type: "positive" as const },
|
{ day: 18, value: "+0.08%", type: "positive" as const },
|
||||||
{ day: 19, value: "0.00%", type: "neutral" as const },
|
{ day: 19, value: "0.00%", type: "neutral" as const },
|
||||||
{ day: 20, value: "+0.12%", type: "positive" as const },
|
{ day: 20, value: "+0.12%", type: "positive" as const },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{ day: 21, value: "+0.08%", type: "positive" as const },
|
{ day: 21, value: "+0.08%", type: "positive" as const },
|
||||||
{ day: 22, value: "+0.15%", type: "positive" as const },
|
{ day: 22, value: "+0.15%", type: "positive" as const },
|
||||||
{ day: 23, value: "-0.03%", type: "negative" as const },
|
{ day: 23, value: "-0.03%", type: "negative" as const },
|
||||||
{ day: 24, value: "+0.12%", type: "current" as const },
|
{ day: 24, value: "+0.12%", type: "current" as const },
|
||||||
{ day: 25, value: "0.00%", type: "neutral" as const },
|
{ day: 25, value: "0.00%", type: "neutral" as const },
|
||||||
{ day: 26, value: "+0.21%", type: "positive" as const },
|
{ day: 26, value: "+0.21%", type: "positive" as const },
|
||||||
{ day: 27, value: "+0.08%", type: "positive" as const },
|
{ day: 27, value: "+0.08%", type: "positive" as const },
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{ day: 28, value: "+0.12%", type: "positive" as const },
|
{ day: 28, value: "+0.12%", type: "positive" as const },
|
||||||
{ day: 30, value: "-0.03%", type: "negative" as const },
|
{ day: 30, value: "-0.03%", type: "negative" as const },
|
||||||
{ day: 29, value: "-0.03%", type: "negative" as const },
|
{ day: 29, value: "-0.03%", type: "negative" as const },
|
||||||
{ day: null, value: "", type: "neutral" as const },
|
{ day: null, value: "", type: "neutral" as const },
|
||||||
{ day: null, value: "", type: "neutral" as const },
|
{ day: null, value: "", type: "neutral" as const },
|
||||||
{ day: null, value: "", type: "neutral" as const },
|
{ day: null, value: "", type: "neutral" as const },
|
||||||
{ day: null, value: "", type: "neutral" as const },
|
{ day: null, value: "", type: "neutral" as const },
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 p-8 flex flex-col gap-8">
|
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 p-8 flex flex-col gap-8">
|
||||||
{/* Top Section - Title and Stats */}
|
{/* Top Section - Title and Stats */}
|
||||||
<div className="flex items-start justify-between pb-8 border-b border-border-gray dark:border-gray-700">
|
<div className="flex items-start justify-between pb-8 border-b border-border-gray dark:border-gray-700">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h2 className="text-body-large font-bold text-text-primary dark:text-white">
|
<h2 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||||
{t("performance.title")}
|
{t("performance.title")}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-body-small font-regular text-[#9ca1af] dark:text-gray-400">
|
<p className="text-body-small font-regular text-[#9ca1af] dark:text-gray-400">
|
||||||
{t("performance.description")}
|
{t("performance.description")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-8">
|
<div className="flex items-center gap-8">
|
||||||
<StatCard label={t("performance.ytd")} value="+8.7%" />
|
<StatCard label={t("performance.ytd")} value="+8.7%" />
|
||||||
<StatCard label={t("performance.ytd")} value="+8.7%" />
|
<StatCard label={t("performance.ytd")} value="+8.7%" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Calendar Section */}
|
{/* Calendar Section */}
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
{/* Calendar Header */}
|
{/* Calendar Header */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-6 h-6">
|
<div className="w-6 h-6">
|
||||||
<Image src="/component-114.svg" alt="" width={24} height={24} />
|
<Image src="/component-114.svg" alt="" width={24} height={24} />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-body-small font-bold text-text-primary dark:text-white">
|
<h3 className="text-body-small font-bold text-text-primary dark:text-white">
|
||||||
{t("performance.dailyNetReturns")}
|
{t("performance.dailyNetReturns")}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button className="w-6 h-6 rounded-lg flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
|
<button className="w-6 h-6 rounded-lg flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
|
||||||
<Image src="/icon9.svg" alt="Previous" width={16} height={16} />
|
<Image src="/icon9.svg" alt="Previous" width={16} height={16} />
|
||||||
</button>
|
</button>
|
||||||
<span className="text-body-small font-bold text-[#0a0a0a] dark:text-white tracking-tight">
|
<span className="text-body-small font-bold text-[#0a0a0a] dark:text-white tracking-tight">
|
||||||
{currentMonth}
|
{currentMonth}
|
||||||
</span>
|
</span>
|
||||||
<button className="w-6 h-6 rounded-lg flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
|
<button className="w-6 h-6 rounded-lg flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors">
|
||||||
<Image src="/icon10.svg" alt="Next" width={16} height={16} />
|
<Image src="/icon10.svg" alt="Next" width={16} height={16} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Calendar */}
|
{/* Calendar */}
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
{/* Weekday Headers */}
|
{/* Weekday Headers */}
|
||||||
<div className="grid grid-cols-7 gap-2">
|
<div className="grid grid-cols-7 gap-2">
|
||||||
{["sun", "mon", "tue", "wed", "thu", "fri", "sat"].map((day) => (
|
{["sun", "mon", "tue", "wed", "thu", "fri", "sat"].map((day) => (
|
||||||
<div key={day} className="flex items-center justify-center">
|
<div key={day} className="flex items-center justify-center">
|
||||||
<span className="text-[10px] font-bold leading-[150%] text-[#94a3b8] dark:text-gray-400">
|
<span className="text-[10px] font-bold leading-[150%] text-[#94a3b8] dark:text-gray-400">
|
||||||
{t(`performance.weekdays.${day}`)}
|
{t(`performance.weekdays.${day}`)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Calendar Grid */}
|
{/* Calendar Grid */}
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
{weekData.map((week, weekIndex) => (
|
{weekData.map((week, weekIndex) => (
|
||||||
<div key={weekIndex} className="grid grid-cols-7 gap-2">
|
<div key={weekIndex} className="grid grid-cols-7 gap-2">
|
||||||
{week.map((day, dayIndex) => (
|
{week.map((day, dayIndex) => (
|
||||||
<CalendarDay
|
<CalendarDay
|
||||||
key={`${weekIndex}-${dayIndex}`}
|
key={`${weekIndex}-${dayIndex}`}
|
||||||
day={day.day}
|
day={day.day}
|
||||||
value={day.value}
|
value={day.value}
|
||||||
type={day.type}
|
type={day.type}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import PerformanceAnalysis from "./PerformanceAnalysis";
|
import PerformanceAnalysis from "./PerformanceAnalysis";
|
||||||
|
|
||||||
export default function PerformanceAnalysisTab() {
|
export default function PerformanceAnalysisTab() {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-8 w-full">
|
<div className="flex flex-col gap-8 w-full">
|
||||||
<PerformanceAnalysis />
|
<PerformanceAnalysis />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
|
||||||
export default function ProductHeader() {
|
export default function ProductHeader() {
|
||||||
const { t } = useApp();
|
const { t } = useApp();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
{/* Product Title Section */}
|
{/* Product Title Section */}
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div className="flex gap-6">
|
<div className="flex gap-6">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<Image src="/lr0.svg" alt="Product Logo" width={80} height={80} />
|
<Image src="/lr0.svg" alt="Product Logo" width={80} height={80} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h1 className="text-heading-h2 font-bold text-text-primary dark:text-white">
|
<h1 className="text-heading-h2 font-bold text-text-primary dark:text-white">
|
||||||
{t("product.gyUsEquityIndexToken")}
|
{t("product.gyUsEquityIndexToken")}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-body-default font-regular text-text-tertiary dark:text-gray-400">
|
<p className="text-body-default font-regular text-text-tertiary dark:text-gray-400">
|
||||||
High-Yield US Equity Quantitative Strategy - Institutional Grade RWA
|
High-Yield US Equity Quantitative Strategy - Institutional Grade RWA
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 px-4 py-2 bg-bg-subtle dark:bg-gray-700 rounded-lg border border-border-gray dark:border-gray-600">
|
<div className="flex items-center gap-2 px-4 py-2 bg-bg-subtle dark:bg-gray-700 rounded-lg border border-border-gray dark:border-gray-600">
|
||||||
<Image src="/group-9270.svg" alt="Contract" width={16} height={16} />
|
<Image src="/group-9270.svg" alt="Contract" width={16} height={16} />
|
||||||
<span className="text-caption-tiny font-medium font-jetbrains text-text-tertiary dark:text-gray-400">
|
<span className="text-caption-tiny font-medium font-jetbrains text-text-tertiary dark:text-gray-400">
|
||||||
{t("product.contractAddress")}: 0x1b19...4f2c
|
{t("product.contractAddress")}: 0x1b19...4f2c
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +1,53 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
|
||||||
interface ProtocolLinkProps {
|
interface ProtocolLinkProps {
|
||||||
icon: string;
|
icon: string;
|
||||||
label: string;
|
label: string;
|
||||||
arrowIcon: string;
|
arrowIcon: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ProtocolLink({ icon, label, arrowIcon }: ProtocolLinkProps) {
|
function ProtocolLink({ icon, label, arrowIcon }: ProtocolLinkProps) {
|
||||||
return (
|
return (
|
||||||
<button className="bg-bg-subtle dark:bg-gray-700 rounded-xl border border-border-gray dark:border-gray-600 px-4 py-3.5 flex items-center justify-between w-full hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors">
|
<button className="bg-bg-subtle dark:bg-gray-700 rounded-xl border border-border-gray dark:border-gray-600 px-4 py-3.5 flex items-center justify-between w-full hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Image src={icon} alt="" width={20} height={24} />
|
<Image src={icon} alt="" width={20} height={24} />
|
||||||
<span className="text-body-small font-medium text-text-tertiary dark:text-gray-300">
|
<span className="text-body-small font-medium text-text-tertiary dark:text-gray-300">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Image src={arrowIcon} alt="" width={20} height={24} />
|
<Image src={arrowIcon} alt="" width={20} height={24} />
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ProtocolInformation() {
|
export default function ProtocolInformation() {
|
||||||
const { t } = useApp();
|
const { t } = useApp();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 px-6 py-8 flex flex-col gap-4">
|
<div className="bg-bg-surface dark:bg-gray-800 rounded-3xl border border-border-gray dark:border-gray-700 px-6 py-8 flex flex-col gap-4">
|
||||||
<h3 className="text-body-large font-bold text-text-primary dark:text-white">
|
<h3 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||||
{t("protocol.title")}
|
{t("protocol.title")}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<ProtocolLink
|
<ProtocolLink
|
||||||
icon="/component-17.svg"
|
icon="/component-17.svg"
|
||||||
label={t("protocol.whitepaper")}
|
label={t("protocol.whitepaper")}
|
||||||
arrowIcon="/component-18.svg"
|
arrowIcon="/component-18.svg"
|
||||||
/>
|
/>
|
||||||
<ProtocolLink
|
<ProtocolLink
|
||||||
icon="/component-19.svg"
|
icon="/component-19.svg"
|
||||||
label={t("protocol.documentation")}
|
label={t("protocol.documentation")}
|
||||||
arrowIcon="/component-110.svg"
|
arrowIcon="/component-110.svg"
|
||||||
/>
|
/>
|
||||||
<ProtocolLink
|
<ProtocolLink
|
||||||
icon="/component-111.svg"
|
icon="/component-111.svg"
|
||||||
label={t("protocol.github")}
|
label={t("protocol.github")}
|
||||||
arrowIcon="/component-112.svg"
|
arrowIcon="/component-112.svg"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
13
components/Providers.tsx
Normal file
13
components/Providers.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { HeroUIProvider } from "@heroui/react";
|
||||||
|
import { AppProvider } from "@/contexts/AppContext";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
export function Providers({ children }: { children: ReactNode }) {
|
||||||
|
return (
|
||||||
|
<HeroUIProvider>
|
||||||
|
<AppProvider>{children}</AppProvider>
|
||||||
|
</HeroUIProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,101 +1,101 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
|
||||||
interface RewardStatProps {
|
interface RewardStatProps {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function RewardStat({ label, value }: RewardStatProps) {
|
function RewardStat({ label, value }: RewardStatProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center gap-1.5">
|
<div className="flex flex-col items-center gap-1.5">
|
||||||
<span
|
<span
|
||||||
className="text-[24px] font-bold leading-[130%] dark:text-white"
|
className="text-[24px] font-bold leading-[130%] dark:text-white"
|
||||||
style={{ color: "#111827", letterSpacing: "-0.005em" }}
|
style={{ color: "#111827", letterSpacing: "-0.005em" }}
|
||||||
>
|
>
|
||||||
{value}
|
{value}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className="text-[10px] font-bold uppercase leading-[150%] text-center dark:text-gray-400"
|
className="text-[10px] font-bold uppercase leading-[150%] text-center dark:text-gray-400"
|
||||||
style={{ color: "#9ca1af", letterSpacing: "0.05em" }}
|
style={{ color: "#9ca1af", letterSpacing: "0.05em" }}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Season1Rewards() {
|
export default function Season1Rewards() {
|
||||||
const { t } = useApp();
|
const { t } = useApp();
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="rounded-3xl border flex flex-col relative overflow-hidden"
|
className="rounded-3xl border flex flex-col relative overflow-hidden"
|
||||||
style={{
|
style={{
|
||||||
background:
|
background:
|
||||||
"radial-gradient(50% 50% at 100% 0%, rgba(255, 217, 100, 0.05) 0%, rgba(16, 185, 129, 0.05) 100%), #ffffff",
|
"radial-gradient(50% 50% at 100% 0%, rgba(255, 217, 100, 0.05) 0%, rgba(16, 185, 129, 0.05) 100%), #ffffff",
|
||||||
borderColor: "rgba(255, 255, 255, 0.6)",
|
borderColor: "rgba(255, 255, 255, 0.6)",
|
||||||
paddingTop: "20px",
|
paddingTop: "20px",
|
||||||
paddingBottom: "20px",
|
paddingBottom: "20px",
|
||||||
paddingLeft: "24px",
|
paddingLeft: "24px",
|
||||||
paddingRight: "24px",
|
paddingRight: "24px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Background Decoration */}
|
{/* Background Decoration */}
|
||||||
<div
|
<div
|
||||||
className="absolute"
|
className="absolute"
|
||||||
style={{ opacity: 0.5, right: "-15px", bottom: "-20px" }}
|
style={{ opacity: 0.5, right: "-15px", bottom: "-20px" }}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src="/component-113.svg"
|
src="/component-113.svg"
|
||||||
alt=""
|
alt=""
|
||||||
width={120}
|
width={120}
|
||||||
height={144}
|
height={144}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content Container */}
|
{/* Content Container */}
|
||||||
<div className="flex flex-row items-center relative z-10" style={{ gap: "400px" }}>
|
<div className="flex flex-row items-center relative z-10" style={{ gap: "400px" }}>
|
||||||
{/* Left: Header and Description */}
|
{/* Left: Header and Description */}
|
||||||
<div className="flex flex-col gap-2 flex-shrink-0">
|
<div className="flex flex-col gap-2 flex-shrink-0">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<h3
|
<h3
|
||||||
className="text-[20px] font-bold leading-[140%] dark:text-white"
|
className="text-[20px] font-bold leading-[140%] dark:text-white"
|
||||||
style={{ color: "#111827" }}
|
style={{ color: "#111827" }}
|
||||||
>
|
>
|
||||||
{t("rewards.season1")}
|
{t("rewards.season1")}
|
||||||
</h3>
|
</h3>
|
||||||
<div
|
<div
|
||||||
className="rounded-full px-2.5 py-1 flex items-center"
|
className="rounded-full px-2.5 py-1 flex items-center"
|
||||||
style={{ backgroundColor: "#111827" }}
|
style={{ backgroundColor: "#111827" }}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="text-[10px] font-bold leading-4"
|
className="text-[10px] font-bold leading-4"
|
||||||
style={{ color: "#fcfcfd" }}
|
style={{ color: "#fcfcfd" }}
|
||||||
>
|
>
|
||||||
{t("rewards.live")}
|
{t("rewards.live")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Description */}
|
{/* Description */}
|
||||||
<p
|
<p
|
||||||
className="text-body-small font-medium dark:text-gray-400"
|
className="text-body-small font-medium dark:text-gray-400"
|
||||||
style={{ color: "#9ca1af" }}
|
style={{ color: "#9ca1af" }}
|
||||||
>
|
>
|
||||||
{t("rewards.earnPoints")}
|
{t("rewards.earnPoints")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right: Stats */}
|
{/* Right: Stats */}
|
||||||
<div className="flex items-center justify-between flex-1">
|
<div className="flex items-center justify-between flex-1">
|
||||||
<RewardStat label={t("rewards.yourPoints")} value="-" />
|
<RewardStat label={t("rewards.yourPoints")} value="-" />
|
||||||
<RewardStat label={t("rewards.badgeBoost")} value="-" />
|
<RewardStat label={t("rewards.badgeBoost")} value="-" />
|
||||||
<RewardStat label={t("rewards.referrals")} value="-" />
|
<RewardStat label={t("rewards.referrals")} value="-" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,80 +1,80 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import NavItem from "./NavItem";
|
import NavItem from "./NavItem";
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
|
||||||
export default function Sidebar() {
|
export default function Sidebar() {
|
||||||
const { t } = useApp();
|
const { t } = useApp();
|
||||||
const [activeItem, setActiveItem] = useState("Assets");
|
const [activeItem, setActiveItem] = useState("Assets");
|
||||||
|
|
||||||
const navigationItems = [
|
const navigationItems = [
|
||||||
{ icon: "/icon-assets.svg", label: t("nav.assets"), key: "Assets", path: "/" },
|
{ icon: "/icon-assets.svg", label: t("nav.assets"), key: "Assets", path: "/" },
|
||||||
{ icon: "/icon-alp.svg", label: t("nav.alp"), key: "ALP", path: "/alp" },
|
{ icon: "/icon-alp.svg", label: t("nav.alp"), key: "ALP", path: "/alp" },
|
||||||
{ icon: "/icon-swap.svg", label: t("nav.swap"), key: "Swap", path: "/swap" },
|
{ icon: "/icon-swap.svg", label: t("nav.swap"), key: "Swap", path: "/swap" },
|
||||||
{ icon: "/icon-lending.svg", label: t("nav.lending"), key: "Lending", path: "/lending" },
|
{ icon: "/icon-lending.svg", label: t("nav.lending"), key: "Lending", path: "/lending" },
|
||||||
{ icon: "/icon-transparency.svg", label: t("nav.transparency"), key: "Transparency", path: "/transparency" },
|
{ icon: "/icon-transparency.svg", label: t("nav.transparency"), key: "Transparency", path: "/transparency" },
|
||||||
{ icon: "/icon-ecosystem.svg", label: t("nav.ecosystem"), key: "Ecosystem", path: "/ecosystem" },
|
{ icon: "/icon-ecosystem.svg", label: t("nav.ecosystem"), key: "Ecosystem", path: "/ecosystem" },
|
||||||
{ icon: "/icon-points.svg", label: t("nav.points"), key: "Points", path: "/points" },
|
{ icon: "/icon-points.svg", label: t("nav.points"), key: "Points", path: "/points" },
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="fixed left-0 top-0 bg-bg-surface dark:bg-gray-800 border-r border-border-normal dark:border-gray-700 flex flex-col items-center px-6 py-8 gap-8 h-screen w-[222px] overflow-y-auto">
|
<aside className="fixed left-0 top-0 bg-bg-surface dark:bg-gray-800 border-r border-border-normal dark:border-gray-700 flex flex-col items-center px-6 py-8 gap-8 h-screen w-[222px] overflow-y-auto">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<div className="w-full h-10">
|
<div className="w-full h-10">
|
||||||
<Image
|
<Image
|
||||||
src="/logo.svg"
|
src="/logo.svg"
|
||||||
alt="ASSETX Logo"
|
alt="ASSETX Logo"
|
||||||
width={174}
|
width={174}
|
||||||
height={40}
|
height={40}
|
||||||
className="w-full h-full"
|
className="w-full h-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Navigation */}
|
{/* Navigation */}
|
||||||
<nav className="flex flex-col gap-2 w-[174px]">
|
<nav className="flex flex-col gap-2 w-[174px]">
|
||||||
{navigationItems.map((item) => (
|
{navigationItems.map((item) => (
|
||||||
<NavItem
|
<NavItem
|
||||||
key={item.key}
|
key={item.key}
|
||||||
icon={item.icon}
|
icon={item.icon}
|
||||||
label={item.label}
|
label={item.label}
|
||||||
isActive={activeItem === item.key}
|
isActive={activeItem === item.key}
|
||||||
onClick={() => setActiveItem(item.key)}
|
onClick={() => setActiveItem(item.key)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Spacer */}
|
{/* Spacer */}
|
||||||
<div className="flex-1" />
|
<div className="flex-1" />
|
||||||
|
|
||||||
{/* Global TVL Section */}
|
{/* Global TVL Section */}
|
||||||
<div className="w-full border-t border-border-gray dark:border-gray-700 pt-8">
|
<div className="w-full border-t border-border-gray dark:border-gray-700 pt-8">
|
||||||
<div className="bg-bg-subtle dark:bg-gray-700 rounded-xl p-4 flex flex-col gap-1 h-[85px]">
|
<div className="bg-bg-subtle dark:bg-gray-700 rounded-xl p-4 flex flex-col gap-1 h-[85px]">
|
||||||
<p className="text-text-tertiary dark:text-gray-400 text-[10px] font-medium leading-[150%] tracking-[0.01em]">
|
<p className="text-text-tertiary dark:text-gray-400 text-[10px] font-medium leading-[150%] tracking-[0.01em]">
|
||||||
{t("nav.globalTVL")}
|
{t("nav.globalTVL")}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-text-primary dark:text-white text-base font-extrabold leading-[150%] font-jetbrains">
|
<p className="text-text-primary dark:text-white text-base font-extrabold leading-[150%] font-jetbrains">
|
||||||
$465,000,000
|
$465,000,000
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* FAQs Link */}
|
{/* FAQs Link */}
|
||||||
<div className="rounded-xl flex items-center h-[42px] mt-8">
|
<div className="rounded-xl flex items-center h-[42px] mt-8">
|
||||||
<div className="flex items-center gap-0">
|
<div className="flex items-center gap-0">
|
||||||
<Image
|
<Image
|
||||||
src="/icon-faq.png"
|
src="/icon-faq.png"
|
||||||
alt="FAQ"
|
alt="FAQ"
|
||||||
width={24}
|
width={24}
|
||||||
height={24}
|
height={24}
|
||||||
className="object-cover"
|
className="object-cover"
|
||||||
/>
|
/>
|
||||||
<span className="text-text-primary dark:text-white text-sm font-bold leading-[150%]">
|
<span className="text-text-primary dark:text-white text-sm font-bold leading-[150%]">
|
||||||
{t("nav.faqs")}
|
{t("nav.faqs")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +1,61 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
|
||||||
interface StatCardProps {
|
interface StatCardProps {
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
change?: string;
|
change?: string;
|
||||||
changeColor?: string;
|
changeColor?: string;
|
||||||
valueColor?: string;
|
valueColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function StatCard({
|
function StatCard({
|
||||||
label,
|
label,
|
||||||
value,
|
value,
|
||||||
change,
|
change,
|
||||||
changeColor = "text-green-500",
|
changeColor = "text-green-500",
|
||||||
valueColor
|
valueColor
|
||||||
}: StatCardProps) {
|
}: StatCardProps) {
|
||||||
const getValueColor = () => {
|
const getValueColor = () => {
|
||||||
if (valueColor) return valueColor;
|
if (valueColor) return valueColor;
|
||||||
if (label.includes("APY") || label.includes("年化")) return "#ff6900";
|
if (label.includes("APY") || label.includes("年化")) return "#ff6900";
|
||||||
return "#111827";
|
return "#111827";
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-bg-subtle dark:bg-gray-700 rounded-2xl border border-border-gray dark:border-gray-600 p-4 flex flex-col gap-2">
|
<div className="bg-bg-subtle dark:bg-gray-700 rounded-2xl border border-border-gray dark:border-gray-600 p-4 flex flex-col gap-2">
|
||||||
<p className="text-caption-tiny font-bold text-text-tertiary dark:text-gray-400 uppercase tracking-wider">
|
<p className="text-caption-tiny font-bold text-text-tertiary dark:text-gray-400 uppercase tracking-wider">
|
||||||
{label}
|
{label}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-heading-h3 font-bold dark:text-white" style={{ color: getValueColor() }}>
|
<p className="text-heading-h3 font-bold dark:text-white" style={{ color: getValueColor() }}>
|
||||||
{value}
|
{value}
|
||||||
</p>
|
</p>
|
||||||
{change && (
|
{change && (
|
||||||
<p className={`text-caption-tiny font-medium ${changeColor} dark:text-gray-400`}>
|
<p className={`text-caption-tiny font-medium ${changeColor} dark:text-gray-400`}>
|
||||||
{change}
|
{change}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function StatsCards() {
|
export default function StatsCards() {
|
||||||
const { t } = useApp();
|
const { t } = useApp();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-5 gap-4 w-full">
|
<div className="grid grid-cols-5 gap-4 w-full">
|
||||||
<StatCard label={t("stats.currentAPY")} value="22%" change="+2.5% WoW" changeColor="text-green-500" />
|
<StatCard label={t("stats.currentAPY")} value="22%" change="+2.5% WoW" changeColor="text-green-500" />
|
||||||
<StatCard label={t("stats.totalValueLocked")} value="$240.5M" change="+$2.3M Today" changeColor="text-green-500" />
|
<StatCard label={t("stats.totalValueLocked")} value="$240.5M" change="+$2.3M Today" changeColor="text-green-500" />
|
||||||
<StatCard label="24h Volume" value="$12.8M" change="↑ 23% vs Avg" changeColor="text-green-500" />
|
<StatCard label="24h Volume" value="$12.8M" change="↑ 23% vs Avg" changeColor="text-green-500" />
|
||||||
<StatCard label={t("stats.yourBalance")} value="0.00" change="$0.00 USD" changeColor="text-text-tertiary" />
|
<StatCard label={t("stats.yourBalance")} value="0.00" change="$0.00 USD" changeColor="text-text-tertiary" />
|
||||||
<StatCard
|
<StatCard
|
||||||
label={t("stats.yourEarnings")}
|
label={t("stats.yourEarnings")}
|
||||||
value="$0.00"
|
value="$0.00"
|
||||||
change="All Time"
|
change="All Time"
|
||||||
changeColor="text-text-tertiary"
|
changeColor="text-text-tertiary"
|
||||||
valueColor="#10b981"
|
valueColor="#10b981"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,43 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { Tabs, Tab } from "@heroui/react";
|
||||||
|
|
||||||
interface Tab {
|
interface TabItem {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TabNavigationProps {
|
interface TabNavigationProps {
|
||||||
tabs: Tab[];
|
tabs: TabItem[];
|
||||||
defaultActiveId?: string;
|
defaultActiveId?: string;
|
||||||
onTabChange?: (tabId: string) => void;
|
onTabChange?: (tabId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TabNavigation({
|
export default function TabNavigation({
|
||||||
tabs,
|
tabs,
|
||||||
defaultActiveId,
|
defaultActiveId,
|
||||||
onTabChange,
|
onTabChange,
|
||||||
}: TabNavigationProps) {
|
}: TabNavigationProps) {
|
||||||
const [activeTab, setActiveTab] = useState(defaultActiveId || tabs[0]?.id);
|
const handleSelectionChange = (key: React.Key) => {
|
||||||
|
onTabChange?.(key.toString());
|
||||||
const handleTabClick = (tabId: string) => {
|
};
|
||||||
setActiveTab(tabId);
|
|
||||||
onTabChange?.(tabId);
|
return (
|
||||||
};
|
<Tabs
|
||||||
|
selectedKey={defaultActiveId || tabs[0]?.id}
|
||||||
return (
|
onSelectionChange={handleSelectionChange}
|
||||||
<div className="flex items-center gap-8">
|
variant="underlined"
|
||||||
{tabs.map((tab) => {
|
classNames={{
|
||||||
const isActive = activeTab === tab.id;
|
base: "w-auto",
|
||||||
return (
|
tabList: "gap-8 w-auto p-0",
|
||||||
<button
|
cursor: "bg-text-primary dark:bg-white",
|
||||||
key={tab.id}
|
tab: "px-0 h-auto",
|
||||||
onClick={() => handleTabClick(tab.id)}
|
tabContent: "text-sm font-bold text-text-tertiary dark:text-gray-400 group-data-[selected=true]:text-text-primary dark:group-data-[selected=true]:text-white",
|
||||||
className="flex flex-col gap-2 items-start"
|
}}
|
||||||
>
|
>
|
||||||
<span
|
{tabs.map((tab) => (
|
||||||
className={`text-sm font-bold leading-[150%] ${
|
<Tab key={tab.id} title={tab.label} />
|
||||||
isActive ? "text-text-primary dark:text-white" : "text-text-tertiary dark:text-gray-400"
|
))}
|
||||||
}`}
|
</Tabs>
|
||||||
>
|
);
|
||||||
{tab.label}
|
}
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
className={`self-stretch border-t-2 -mt-[2px] ${
|
|
||||||
isActive ? "border-text-primary dark:border-white" : "border-transparent"
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,46 +1,53 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useApp } from "@/contexts/AppContext";
|
import { useApp } from "@/contexts/AppContext";
|
||||||
|
import { Button } from "@heroui/react";
|
||||||
export default function ThemeSwitch() {
|
|
||||||
const { theme, setTheme } = useApp();
|
export default function ThemeSwitch() {
|
||||||
|
const { theme, setTheme } = useApp();
|
||||||
const toggleTheme = () => {
|
|
||||||
setTheme(theme === "light" ? "dark" : "light");
|
const toggleTheme = () => {
|
||||||
};
|
setTheme(theme === "light" ? "dark" : "light");
|
||||||
|
};
|
||||||
return (
|
|
||||||
<button
|
return (
|
||||||
onClick={toggleTheme}
|
<Button
|
||||||
className="bg-bg-surface dark:bg-gray-800 rounded-lg border border-border-normal dark:border-gray-700 px-3 py-2 flex items-center justify-center h-10 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
isIconOnly
|
||||||
>
|
variant="bordered"
|
||||||
{theme === "light" ? (
|
onPress={toggleTheme}
|
||||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
className="bg-bg-surface dark:bg-gray-800 border-border-normal dark:border-gray-700 min-w-10 h-10 rounded-lg"
|
||||||
<path
|
aria-label={`Switch to ${theme === "light" ? "dark" : "light"} mode`}
|
||||||
d="M10 15C12.7614 15 15 12.7614 15 10C15 7.23858 12.7614 5 10 5C7.23858 5 5 7.23858 5 10C5 12.7614 7.23858 15 10 15Z"
|
>
|
||||||
stroke="#111827"
|
{theme === "light" ? (
|
||||||
strokeWidth="1.5"
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||||
strokeLinecap="round"
|
<path
|
||||||
strokeLinejoin="round"
|
d="M10 15C12.7614 15 15 12.7614 15 10C15 7.23858 12.7614 5 10 5C7.23858 5 5 7.23858 5 10C5 12.7614 7.23858 15 10 15Z"
|
||||||
/>
|
stroke="currentColor"
|
||||||
<path
|
strokeWidth="1.5"
|
||||||
d="M10 1V3M10 17V19M19 10H17M3 10H1M16.07 16.07L14.64 14.64M5.36 5.36L3.93 3.93M16.07 3.93L14.64 5.36M5.36 14.64L3.93 16.07"
|
strokeLinecap="round"
|
||||||
stroke="#111827"
|
strokeLinejoin="round"
|
||||||
strokeWidth="1.5"
|
className="text-text-primary dark:text-white"
|
||||||
strokeLinecap="round"
|
/>
|
||||||
/>
|
<path
|
||||||
</svg>
|
d="M10 1V3M10 17V19M19 10H17M3 10H1M16.07 16.07L14.64 14.64M5.36 5.36L3.93 3.93M16.07 3.93L14.64 5.36M5.36 14.64L3.93 16.07"
|
||||||
) : (
|
stroke="currentColor"
|
||||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
strokeWidth="1.5"
|
||||||
<path
|
strokeLinecap="round"
|
||||||
d="M17 10.79C16.8427 12.4922 16.1039 14.0754 14.9116 15.2662C13.7192 16.4571 12.1503 17.1913 10.4501 17.3464C8.74989 17.5016 7.04992 17.0676 5.63182 16.1159C4.21372 15.1642 3.15973 13.7534 2.63564 12.1102C2.11155 10.467 2.14637 8.68739 2.73477 7.06725C3.32317 5.44711 4.43113 4.07931 5.88616 3.18637C7.3412 2.29343 9.05859 1.93047 10.7542 2.15507C12.4498 2.37967 13.9989 3.17747 15.16 4.41"
|
className="text-text-primary dark:text-white"
|
||||||
stroke="#111827"
|
/>
|
||||||
strokeWidth="1.5"
|
</svg>
|
||||||
strokeLinecap="round"
|
) : (
|
||||||
strokeLinejoin="round"
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||||
/>
|
<path
|
||||||
</svg>
|
d="M17 10.79C16.8427 12.4922 16.1039 14.0754 14.9116 15.2662C13.7192 16.4571 12.1503 17.1913 10.4501 17.3464C8.74989 17.5016 7.04992 17.0676 5.63182 16.1159C4.21372 15.1642 3.15973 13.7534 2.63564 12.1102C2.11155 10.467 2.14637 8.68739 2.73477 7.06725C3.32317 5.44711 4.43113 4.07931 5.88616 3.18637C7.3412 2.29343 9.05859 1.93047 10.7542 2.15507C12.4498 2.37967 13.9989 3.17747 15.16 4.41"
|
||||||
)}
|
stroke="currentColor"
|
||||||
</button>
|
strokeWidth="1.5"
|
||||||
);
|
strokeLinecap="round"
|
||||||
}
|
strokeLinejoin="round"
|
||||||
|
className="text-text-primary dark:text-white"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,50 +1,59 @@
|
|||||||
import Image from "next/image";
|
"use client";
|
||||||
import Breadcrumb from "./Breadcrumb";
|
|
||||||
import LanguageSwitch from "./LanguageSwitch";
|
import Image from "next/image";
|
||||||
import ThemeSwitch from "./ThemeSwitch";
|
import { Button } from "@heroui/react";
|
||||||
|
import Breadcrumb from "./Breadcrumb";
|
||||||
export default function TopBar() {
|
import LanguageSwitch from "./LanguageSwitch";
|
||||||
const breadcrumbItems = [
|
import ThemeSwitch from "./ThemeSwitch";
|
||||||
{ label: "ASSETX" },
|
|
||||||
{ label: "Product" },
|
export default function TopBar() {
|
||||||
{ label: "Detail" },
|
const breadcrumbItems = [
|
||||||
];
|
{ label: "ASSETX" },
|
||||||
|
{ label: "Product" },
|
||||||
return (
|
{ label: "Detail" },
|
||||||
<div className="flex items-center justify-between w-full">
|
];
|
||||||
{/* Left: Breadcrumb */}
|
|
||||||
<div className="flex items-center gap-2">
|
return (
|
||||||
<Breadcrumb items={breadcrumbItems} />
|
<div className="flex items-center justify-between w-full">
|
||||||
</div>
|
{/* Left: Breadcrumb */}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
{/* Right: Actions */}
|
<Breadcrumb items={breadcrumbItems} />
|
||||||
<div className="flex items-center gap-4">
|
</div>
|
||||||
{/* Language Switch */}
|
|
||||||
<LanguageSwitch />
|
{/* Right: Actions */}
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
{/* Theme Switch */}
|
{/* Language Switch */}
|
||||||
<ThemeSwitch />
|
<LanguageSwitch />
|
||||||
|
|
||||||
{/* Wallet Button */}
|
{/* Theme Switch */}
|
||||||
<button className="bg-bg-surface rounded-lg border border-border-normal px-2 py-2 flex items-center justify-center h-10">
|
<ThemeSwitch />
|
||||||
<Image src="/icon-wallet.svg" alt="Wallet" width={20} height={20} />
|
|
||||||
<Image
|
{/* Wallet Button */}
|
||||||
src="/icon-notification.svg"
|
<Button
|
||||||
alt="Notification"
|
isIconOnly
|
||||||
width={14}
|
variant="bordered"
|
||||||
height={14}
|
className="bg-bg-surface border-border-normal min-w-10 h-10 rounded-lg"
|
||||||
className="ml-1"
|
>
|
||||||
/>
|
<div className="flex items-center justify-center">
|
||||||
</button>
|
<Image src="/icon-wallet.svg" alt="Wallet" width={20} height={20} />
|
||||||
|
<Image
|
||||||
{/* Address Button */}
|
src="/icon-notification.svg"
|
||||||
<button className="bg-text-primary rounded-lg px-4 py-2 flex items-center justify-center gap-2 h-10 hover:bg-gray-800 transition-colors">
|
alt="Notification"
|
||||||
<Image src="/icon-copy.svg" alt="Copy" width={16} height={16} />
|
width={14}
|
||||||
<span className="text-white text-sm font-bold font-jetbrains">
|
height={14}
|
||||||
0x12...4F82
|
className="ml-1"
|
||||||
</span>
|
/>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</Button>
|
||||||
</div>
|
|
||||||
);
|
{/* Address Button */}
|
||||||
}
|
<Button
|
||||||
|
className="bg-text-primary text-white font-bold font-jetbrains text-sm h-10 px-4 rounded-lg"
|
||||||
|
startContent={<Image src="/icon-copy.svg" alt="Copy" width={16} height={16} />}
|
||||||
|
>
|
||||||
|
0x12...4F82
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
15634
package-lock.json
generated
15634
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
57
package.json
57
package.json
@@ -1,27 +1,30 @@
|
|||||||
{
|
{
|
||||||
"name": "asset-dashboard-next",
|
"name": "asset-dashboard-next",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -H 0.0.0.0",
|
"dev": "next dev -H 0.0.0.0",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "^15.1.4",
|
"@heroui/react": "^2.8.8",
|
||||||
"react": "^19.0.0",
|
"@heroui/theme": "^2.4.26",
|
||||||
"react-dom": "^19.0.0"
|
"framer-motion": "^12.29.2",
|
||||||
},
|
"next": "^15.1.4",
|
||||||
"devDependencies": {
|
"react": "^19.0.0",
|
||||||
"@types/node": "^20",
|
"react-dom": "^19.0.0"
|
||||||
"@types/react": "^19",
|
},
|
||||||
"@types/react-dom": "^19",
|
"devDependencies": {
|
||||||
"autoprefixer": "^10.4.20",
|
"@types/node": "^20",
|
||||||
"eslint": "^8",
|
"@types/react": "^19",
|
||||||
"eslint-config-next": "^15.1.4",
|
"@types/react-dom": "^19",
|
||||||
"postcss": "^8.4.49",
|
"autoprefixer": "^10.4.20",
|
||||||
"tailwindcss": "^3.4.17",
|
"eslint": "^8",
|
||||||
"typescript": "^5"
|
"eslint-config-next": "^15.1.4",
|
||||||
}
|
"postcss": "^8.4.49",
|
||||||
}
|
"tailwindcss": "^3.4.17",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,44 +1,46 @@
|
|||||||
import type { Config } from "tailwindcss";
|
import type { Config } from "tailwindcss";
|
||||||
|
import { heroui } from "@heroui/theme";
|
||||||
export default {
|
|
||||||
darkMode: "class",
|
export default {
|
||||||
content: [
|
darkMode: "class",
|
||||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
content: [
|
||||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
],
|
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
theme: {
|
"./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}",
|
||||||
extend: {
|
],
|
||||||
colors: {
|
theme: {
|
||||||
background: "var(--background)",
|
extend: {
|
||||||
foreground: "var(--foreground)",
|
colors: {
|
||||||
'bg-subtle': '#f9fafb',
|
background: "var(--background)",
|
||||||
'bg-surface': '#ffffff',
|
foreground: "var(--foreground)",
|
||||||
'border-normal': '#e5e7eb',
|
'bg-subtle': '#f9fafb',
|
||||||
'border-gray': '#f3f4f6',
|
'bg-surface': '#ffffff',
|
||||||
'text-primary': '#111827',
|
'border-normal': '#e5e7eb',
|
||||||
'text-tertiary': '#9ca1af',
|
'border-gray': '#f3f4f6',
|
||||||
'fill-secondary-click': '#f3f4f6',
|
'text-primary': '#111827',
|
||||||
},
|
'text-tertiary': '#9ca1af',
|
||||||
fontFamily: {
|
'fill-secondary-click': '#f3f4f6',
|
||||||
inter: ['var(--font-inter)', 'Inter', 'sans-serif'],
|
},
|
||||||
jetbrains: ['var(--font-jetbrains)', 'JetBrains Mono', 'monospace'],
|
fontFamily: {
|
||||||
},
|
inter: ['var(--font-inter)', 'Inter', 'sans-serif'],
|
||||||
fontSize: {
|
jetbrains: ['var(--font-jetbrains)', 'JetBrains Mono', 'monospace'],
|
||||||
'caption-tiny': ['12px', { lineHeight: '150%', letterSpacing: '0.01em' }],
|
},
|
||||||
'body-small': ['14px', { lineHeight: '150%' }],
|
fontSize: {
|
||||||
'body-default': ['16px', { lineHeight: '150%' }],
|
'caption-tiny': ['12px', { lineHeight: '150%', letterSpacing: '0.01em' }],
|
||||||
'body-large': ['18px', { lineHeight: '150%' }],
|
'body-small': ['14px', { lineHeight: '150%' }],
|
||||||
'heading-h3': ['24px', { lineHeight: '130%', letterSpacing: '-0.005em' }],
|
'body-default': ['16px', { lineHeight: '150%' }],
|
||||||
'heading-h2': ['32px', { lineHeight: '120%', letterSpacing: '-0.01em' }],
|
'body-large': ['18px', { lineHeight: '150%' }],
|
||||||
},
|
'heading-h3': ['24px', { lineHeight: '130%', letterSpacing: '-0.005em' }],
|
||||||
fontWeight: {
|
'heading-h2': ['32px', { lineHeight: '120%', letterSpacing: '-0.01em' }],
|
||||||
regular: '400',
|
},
|
||||||
medium: '500',
|
fontWeight: {
|
||||||
bold: '700',
|
regular: '400',
|
||||||
extrabold: '800',
|
medium: '500',
|
||||||
},
|
bold: '700',
|
||||||
},
|
extrabold: '800',
|
||||||
},
|
},
|
||||||
plugins: [],
|
},
|
||||||
} satisfies Config;
|
},
|
||||||
|
plugins: [heroui()],
|
||||||
|
} satisfies Config;
|
||||||
|
|||||||
Reference in New Issue
Block a user