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 { Inter, JetBrains_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { AppProvider } from "@/contexts/AppContext";
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "500", "700", "800"],
|
||||
variable: "--font-inter",
|
||||
});
|
||||
|
||||
const jetbrainsMono = JetBrains_Mono({
|
||||
subsets: ["latin"],
|
||||
weight: ["500", "700", "800"],
|
||||
variable: "--font-jetbrains",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "AssetX Dashboard",
|
||||
description: "DeFi Asset Management Platform",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<body className={`${inter.variable} ${jetbrainsMono.variable} ${inter.className}`}>
|
||||
<AppProvider>{children}</AppProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
import type { Metadata } from "next";
|
||||
import { Inter, JetBrains_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { Providers } from "@/components/Providers";
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "500", "700", "800"],
|
||||
variable: "--font-inter",
|
||||
});
|
||||
|
||||
const jetbrainsMono = JetBrains_Mono({
|
||||
subsets: ["latin"],
|
||||
weight: ["500", "700", "800"],
|
||||
variable: "--font-jetbrains",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "AssetX Dashboard",
|
||||
description: "DeFi Asset Management Platform",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<body className={`${inter.variable} ${jetbrainsMono.variable} ${inter.className}`}>
|
||||
<Providers>{children}</Providers>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,101 +1,101 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
export default function APYHistoryCard() {
|
||||
const { t } = useApp();
|
||||
const [activeTab, setActiveTab] = useState<"apy" | "price">("apy");
|
||||
|
||||
// 橙色渐变条形图数据
|
||||
const chartData = [
|
||||
{ height: 85, color: "#ffe9dc" },
|
||||
{ height: 93, color: "#ffc9ad" },
|
||||
{ height: 100, color: "#ffc9ad" },
|
||||
{ height: 105, color: "#ffba96" },
|
||||
{ height: 108, color: "#ffa67e" },
|
||||
{ height: 116, color: "#f48d5f" },
|
||||
{ height: 124, color: "#ff6900" },
|
||||
{ height: 127, color: "#f35b00" },
|
||||
{ height: 139, color: "#d64700" },
|
||||
];
|
||||
|
||||
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">
|
||||
{/* Tabs */}
|
||||
<div className="flex gap-6 border-b border-border-gray dark:border-gray-700">
|
||||
<button
|
||||
onClick={() => setActiveTab("apy")}
|
||||
className={`pb-3 px-1 text-body-small font-bold transition-colors ${
|
||||
activeTab === "apy"
|
||||
? "text-text-primary dark:text-white border-b-2 border-text-primary dark:border-white -mb-[1px]"
|
||||
: "text-text-tertiary dark:text-gray-400"
|
||||
}`}
|
||||
>
|
||||
{t("apy.apyHistory")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("price")}
|
||||
className={`pb-3 px-1 text-body-small font-bold transition-colors ${
|
||||
activeTab === "price"
|
||||
? "text-text-primary dark:text-white border-b-2 border-text-primary dark:border-white -mb-[1px]"
|
||||
: "text-text-tertiary dark:text-gray-400"
|
||||
}`}
|
||||
>
|
||||
{t("apy.priceHistory")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Chart Area */}
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
||||
{t("apy.lastDays")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Orange Gradient Chart */}
|
||||
<div
|
||||
className="flex items-end gap-4 w-full border-b border-border-gray dark:border-gray-700"
|
||||
style={{
|
||||
height: "140px",
|
||||
background:
|
||||
"linear-gradient(0deg, rgba(255, 247, 237, 0.5) 0%, rgba(255, 247, 237, 0) 100%)",
|
||||
}}
|
||||
>
|
||||
{chartData.map((bar, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex-1"
|
||||
style={{
|
||||
height: `${bar.height}px`,
|
||||
backgroundColor: bar.color,
|
||||
borderRadius: "2px 2px 0px 0px",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
||||
{t("apy.highest")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
24.8%
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
||||
{t("apy.lowest")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
18.2%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
export default function APYHistoryCard() {
|
||||
const { t } = useApp();
|
||||
const [activeTab, setActiveTab] = useState<"apy" | "price">("apy");
|
||||
|
||||
// 橙色渐变条形图数据
|
||||
const chartData = [
|
||||
{ height: 85, color: "#ffe9dc" },
|
||||
{ height: 93, color: "#ffc9ad" },
|
||||
{ height: 100, color: "#ffc9ad" },
|
||||
{ height: 105, color: "#ffba96" },
|
||||
{ height: 108, color: "#ffa67e" },
|
||||
{ height: 116, color: "#f48d5f" },
|
||||
{ height: 124, color: "#ff6900" },
|
||||
{ height: 127, color: "#f35b00" },
|
||||
{ height: 139, color: "#d64700" },
|
||||
];
|
||||
|
||||
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">
|
||||
{/* Tabs */}
|
||||
<div className="flex gap-6 border-b border-border-gray dark:border-gray-700">
|
||||
<button
|
||||
onClick={() => setActiveTab("apy")}
|
||||
className={`pb-3 px-1 text-body-small font-bold transition-colors ${
|
||||
activeTab === "apy"
|
||||
? "text-text-primary dark:text-white border-b-2 border-text-primary dark:border-white -mb-[1px]"
|
||||
: "text-text-tertiary dark:text-gray-400"
|
||||
}`}
|
||||
>
|
||||
{t("apy.apyHistory")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("price")}
|
||||
className={`pb-3 px-1 text-body-small font-bold transition-colors ${
|
||||
activeTab === "price"
|
||||
? "text-text-primary dark:text-white border-b-2 border-text-primary dark:border-white -mb-[1px]"
|
||||
: "text-text-tertiary dark:text-gray-400"
|
||||
}`}
|
||||
>
|
||||
{t("apy.priceHistory")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Chart Area */}
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
||||
{t("apy.lastDays")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Orange Gradient Chart */}
|
||||
<div
|
||||
className="flex items-end gap-4 w-full border-b border-border-gray dark:border-gray-700"
|
||||
style={{
|
||||
height: "140px",
|
||||
background:
|
||||
"linear-gradient(0deg, rgba(255, 247, 237, 0.5) 0%, rgba(255, 247, 237, 0) 100%)",
|
||||
}}
|
||||
>
|
||||
{chartData.map((bar, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex-1"
|
||||
style={{
|
||||
height: `${bar.height}px`,
|
||||
backgroundColor: bar.color,
|
||||
borderRadius: "2px 2px 0px 0px",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Stats */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
||||
{t("apy.highest")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
24.8%
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-caption-tiny font-medium text-text-tertiary dark:text-gray-400">
|
||||
{t("apy.lowest")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
18.2%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,237 +1,237 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
interface VerificationCardProps {
|
||||
icon: string;
|
||||
title: string;
|
||||
description: string;
|
||||
buttonText: string;
|
||||
}
|
||||
|
||||
function VerificationCard({ icon, title, description, buttonText }: VerificationCardProps) {
|
||||
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 items-start gap-4">
|
||||
<div className="w-5 h-5 flex-shrink-0">
|
||||
<Image src={icon} alt={title} width={20} height={20} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<h4 className="text-body-default font-bold text-text-primary dark:text-white">
|
||||
{title}
|
||||
</h4>
|
||||
<p className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
{buttonText}
|
||||
</span>
|
||||
<Image src="/component-118.svg" alt="" width={16} height={16} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AssetCustodyVerification() {
|
||||
const { t } = useApp();
|
||||
return (
|
||||
<div className="flex flex-col gap-8 w-full">
|
||||
{/* Header */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<h2 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||
{t("custody.title")}
|
||||
</h2>
|
||||
<p className="text-body-small font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.description")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 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">
|
||||
{/* 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-1">
|
||||
<h3 className="text-body-default font-bold text-text-primary dark:text-white">
|
||||
{t("custody.underlyingHoldings")}
|
||||
</h3>
|
||||
<p className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.verifiedBy")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Image src="/component-115.svg" alt="" width={16} height={16} />
|
||||
<span className="text-caption-tiny font-medium text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.lastUpdated")}: 2 {t("custody.minutesAgo")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="flex flex-col">
|
||||
{/* Table Header Row */}
|
||||
<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">
|
||||
{t("custody.custodian")}
|
||||
</div>
|
||||
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.assetType")}
|
||||
</div>
|
||||
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.maturity")}
|
||||
</div>
|
||||
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.valueUSD")}
|
||||
</div>
|
||||
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.status")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table Body Row */}
|
||||
<div className="grid grid-cols-5 gap-4 py-6">
|
||||
{/* Custodian */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0"
|
||||
style={{
|
||||
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">
|
||||
GY
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
{t("custody.morganStanley")}
|
||||
</span>
|
||||
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.primeBroker")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Asset Type */}
|
||||
<div className="flex items-center">
|
||||
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
||||
{t("custody.usEquityPortfolio")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Maturity */}
|
||||
<div className="flex flex-col justify-center">
|
||||
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
||||
05 Feb 2026
|
||||
</span>
|
||||
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
(77 {t("custody.days")})
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Value */}
|
||||
<div className="flex items-center">
|
||||
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
||||
$12,500,000.00
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Status */}
|
||||
<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">
|
||||
<Image src="/component-116.svg" alt="" width={12} height={12} />
|
||||
<span className="text-[10px] font-bold leading-[150%] text-[#10b981] dark:text-green-400">
|
||||
{t("custody.verified")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table Footer Row */}
|
||||
<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">
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
{t("custody.totalValue")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="col-span-2 flex items-center">
|
||||
<span className="text-body-default font-bold text-text-primary dark:text-white">
|
||||
$12,500,000.00
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Verification Cards Row */}
|
||||
<div className="flex flex-row gap-6 pt-6">
|
||||
<VerificationCard
|
||||
icon="/component-117.svg"
|
||||
title={t("custody.smartContract")}
|
||||
description={t("custody.smartContractDesc")}
|
||||
buttonText={t("custody.viewReports")}
|
||||
/>
|
||||
<VerificationCard
|
||||
icon="/component-119.svg"
|
||||
title={t("custody.compliance")}
|
||||
description={t("custody.complianceDesc")}
|
||||
buttonText={t("custody.viewReports")}
|
||||
/>
|
||||
<VerificationCard
|
||||
icon="/component-121.svg"
|
||||
title={t("custody.proofOfReserves")}
|
||||
description={t("custody.proofDesc")}
|
||||
buttonText={t("custody.viewReports")}
|
||||
/>
|
||||
|
||||
{/* 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 flex-col gap-2">
|
||||
<h3 className="text-body-default font-bold text-text-primary dark:text-white">
|
||||
{t("custody.independentVerifications")}
|
||||
</h3>
|
||||
<p className="text-body-small font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.independentDesc")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<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="flex flex-col gap-0.5">
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
{t("custody.attestationReport")}
|
||||
</span>
|
||||
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
November 2025
|
||||
</span>
|
||||
</div>
|
||||
<Image src="/component-123.svg" alt="" width={24} height={24} />
|
||||
</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="flex flex-col gap-0.5">
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
{t("custody.attestationReport")}
|
||||
</span>
|
||||
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
October 2025
|
||||
</span>
|
||||
</div>
|
||||
<Image src="/component-124.svg" alt="" width={24} height={24} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
{t("custody.viewAllArchive")}
|
||||
</span>
|
||||
<Image src="/component-125.svg" alt="" width={16} height={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
interface VerificationCardProps {
|
||||
icon: string;
|
||||
title: string;
|
||||
description: string;
|
||||
buttonText: string;
|
||||
}
|
||||
|
||||
function VerificationCard({ icon, title, description, buttonText }: VerificationCardProps) {
|
||||
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 items-start gap-4">
|
||||
<div className="w-5 h-5 flex-shrink-0">
|
||||
<Image src={icon} alt={title} width={20} height={20} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<h4 className="text-body-default font-bold text-text-primary dark:text-white">
|
||||
{title}
|
||||
</h4>
|
||||
<p className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
{buttonText}
|
||||
</span>
|
||||
<Image src="/component-118.svg" alt="" width={16} height={16} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AssetCustodyVerification() {
|
||||
const { t } = useApp();
|
||||
return (
|
||||
<div className="flex flex-col gap-8 w-full">
|
||||
{/* Header */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<h2 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||
{t("custody.title")}
|
||||
</h2>
|
||||
<p className="text-body-small font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.description")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 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">
|
||||
{/* 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-1">
|
||||
<h3 className="text-body-default font-bold text-text-primary dark:text-white">
|
||||
{t("custody.underlyingHoldings")}
|
||||
</h3>
|
||||
<p className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.verifiedBy")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Image src="/component-115.svg" alt="" width={16} height={16} />
|
||||
<span className="text-caption-tiny font-medium text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.lastUpdated")}: 2 {t("custody.minutesAgo")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="flex flex-col">
|
||||
{/* Table Header Row */}
|
||||
<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">
|
||||
{t("custody.custodian")}
|
||||
</div>
|
||||
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.assetType")}
|
||||
</div>
|
||||
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.maturity")}
|
||||
</div>
|
||||
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.valueUSD")}
|
||||
</div>
|
||||
<div className="text-caption-tiny font-bold uppercase tracking-wider text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.status")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table Body Row */}
|
||||
<div className="grid grid-cols-5 gap-4 py-6">
|
||||
{/* Custodian */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className="w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0"
|
||||
style={{
|
||||
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">
|
||||
GY
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
{t("custody.morganStanley")}
|
||||
</span>
|
||||
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.primeBroker")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Asset Type */}
|
||||
<div className="flex items-center">
|
||||
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
||||
{t("custody.usEquityPortfolio")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Maturity */}
|
||||
<div className="flex flex-col justify-center">
|
||||
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
||||
05 Feb 2026
|
||||
</span>
|
||||
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
(77 {t("custody.days")})
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Value */}
|
||||
<div className="flex items-center">
|
||||
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
||||
$12,500,000.00
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Status */}
|
||||
<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">
|
||||
<Image src="/component-116.svg" alt="" width={12} height={12} />
|
||||
<span className="text-[10px] font-bold leading-[150%] text-[#10b981] dark:text-green-400">
|
||||
{t("custody.verified")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table Footer Row */}
|
||||
<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">
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
{t("custody.totalValue")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="col-span-2 flex items-center">
|
||||
<span className="text-body-default font-bold text-text-primary dark:text-white">
|
||||
$12,500,000.00
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Verification Cards Row */}
|
||||
<div className="flex flex-row gap-6 pt-6">
|
||||
<VerificationCard
|
||||
icon="/component-117.svg"
|
||||
title={t("custody.smartContract")}
|
||||
description={t("custody.smartContractDesc")}
|
||||
buttonText={t("custody.viewReports")}
|
||||
/>
|
||||
<VerificationCard
|
||||
icon="/component-119.svg"
|
||||
title={t("custody.compliance")}
|
||||
description={t("custody.complianceDesc")}
|
||||
buttonText={t("custody.viewReports")}
|
||||
/>
|
||||
<VerificationCard
|
||||
icon="/component-121.svg"
|
||||
title={t("custody.proofOfReserves")}
|
||||
description={t("custody.proofDesc")}
|
||||
buttonText={t("custody.viewReports")}
|
||||
/>
|
||||
|
||||
{/* 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 flex-col gap-2">
|
||||
<h3 className="text-body-default font-bold text-text-primary dark:text-white">
|
||||
{t("custody.independentVerifications")}
|
||||
</h3>
|
||||
<p className="text-body-small font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
{t("custody.independentDesc")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<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="flex flex-col gap-0.5">
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
{t("custody.attestationReport")}
|
||||
</span>
|
||||
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
November 2025
|
||||
</span>
|
||||
</div>
|
||||
<Image src="/component-123.svg" alt="" width={24} height={24} />
|
||||
</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="flex flex-col gap-0.5">
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
{t("custody.attestationReport")}
|
||||
</span>
|
||||
<span className="text-caption-tiny font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
October 2025
|
||||
</span>
|
||||
</div>
|
||||
<Image src="/component-124.svg" alt="" width={24} height={24} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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">
|
||||
{t("custody.viewAllArchive")}
|
||||
</span>
|
||||
<Image src="/component-125.svg" alt="" width={16} height={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import AssetCustodyVerification from "./AssetCustodyVerification";
|
||||
|
||||
export default function AssetCustodyVerificationTab() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8 w-full">
|
||||
<AssetCustodyVerification />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
import AssetCustodyVerification from "./AssetCustodyVerification";
|
||||
|
||||
export default function AssetCustodyVerificationTab() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8 w-full">
|
||||
<AssetCustodyVerification />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
export default function AssetDescriptionCard() {
|
||||
const { t } = useApp();
|
||||
|
||||
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]">
|
||||
<h3 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||
{t("description.title")}
|
||||
</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">
|
||||
{t("description.content")}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
export default function AssetDescriptionCard() {
|
||||
const { t } = useApp();
|
||||
|
||||
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]">
|
||||
<h3 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||
{t("description.title")}
|
||||
</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">
|
||||
{t("description.content")}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import AssetDescriptionCard from "./AssetDescriptionCard";
|
||||
|
||||
export default function AssetDescriptionTab() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8 w-full">
|
||||
<AssetDescriptionCard />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
import AssetDescriptionCard from "./AssetDescriptionCard";
|
||||
|
||||
export default function AssetDescriptionTab() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8 w-full">
|
||||
<AssetDescriptionCard />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,116 +1,116 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
interface OverviewItemProps {
|
||||
icon: string;
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
function OverviewItem({ icon, label, value }: OverviewItemProps) {
|
||||
return (
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-5 h-6 flex-shrink-0">
|
||||
<Image src={icon} alt={label} width={20} height={24} />
|
||||
</div>
|
||||
<span className="text-body-small font-medium text-text-tertiary dark:text-gray-400">
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AssetOverviewCard() {
|
||||
const { t } = useApp();
|
||||
|
||||
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">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||
{t("assetOverview.title")}
|
||||
</h3>
|
||||
<div
|
||||
className="rounded-full border flex items-center gap-2 px-3 py-1.5"
|
||||
style={{
|
||||
backgroundColor: "#fffbf5",
|
||||
borderColor: "#ffedd5",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="w-1.5 h-1.5 rounded-full flex-shrink-0"
|
||||
style={{ backgroundColor: "#ffb933" }}
|
||||
/>
|
||||
<span
|
||||
className="text-xs font-semibold leading-4"
|
||||
style={{ color: "#ffb933" }}
|
||||
>
|
||||
{t("assetOverview.mediumRisk")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Overview Items */}
|
||||
<div className="flex gap-12 w-full">
|
||||
<div className="flex-1 flex flex-col gap-5">
|
||||
<OverviewItem
|
||||
icon="/component-11.svg"
|
||||
label={t("assetOverview.underlyingAssets")}
|
||||
value={t("assetOverview.usEquityIndex")}
|
||||
/>
|
||||
<OverviewItem
|
||||
icon="/component-12.svg"
|
||||
label={t("assetOverview.maturityRange")}
|
||||
value="05 Feb 2026"
|
||||
/>
|
||||
<OverviewItem
|
||||
icon="/component-13.svg"
|
||||
label={t("assetOverview.cap")}
|
||||
value="$50,000,000"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col gap-5">
|
||||
<OverviewItem
|
||||
icon="/component-14.svg"
|
||||
label={t("assetOverview.minInvestment")}
|
||||
value="100 USDC"
|
||||
/>
|
||||
<div className="flex flex-col gap-3 w-full">
|
||||
<OverviewItem
|
||||
icon="/component-15.svg"
|
||||
label={t("assetOverview.poolCapacity")}
|
||||
value="75%"
|
||||
/>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="border-t border-border-gray dark:border-gray-700" />
|
||||
|
||||
{/* Current Price */}
|
||||
<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">
|
||||
<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">
|
||||
{t("assetOverview.currentPrice")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-[20px] font-bold leading-[140%]">
|
||||
<span className="text-text-primary dark:text-white">1 GY-US = </span>
|
||||
<span style={{ color: "#10b981" }}>1.04 USDC</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
interface OverviewItemProps {
|
||||
icon: string;
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
function OverviewItem({ icon, label, value }: OverviewItemProps) {
|
||||
return (
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-5 h-6 flex-shrink-0">
|
||||
<Image src={icon} alt={label} width={20} height={24} />
|
||||
</div>
|
||||
<span className="text-body-small font-medium text-text-tertiary dark:text-gray-400">
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-body-small font-medium text-text-primary dark:text-white">
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AssetOverviewCard() {
|
||||
const { t } = useApp();
|
||||
|
||||
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">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||
{t("assetOverview.title")}
|
||||
</h3>
|
||||
<div
|
||||
className="rounded-full border flex items-center gap-2 px-3 py-1.5"
|
||||
style={{
|
||||
backgroundColor: "#fffbf5",
|
||||
borderColor: "#ffedd5",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="w-1.5 h-1.5 rounded-full flex-shrink-0"
|
||||
style={{ backgroundColor: "#ffb933" }}
|
||||
/>
|
||||
<span
|
||||
className="text-xs font-semibold leading-4"
|
||||
style={{ color: "#ffb933" }}
|
||||
>
|
||||
{t("assetOverview.mediumRisk")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Overview Items */}
|
||||
<div className="flex gap-12 w-full">
|
||||
<div className="flex-1 flex flex-col gap-5">
|
||||
<OverviewItem
|
||||
icon="/component-11.svg"
|
||||
label={t("assetOverview.underlyingAssets")}
|
||||
value={t("assetOverview.usEquityIndex")}
|
||||
/>
|
||||
<OverviewItem
|
||||
icon="/component-12.svg"
|
||||
label={t("assetOverview.maturityRange")}
|
||||
value="05 Feb 2026"
|
||||
/>
|
||||
<OverviewItem
|
||||
icon="/component-13.svg"
|
||||
label={t("assetOverview.cap")}
|
||||
value="$50,000,000"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col gap-5">
|
||||
<OverviewItem
|
||||
icon="/component-14.svg"
|
||||
label={t("assetOverview.minInvestment")}
|
||||
value="100 USDC"
|
||||
/>
|
||||
<div className="flex flex-col gap-3 w-full">
|
||||
<OverviewItem
|
||||
icon="/component-15.svg"
|
||||
label={t("assetOverview.poolCapacity")}
|
||||
value="75%"
|
||||
/>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="border-t border-border-gray dark:border-gray-700" />
|
||||
|
||||
{/* Current Price */}
|
||||
<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">
|
||||
<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">
|
||||
{t("assetOverview.currentPrice")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-[20px] font-bold leading-[140%]">
|
||||
<span className="text-text-primary dark:text-white">1 GY-US = </span>
|
||||
<span style={{ color: "#10b981" }}>1.04 USDC</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
import Image from "next/image";
|
||||
|
||||
interface BreadcrumbItem {
|
||||
label: string;
|
||||
href?: string;
|
||||
}
|
||||
|
||||
interface BreadcrumbProps {
|
||||
items: BreadcrumbItem[];
|
||||
}
|
||||
|
||||
export default function Breadcrumb({ items }: BreadcrumbProps) {
|
||||
return (
|
||||
<nav className="flex items-center gap-[3px] h-5">
|
||||
{items.map((item, index) => (
|
||||
<div key={index} className="flex items-center gap-[3px]">
|
||||
<span
|
||||
className={`text-sm font-medium leading-[150%] ${
|
||||
index === items.length - 1
|
||||
? "text-text-primary font-bold"
|
||||
: "text-text-tertiary"
|
||||
}`}
|
||||
>
|
||||
{item.label}
|
||||
</span>
|
||||
{index < items.length - 1 && (
|
||||
<Image
|
||||
src="/icon-chevron-right.svg"
|
||||
alt="›"
|
||||
width={14}
|
||||
height={14}
|
||||
className="flex-shrink-0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
import Image from "next/image";
|
||||
|
||||
interface BreadcrumbItem {
|
||||
label: string;
|
||||
href?: string;
|
||||
}
|
||||
|
||||
interface BreadcrumbProps {
|
||||
items: BreadcrumbItem[];
|
||||
}
|
||||
|
||||
export default function Breadcrumb({ items }: BreadcrumbProps) {
|
||||
return (
|
||||
<nav className="flex items-center gap-[3px] h-5">
|
||||
{items.map((item, index) => (
|
||||
<div key={index} className="flex items-center gap-[3px]">
|
||||
<span
|
||||
className={`text-sm font-medium leading-[150%] ${
|
||||
index === items.length - 1
|
||||
? "text-text-primary font-bold"
|
||||
: "text-text-tertiary"
|
||||
}`}
|
||||
>
|
||||
{item.label}
|
||||
</span>
|
||||
{index < items.length - 1 && (
|
||||
<Image
|
||||
src="/icon-chevron-right.svg"
|
||||
alt="›"
|
||||
width={14}
|
||||
height={14}
|
||||
className="flex-shrink-0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import TabNavigation from "./TabNavigation";
|
||||
import OverviewTab from "./OverviewTab";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
export default function ContentSection() {
|
||||
const { t } = useApp();
|
||||
|
||||
const tabs = [
|
||||
{ id: "overview", label: t("tabs.overview") },
|
||||
{ id: "asset-description", label: t("tabs.assetDescription") },
|
||||
{ id: "analytics", label: t("tabs.analytics") },
|
||||
{ id: "performance-analysis", label: t("tabs.performanceAnalysis") },
|
||||
{ id: "asset-custody", label: t("tabs.assetCustody") },
|
||||
];
|
||||
|
||||
const [activeTab, setActiveTab] = useState("overview");
|
||||
|
||||
const handleTabChange = (tabId: string) => {
|
||||
// If clicking asset-description, performance-analysis, or asset-custody, scroll to that section
|
||||
if (tabId === "asset-description" || tabId === "performance-analysis" || tabId === "asset-custody") {
|
||||
setTimeout(() => {
|
||||
const element = document.getElementById(tabId);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
}, 100);
|
||||
// Keep active tab as overview
|
||||
setActiveTab("overview");
|
||||
} else {
|
||||
setActiveTab(tabId);
|
||||
}
|
||||
};
|
||||
|
||||
// Show OverviewTab for overview, asset-description, analytics, performance-analysis, and asset-custody
|
||||
const showOverview = ["overview", "asset-description", "analytics", "performance-analysis", "asset-custody"].includes(activeTab);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6 w-full">
|
||||
{/* Tab Navigation */}
|
||||
<TabNavigation
|
||||
tabs={tabs}
|
||||
defaultActiveId="overview"
|
||||
onTabChange={handleTabChange}
|
||||
/>
|
||||
|
||||
{/* Content Area */}
|
||||
<div className="w-full">
|
||||
{showOverview && <OverviewTab />}
|
||||
{!showOverview && (
|
||||
<div className="bg-bg-surface rounded-2xl border border-border-normal p-6">
|
||||
<p className="text-text-tertiary">
|
||||
{tabs.find((t) => t.id === activeTab)?.label} content will be
|
||||
displayed here...
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import TabNavigation from "./TabNavigation";
|
||||
import OverviewTab from "./OverviewTab";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
export default function ContentSection() {
|
||||
const { t } = useApp();
|
||||
|
||||
const tabs = [
|
||||
{ id: "overview", label: t("tabs.overview") },
|
||||
{ id: "asset-description", label: t("tabs.assetDescription") },
|
||||
{ id: "analytics", label: t("tabs.analytics") },
|
||||
{ id: "performance-analysis", label: t("tabs.performanceAnalysis") },
|
||||
{ id: "asset-custody", label: t("tabs.assetCustody") },
|
||||
];
|
||||
|
||||
const [activeTab, setActiveTab] = useState("overview");
|
||||
|
||||
const handleTabChange = (tabId: string) => {
|
||||
// If clicking asset-description, performance-analysis, or asset-custody, scroll to that section
|
||||
if (tabId === "asset-description" || tabId === "performance-analysis" || tabId === "asset-custody") {
|
||||
setTimeout(() => {
|
||||
const element = document.getElementById(tabId);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
}, 100);
|
||||
// Keep active tab as overview
|
||||
setActiveTab("overview");
|
||||
} else {
|
||||
setActiveTab(tabId);
|
||||
}
|
||||
};
|
||||
|
||||
// Show OverviewTab for overview, asset-description, analytics, performance-analysis, and asset-custody
|
||||
const showOverview = ["overview", "asset-description", "analytics", "performance-analysis", "asset-custody"].includes(activeTab);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6 w-full">
|
||||
{/* Tab Navigation */}
|
||||
<TabNavigation
|
||||
tabs={tabs}
|
||||
defaultActiveId="overview"
|
||||
onTabChange={handleTabChange}
|
||||
/>
|
||||
|
||||
{/* Content Area */}
|
||||
<div className="w-full">
|
||||
{showOverview && <OverviewTab />}
|
||||
{!showOverview && (
|
||||
<div className="bg-bg-surface rounded-2xl border border-border-normal p-6">
|
||||
<p className="text-text-tertiary">
|
||||
{tabs.find((t) => t.id === activeTab)?.label} content will be
|
||||
displayed here...
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
export default function LanguageSwitch() {
|
||||
const { language, setLanguage } = useApp();
|
||||
|
||||
const toggleLanguage = () => {
|
||||
setLanguage(language === "zh" ? "en" : "zh");
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
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"
|
||||
>
|
||||
<span className="text-sm font-medium text-text-primary dark:text-white">
|
||||
{language === "zh" ? "中" : "EN"}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
import { Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, Button } from "@heroui/react";
|
||||
|
||||
export default function LanguageSwitch() {
|
||||
const { language, setLanguage } = useApp();
|
||||
|
||||
const languages = [
|
||||
{ key: "zh", label: "中文" },
|
||||
{ key: "en", label: "English" },
|
||||
];
|
||||
|
||||
const handleSelectionChange = (key: React.Key) => {
|
||||
setLanguage(key as "zh" | "en");
|
||||
};
|
||||
|
||||
return (
|
||||
<Dropdown>
|
||||
<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";
|
||||
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
export default function MintSwapPanel() {
|
||||
const { t } = useApp();
|
||||
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">
|
||||
{/* Mint/Swap Tabs */}
|
||||
<div className="flex border-b border-border-gray dark:border-gray-700">
|
||||
<button
|
||||
onClick={() => setActiveMode("mint")}
|
||||
className={`flex-1 h-[53px] flex items-center justify-center text-body-small font-bold rounded-tl-3xl transition-colors ${
|
||||
activeMode === "mint"
|
||||
? "bg-bg-subtle dark:bg-gray-700 border-b-2 border-text-primary dark:border-blue-500 text-[#0f172b] dark:text-white"
|
||||
: "text-text-tertiary dark:text-gray-400"
|
||||
}`}
|
||||
>
|
||||
{t("mintSwap.mint")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveMode("swap")}
|
||||
className={`flex-1 h-[53px] flex items-center justify-center text-body-small font-bold transition-colors ${
|
||||
activeMode === "swap"
|
||||
? "bg-bg-subtle dark:bg-gray-700 border-b-2 border-text-primary dark:border-blue-500 text-[#0f172b] dark:text-white"
|
||||
: "text-text-tertiary dark:text-gray-400"
|
||||
}`}
|
||||
>
|
||||
{t("mintSwap.swap")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex flex-col gap-6 p-6">
|
||||
{/* Deposit/Withdraw Toggle */}
|
||||
<div className="bg-[#f9fafb] dark:bg-gray-700 rounded-xl p-1 flex gap-0">
|
||||
<button
|
||||
onClick={() => setActiveAction("deposit")}
|
||||
className={`flex-1 h-8 px-4 rounded-lg text-body-small transition-all ${
|
||||
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"
|
||||
}`}
|
||||
>
|
||||
{t("mintSwap.deposit")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveAction("withdraw")}
|
||||
className={`flex-1 h-8 px-4 rounded-lg text-body-small transition-all ${
|
||||
activeAction === "withdraw"
|
||||
? "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"
|
||||
}`}
|
||||
>
|
||||
{t("mintSwap.withdraw")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Input Area */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<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">
|
||||
{/* Label and Balance */}
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-caption-tiny font-medium text-[#4b5563] dark:text-gray-400">
|
||||
{t("mintSwap.deposit")}
|
||||
</span>
|
||||
<div className="flex items-center gap-1">
|
||||
<Image src="/icon7.svg" alt="" width={12} height={12} />
|
||||
<span className="text-caption-tiny font-medium text-[#4b5563] dark:text-gray-400">
|
||||
{t("mintSwap.balance")}: $12,500.00
|
||||
</span>
|
||||
<button className="rounded-full px-3 h-[22px] text-[10px] font-medium bg-[#e5e7eb] dark:bg-gray-600 text-[#111827] dark:text-white">
|
||||
{t("mintSwap.max")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Input Row */}
|
||||
<div className="flex items-center justify-between">
|
||||
<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="/usd-coin-usdc-logo-10.svg"
|
||||
alt="USDC"
|
||||
width={32}
|
||||
height={32}
|
||||
/>
|
||||
<span className="text-body-default font-bold text-text-primary dark:text-white">USDC</span>
|
||||
</button>
|
||||
<div className="flex flex-col items-end">
|
||||
<span className="text-heading-h3 font-bold text-[#d1d5db] dark:text-gray-500">
|
||||
0.00
|
||||
</span>
|
||||
<span className="text-caption-tiny font-regular text-text-tertiary dark:text-gray-400">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Estimated Returns */}
|
||||
<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 className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-bold text-[#4b5563] dark:text-gray-300">
|
||||
{t("mintSwap.estimatedReturns")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||
{t("mintSwap.estAPY")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-[#ff6900] dark:text-orange-400">
|
||||
22%
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||
{t("mintSwap.estReturns")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-[#10b981] dark:text-green-400">
|
||||
~ $0.50
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Transaction Summary */}
|
||||
<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 className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-bold text-[#4b5563] dark:text-gray-300">
|
||||
{t("mintSwap.transactionSummary")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||
{t("mintSwap.youGet")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-[#10b981] dark:text-green-400">
|
||||
9852.21 GYUS
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||
{t("mintSwap.salesPrice")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">$1.04 USDC</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||
{t("mintSwap.fee")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-[#dc2626] dark:text-red-400">
|
||||
-$50 (0.5%)
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||
{t("mintSwap.gas")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-[#dc2626] dark:text-red-400">
|
||||
-$0.09
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<button
|
||||
className="rounded-xl h-12 flex items-center justify-center gap-2 bg-[#9ca1af] dark:bg-gray-600"
|
||||
disabled
|
||||
>
|
||||
<span className="text-lg font-bold text-white leading-7">
|
||||
{t("mintSwap.approveDeposit")}
|
||||
</span>
|
||||
<Image src="/icon8.svg" alt="" width={20} height={20} />
|
||||
</button>
|
||||
|
||||
{/* Terms */}
|
||||
<div className="flex flex-col gap-0 text-center">
|
||||
<div className="text-caption-tiny font-regular">
|
||||
<span className="text-[#9ca1af] dark:text-gray-400">
|
||||
{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>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
import { Tabs, Tab, Button } from "@heroui/react";
|
||||
|
||||
export default function MintSwapPanel() {
|
||||
const { t } = useApp();
|
||||
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 overflow-hidden">
|
||||
{/* Mint/Swap Tabs */}
|
||||
<Tabs
|
||||
selectedKey={activeMode}
|
||||
onSelectionChange={(key) => setActiveMode(key as "mint" | "swap")}
|
||||
variant="underlined"
|
||||
classNames={{
|
||||
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]",
|
||||
tab: "h-[53px] flex-1 rounded-none data-[selected=true]:bg-bg-subtle dark:data-[selected=true]:bg-gray-700",
|
||||
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",
|
||||
}}
|
||||
>
|
||||
<Tab key="mint" title={t("mintSwap.mint")} />
|
||||
<Tab key="swap" title={t("mintSwap.swap")} />
|
||||
</Tabs>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex flex-col gap-6 p-6">
|
||||
{/* Deposit/Withdraw Toggle */}
|
||||
<Tabs
|
||||
selectedKey={activeAction}
|
||||
onSelectionChange={(key) => setActiveAction(key as "deposit" | "withdraw")}
|
||||
variant="solid"
|
||||
classNames={{
|
||||
base: "w-full",
|
||||
tabList: "bg-[#f9fafb] dark:bg-gray-700 rounded-xl p-1 gap-0 w-full",
|
||||
cursor: "bg-bg-surface dark:bg-gray-600 shadow-sm",
|
||||
tab: "h-8 px-4",
|
||||
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",
|
||||
}}
|
||||
>
|
||||
<Tab key="deposit" title={t("mintSwap.deposit")} />
|
||||
<Tab key="withdraw" title={t("mintSwap.withdraw")} />
|
||||
</Tabs>
|
||||
|
||||
{/* Input Area */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<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">
|
||||
{/* Label and Balance */}
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-caption-tiny font-medium text-[#4b5563] dark:text-gray-400">
|
||||
{t("mintSwap.deposit")}
|
||||
</span>
|
||||
<div className="flex items-center gap-1">
|
||||
<Image src="/icon7.svg" alt="" width={12} height={12} />
|
||||
<span className="text-caption-tiny font-medium text-[#4b5563] dark:text-gray-400">
|
||||
{t("mintSwap.balance")}: $12,500.00
|
||||
</span>
|
||||
<button className="rounded-full px-3 h-[22px] text-[10px] font-medium bg-[#e5e7eb] dark:bg-gray-600 text-[#111827] dark:text-white">
|
||||
{t("mintSwap.max")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Input Row */}
|
||||
<div className="flex items-center justify-between">
|
||||
<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="/usd-coin-usdc-logo-10.svg"
|
||||
alt="USDC"
|
||||
width={32}
|
||||
height={32}
|
||||
/>
|
||||
<span className="text-body-default font-bold text-text-primary dark:text-white">USDC</span>
|
||||
</button>
|
||||
<div className="flex flex-col items-end">
|
||||
<span className="text-heading-h3 font-bold text-[#d1d5db] dark:text-gray-500">
|
||||
0.00
|
||||
</span>
|
||||
<span className="text-caption-tiny font-regular text-text-tertiary dark:text-gray-400">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Estimated Returns */}
|
||||
<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 className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-bold text-[#4b5563] dark:text-gray-300">
|
||||
{t("mintSwap.estimatedReturns")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||
{t("mintSwap.estAPY")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-[#ff6900] dark:text-orange-400">
|
||||
22%
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||
{t("mintSwap.estReturns")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-[#10b981] dark:text-green-400">
|
||||
~ $0.50
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Transaction Summary */}
|
||||
<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 className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-bold text-[#4b5563] dark:text-gray-300">
|
||||
{t("mintSwap.transactionSummary")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||
{t("mintSwap.youGet")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-[#10b981] dark:text-green-400">
|
||||
9852.21 GYUS
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||
{t("mintSwap.salesPrice")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">$1.04 USDC</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||
{t("mintSwap.fee")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-[#dc2626] dark:text-red-400">
|
||||
-$50 (0.5%)
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between h-5">
|
||||
<span className="text-body-small font-regular text-text-tertiary dark:text-gray-400">
|
||||
{t("mintSwap.gas")}
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-[#dc2626] dark:text-red-400">
|
||||
-$0.09
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Submit Button */}
|
||||
<Button
|
||||
isDisabled
|
||||
className="rounded-xl h-12 bg-[#9ca1af] dark:bg-gray-600 text-lg font-bold text-white"
|
||||
endContent={<Image src="/icon8.svg" alt="" width={20} height={20} />}
|
||||
>
|
||||
{t("mintSwap.approveDeposit")}
|
||||
</Button>
|
||||
|
||||
{/* Terms */}
|
||||
<div className="flex flex-col gap-0 text-center">
|
||||
<div className="text-caption-tiny font-regular">
|
||||
<span className="text-[#9ca1af] dark:text-gray-400">
|
||||
{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";
|
||||
|
||||
interface NavItemProps {
|
||||
icon: string;
|
||||
label: string;
|
||||
isActive: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export default function NavItem({ icon, label, isActive, onClick }: NavItemProps) {
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`
|
||||
rounded-xl
|
||||
pl-4
|
||||
flex
|
||||
items-center
|
||||
gap-2
|
||||
h-[42px]
|
||||
w-full
|
||||
overflow-hidden
|
||||
transition-colors
|
||||
${isActive
|
||||
? 'bg-fill-secondary-click'
|
||||
: 'hover:bg-gray-50'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<div className="w-[22px] h-[22px] flex-shrink-0 relative">
|
||||
<Image
|
||||
src={icon}
|
||||
alt={label}
|
||||
width={22}
|
||||
height={22}
|
||||
className="w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className={`
|
||||
text-sm
|
||||
leading-[150%]
|
||||
${isActive
|
||||
? 'text-text-primary font-bold'
|
||||
: 'text-text-tertiary font-medium'
|
||||
}
|
||||
`}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
import Image from "next/image";
|
||||
|
||||
interface NavItemProps {
|
||||
icon: string;
|
||||
label: string;
|
||||
isActive: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export default function NavItem({ icon, label, isActive, onClick }: NavItemProps) {
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`
|
||||
rounded-xl
|
||||
pl-4
|
||||
flex
|
||||
items-center
|
||||
gap-2
|
||||
h-[42px]
|
||||
w-full
|
||||
overflow-hidden
|
||||
transition-colors
|
||||
${isActive
|
||||
? 'bg-fill-secondary-click'
|
||||
: 'hover:bg-gray-50'
|
||||
}
|
||||
`}
|
||||
>
|
||||
<div className="w-[22px] h-[22px] flex-shrink-0 relative">
|
||||
<Image
|
||||
src={icon}
|
||||
alt={label}
|
||||
width={22}
|
||||
height={22}
|
||||
className="w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className={`
|
||||
text-sm
|
||||
leading-[150%]
|
||||
${isActive
|
||||
? 'text-text-primary font-bold'
|
||||
: 'text-text-tertiary font-medium'
|
||||
}
|
||||
`}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
import ProductHeader from "./ProductHeader";
|
||||
import StatsCards from "./StatsCards";
|
||||
import AssetOverviewCard from "./AssetOverviewCard";
|
||||
import APYHistoryCard from "./APYHistoryCard";
|
||||
import AssetDescriptionCard from "./AssetDescriptionCard";
|
||||
import MintSwapPanel from "./MintSwapPanel";
|
||||
import ProtocolInformation from "./ProtocolInformation";
|
||||
import PerformanceAnalysis from "./PerformanceAnalysis";
|
||||
import Season1Rewards from "./Season1Rewards";
|
||||
import AssetCustodyVerification from "./AssetCustodyVerification";
|
||||
|
||||
export default function OverviewTab() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8 w-full">
|
||||
{/* Product Header */}
|
||||
<ProductHeader />
|
||||
|
||||
{/* Stats Cards */}
|
||||
<StatsCards />
|
||||
|
||||
{/* Main Content Grid */}
|
||||
<div className="grid grid-cols-3 gap-8">
|
||||
{/* Left Column - 2/3 width */}
|
||||
<div className="col-span-2 flex flex-col gap-8">
|
||||
<AssetOverviewCard />
|
||||
<APYHistoryCard />
|
||||
<div id="asset-description">
|
||||
<AssetDescriptionCard />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - 1/3 width */}
|
||||
<div className="col-span-1">
|
||||
<div className="sticky top-8 flex flex-col gap-8">
|
||||
<MintSwapPanel />
|
||||
<ProtocolInformation />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Season 1 Rewards */}
|
||||
<Season1Rewards />
|
||||
|
||||
{/* Performance Analysis */}
|
||||
<div id="performance-analysis">
|
||||
<PerformanceAnalysis />
|
||||
</div>
|
||||
|
||||
{/* Asset Custody & Verification */}
|
||||
<div id="asset-custody">
|
||||
<AssetCustodyVerification />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
import ProductHeader from "./ProductHeader";
|
||||
import StatsCards from "./StatsCards";
|
||||
import AssetOverviewCard from "./AssetOverviewCard";
|
||||
import APYHistoryCard from "./APYHistoryCard";
|
||||
import AssetDescriptionCard from "./AssetDescriptionCard";
|
||||
import MintSwapPanel from "./MintSwapPanel";
|
||||
import ProtocolInformation from "./ProtocolInformation";
|
||||
import PerformanceAnalysis from "./PerformanceAnalysis";
|
||||
import Season1Rewards from "./Season1Rewards";
|
||||
import AssetCustodyVerification from "./AssetCustodyVerification";
|
||||
|
||||
export default function OverviewTab() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8 w-full">
|
||||
{/* Product Header */}
|
||||
<ProductHeader />
|
||||
|
||||
{/* Stats Cards */}
|
||||
<StatsCards />
|
||||
|
||||
{/* Main Content Grid */}
|
||||
<div className="grid grid-cols-3 gap-8">
|
||||
{/* Left Column - 2/3 width */}
|
||||
<div className="col-span-2 flex flex-col gap-8">
|
||||
<AssetOverviewCard />
|
||||
<APYHistoryCard />
|
||||
<div id="asset-description">
|
||||
<AssetDescriptionCard />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column - 1/3 width */}
|
||||
<div className="col-span-1">
|
||||
<div className="sticky top-8 flex flex-col gap-8">
|
||||
<MintSwapPanel />
|
||||
<ProtocolInformation />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Season 1 Rewards */}
|
||||
<Season1Rewards />
|
||||
|
||||
{/* Performance Analysis */}
|
||||
<div id="performance-analysis">
|
||||
<PerformanceAnalysis />
|
||||
</div>
|
||||
|
||||
{/* Asset Custody & Verification */}
|
||||
<div id="asset-custody">
|
||||
<AssetCustodyVerification />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,207 +1,207 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
interface CalendarDayProps {
|
||||
day: number | null;
|
||||
value: string;
|
||||
type: "positive" | "negative" | "neutral" | "current";
|
||||
}
|
||||
|
||||
function CalendarDay({ day, value, type }: CalendarDayProps) {
|
||||
// Empty cell
|
||||
if (day === null) {
|
||||
return <div className="flex-1" />;
|
||||
}
|
||||
|
||||
const typeStyles = {
|
||||
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",
|
||||
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",
|
||||
};
|
||||
|
||||
const isCurrent = type === "current";
|
||||
const dayTextStyle = isCurrent
|
||||
? "text-[#fcfcfd]"
|
||||
: "text-[#9ca1af] dark:text-gray-400";
|
||||
|
||||
// Value text color should match the type
|
||||
let valueTextStyle = "";
|
||||
if (isCurrent) {
|
||||
valueTextStyle = "text-[#fcfcfd]";
|
||||
} else if (type === "positive") {
|
||||
valueTextStyle = "text-[#10b981] dark:text-green-400";
|
||||
} else if (type === "negative") {
|
||||
valueTextStyle = "text-[#dc2626] dark:text-red-400";
|
||||
} else {
|
||||
valueTextStyle = "text-[#9ca1af] dark:text-gray-400";
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
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">
|
||||
<span className={`text-[10px] font-bold leading-[150%] ${dayTextStyle}`}>
|
||||
{day}
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-full flex items-end justify-end">
|
||||
<span className={`text-body-small font-bold leading-[150%] ${valueTextStyle}`}>
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface StatCardProps {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
function StatCard({ label, value }: StatCardProps) {
|
||||
return (
|
||||
<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">
|
||||
{label}
|
||||
</span>
|
||||
<span className="text-body-large font-bold text-[#10b981] dark:text-green-400">
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function PerformanceAnalysis() {
|
||||
const { t } = useApp();
|
||||
const [currentMonth] = useState("November 2025");
|
||||
|
||||
// 模拟日历数据 - 5周数据
|
||||
const weekData = [
|
||||
[
|
||||
{ day: 31, value: "0.00%", type: "neutral" as const },
|
||||
{ day: 1, value: "+0.12%", type: "positive" as const },
|
||||
{ day: 2, value: "+0.08%", type: "positive" as const },
|
||||
{ day: 3, value: "-0.03%", type: "negative" as const },
|
||||
{ day: 4, value: "+0.15%", type: "positive" as const },
|
||||
{ day: 5, value: "+0.21%", type: "positive" as const },
|
||||
{ day: 6, value: "0.00%", type: "neutral" as const },
|
||||
],
|
||||
[
|
||||
{ day: 7, 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: 10, value: "+0.08%", type: "positive" as const },
|
||||
{ day: 11, value: "-0.03%", type: "negative" as const },
|
||||
{ day: 12, value: "+0.21%", type: "positive" as const },
|
||||
{ day: 13, value: "0.00%", type: "neutral" as const },
|
||||
],
|
||||
[
|
||||
{ day: 14, 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: 17, value: "+0.21%", type: "positive" as const },
|
||||
{ day: 18, value: "+0.08%", type: "positive" as const },
|
||||
{ day: 19, value: "0.00%", type: "neutral" as const },
|
||||
{ day: 20, value: "+0.12%", type: "positive" as const },
|
||||
],
|
||||
[
|
||||
{ day: 21, value: "+0.08%", type: "positive" as const },
|
||||
{ day: 22, value: "+0.15%", type: "positive" as const },
|
||||
{ day: 23, value: "-0.03%", type: "negative" as const },
|
||||
{ day: 24, value: "+0.12%", type: "current" as const },
|
||||
{ day: 25, value: "0.00%", type: "neutral" as const },
|
||||
{ day: 26, value: "+0.21%", type: "positive" as const },
|
||||
{ day: 27, value: "+0.08%", type: "positive" as const },
|
||||
],
|
||||
[
|
||||
{ day: 28, value: "+0.12%", type: "positive" as const },
|
||||
{ day: 30, 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 },
|
||||
],
|
||||
];
|
||||
|
||||
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">
|
||||
{/* 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 flex-col gap-2">
|
||||
<h2 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||
{t("performance.title")}
|
||||
</h2>
|
||||
<p className="text-body-small font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
{t("performance.description")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-8">
|
||||
<StatCard label={t("performance.ytd")} value="+8.7%" />
|
||||
<StatCard label={t("performance.ytd")} value="+8.7%" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Calendar Section */}
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Calendar Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-6 h-6">
|
||||
<Image src="/component-114.svg" alt="" width={24} height={24} />
|
||||
</div>
|
||||
<h3 className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
{t("performance.dailyNetReturns")}
|
||||
</h3>
|
||||
</div>
|
||||
<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">
|
||||
<Image src="/icon9.svg" alt="Previous" width={16} height={16} />
|
||||
</button>
|
||||
<span className="text-body-small font-bold text-[#0a0a0a] dark:text-white tracking-tight">
|
||||
{currentMonth}
|
||||
</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">
|
||||
<Image src="/icon10.svg" alt="Next" width={16} height={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Calendar */}
|
||||
<div className="flex flex-col gap-4">
|
||||
{/* Weekday Headers */}
|
||||
<div className="grid grid-cols-7 gap-2">
|
||||
{["sun", "mon", "tue", "wed", "thu", "fri", "sat"].map((day) => (
|
||||
<div key={day} className="flex items-center justify-center">
|
||||
<span className="text-[10px] font-bold leading-[150%] text-[#94a3b8] dark:text-gray-400">
|
||||
{t(`performance.weekdays.${day}`)}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Calendar Grid */}
|
||||
<div className="flex flex-col gap-1">
|
||||
{weekData.map((week, weekIndex) => (
|
||||
<div key={weekIndex} className="grid grid-cols-7 gap-2">
|
||||
{week.map((day, dayIndex) => (
|
||||
<CalendarDay
|
||||
key={`${weekIndex}-${dayIndex}`}
|
||||
day={day.day}
|
||||
value={day.value}
|
||||
type={day.type}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
interface CalendarDayProps {
|
||||
day: number | null;
|
||||
value: string;
|
||||
type: "positive" | "negative" | "neutral" | "current";
|
||||
}
|
||||
|
||||
function CalendarDay({ day, value, type }: CalendarDayProps) {
|
||||
// Empty cell
|
||||
if (day === null) {
|
||||
return <div className="flex-1" />;
|
||||
}
|
||||
|
||||
const typeStyles = {
|
||||
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",
|
||||
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",
|
||||
};
|
||||
|
||||
const isCurrent = type === "current";
|
||||
const dayTextStyle = isCurrent
|
||||
? "text-[#fcfcfd]"
|
||||
: "text-[#9ca1af] dark:text-gray-400";
|
||||
|
||||
// Value text color should match the type
|
||||
let valueTextStyle = "";
|
||||
if (isCurrent) {
|
||||
valueTextStyle = "text-[#fcfcfd]";
|
||||
} else if (type === "positive") {
|
||||
valueTextStyle = "text-[#10b981] dark:text-green-400";
|
||||
} else if (type === "negative") {
|
||||
valueTextStyle = "text-[#dc2626] dark:text-red-400";
|
||||
} else {
|
||||
valueTextStyle = "text-[#9ca1af] dark:text-gray-400";
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
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">
|
||||
<span className={`text-[10px] font-bold leading-[150%] ${dayTextStyle}`}>
|
||||
{day}
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-full flex items-end justify-end">
|
||||
<span className={`text-body-small font-bold leading-[150%] ${valueTextStyle}`}>
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface StatCardProps {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
function StatCard({ label, value }: StatCardProps) {
|
||||
return (
|
||||
<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">
|
||||
{label}
|
||||
</span>
|
||||
<span className="text-body-large font-bold text-[#10b981] dark:text-green-400">
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function PerformanceAnalysis() {
|
||||
const { t } = useApp();
|
||||
const [currentMonth] = useState("November 2025");
|
||||
|
||||
// 模拟日历数据 - 5周数据
|
||||
const weekData = [
|
||||
[
|
||||
{ day: 31, value: "0.00%", type: "neutral" as const },
|
||||
{ day: 1, value: "+0.12%", type: "positive" as const },
|
||||
{ day: 2, value: "+0.08%", type: "positive" as const },
|
||||
{ day: 3, value: "-0.03%", type: "negative" as const },
|
||||
{ day: 4, value: "+0.15%", type: "positive" as const },
|
||||
{ day: 5, value: "+0.21%", type: "positive" as const },
|
||||
{ day: 6, value: "0.00%", type: "neutral" as const },
|
||||
],
|
||||
[
|
||||
{ day: 7, 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: 10, value: "+0.08%", type: "positive" as const },
|
||||
{ day: 11, value: "-0.03%", type: "negative" as const },
|
||||
{ day: 12, value: "+0.21%", type: "positive" as const },
|
||||
{ day: 13, value: "0.00%", type: "neutral" as const },
|
||||
],
|
||||
[
|
||||
{ day: 14, 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: 17, value: "+0.21%", type: "positive" as const },
|
||||
{ day: 18, value: "+0.08%", type: "positive" as const },
|
||||
{ day: 19, value: "0.00%", type: "neutral" as const },
|
||||
{ day: 20, value: "+0.12%", type: "positive" as const },
|
||||
],
|
||||
[
|
||||
{ day: 21, value: "+0.08%", type: "positive" as const },
|
||||
{ day: 22, value: "+0.15%", type: "positive" as const },
|
||||
{ day: 23, value: "-0.03%", type: "negative" as const },
|
||||
{ day: 24, value: "+0.12%", type: "current" as const },
|
||||
{ day: 25, value: "0.00%", type: "neutral" as const },
|
||||
{ day: 26, value: "+0.21%", type: "positive" as const },
|
||||
{ day: 27, value: "+0.08%", type: "positive" as const },
|
||||
],
|
||||
[
|
||||
{ day: 28, value: "+0.12%", type: "positive" as const },
|
||||
{ day: 30, 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 },
|
||||
],
|
||||
];
|
||||
|
||||
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">
|
||||
{/* 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 flex-col gap-2">
|
||||
<h2 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||
{t("performance.title")}
|
||||
</h2>
|
||||
<p className="text-body-small font-regular text-[#9ca1af] dark:text-gray-400">
|
||||
{t("performance.description")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-8">
|
||||
<StatCard label={t("performance.ytd")} value="+8.7%" />
|
||||
<StatCard label={t("performance.ytd")} value="+8.7%" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Calendar Section */}
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Calendar Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-6 h-6">
|
||||
<Image src="/component-114.svg" alt="" width={24} height={24} />
|
||||
</div>
|
||||
<h3 className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
{t("performance.dailyNetReturns")}
|
||||
</h3>
|
||||
</div>
|
||||
<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">
|
||||
<Image src="/icon9.svg" alt="Previous" width={16} height={16} />
|
||||
</button>
|
||||
<span className="text-body-small font-bold text-[#0a0a0a] dark:text-white tracking-tight">
|
||||
{currentMonth}
|
||||
</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">
|
||||
<Image src="/icon10.svg" alt="Next" width={16} height={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Calendar */}
|
||||
<div className="flex flex-col gap-4">
|
||||
{/* Weekday Headers */}
|
||||
<div className="grid grid-cols-7 gap-2">
|
||||
{["sun", "mon", "tue", "wed", "thu", "fri", "sat"].map((day) => (
|
||||
<div key={day} className="flex items-center justify-center">
|
||||
<span className="text-[10px] font-bold leading-[150%] text-[#94a3b8] dark:text-gray-400">
|
||||
{t(`performance.weekdays.${day}`)}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Calendar Grid */}
|
||||
<div className="flex flex-col gap-1">
|
||||
{weekData.map((week, weekIndex) => (
|
||||
<div key={weekIndex} className="grid grid-cols-7 gap-2">
|
||||
{week.map((day, dayIndex) => (
|
||||
<CalendarDay
|
||||
key={`${weekIndex}-${dayIndex}`}
|
||||
day={day.day}
|
||||
value={day.value}
|
||||
type={day.type}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import PerformanceAnalysis from "./PerformanceAnalysis";
|
||||
|
||||
export default function PerformanceAnalysisTab() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8 w-full">
|
||||
<PerformanceAnalysis />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
import PerformanceAnalysis from "./PerformanceAnalysis";
|
||||
|
||||
export default function PerformanceAnalysisTab() {
|
||||
return (
|
||||
<div className="flex flex-col gap-8 w-full">
|
||||
<PerformanceAnalysis />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
export default function ProductHeader() {
|
||||
const { t } = useApp();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Product Title Section */}
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex gap-6">
|
||||
<div className="flex-shrink-0">
|
||||
<Image src="/lr0.svg" alt="Product Logo" width={80} height={80} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-heading-h2 font-bold text-text-primary dark:text-white">
|
||||
{t("product.gyUsEquityIndexToken")}
|
||||
</h1>
|
||||
<p className="text-body-default font-regular text-text-tertiary dark:text-gray-400">
|
||||
High-Yield US Equity Quantitative Strategy - Institutional Grade RWA
|
||||
</p>
|
||||
</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">
|
||||
<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">
|
||||
{t("product.contractAddress")}: 0x1b19...4f2c
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
export default function ProductHeader() {
|
||||
const { t } = useApp();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Product Title Section */}
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex gap-6">
|
||||
<div className="flex-shrink-0">
|
||||
<Image src="/lr0.svg" alt="Product Logo" width={80} height={80} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-heading-h2 font-bold text-text-primary dark:text-white">
|
||||
{t("product.gyUsEquityIndexToken")}
|
||||
</h1>
|
||||
<p className="text-body-default font-regular text-text-tertiary dark:text-gray-400">
|
||||
High-Yield US Equity Quantitative Strategy - Institutional Grade RWA
|
||||
</p>
|
||||
</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">
|
||||
<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">
|
||||
{t("product.contractAddress")}: 0x1b19...4f2c
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
interface ProtocolLinkProps {
|
||||
icon: string;
|
||||
label: string;
|
||||
arrowIcon: string;
|
||||
}
|
||||
|
||||
function ProtocolLink({ icon, label, arrowIcon }: ProtocolLinkProps) {
|
||||
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">
|
||||
<div className="flex items-center gap-1">
|
||||
<Image src={icon} alt="" width={20} height={24} />
|
||||
<span className="text-body-small font-medium text-text-tertiary dark:text-gray-300">
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
<Image src={arrowIcon} alt="" width={20} height={24} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ProtocolInformation() {
|
||||
const { t } = useApp();
|
||||
|
||||
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">
|
||||
<h3 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||
{t("protocol.title")}
|
||||
</h3>
|
||||
<div className="flex flex-col gap-2">
|
||||
<ProtocolLink
|
||||
icon="/component-17.svg"
|
||||
label={t("protocol.whitepaper")}
|
||||
arrowIcon="/component-18.svg"
|
||||
/>
|
||||
<ProtocolLink
|
||||
icon="/component-19.svg"
|
||||
label={t("protocol.documentation")}
|
||||
arrowIcon="/component-110.svg"
|
||||
/>
|
||||
<ProtocolLink
|
||||
icon="/component-111.svg"
|
||||
label={t("protocol.github")}
|
||||
arrowIcon="/component-112.svg"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
interface ProtocolLinkProps {
|
||||
icon: string;
|
||||
label: string;
|
||||
arrowIcon: string;
|
||||
}
|
||||
|
||||
function ProtocolLink({ icon, label, arrowIcon }: ProtocolLinkProps) {
|
||||
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">
|
||||
<div className="flex items-center gap-1">
|
||||
<Image src={icon} alt="" width={20} height={24} />
|
||||
<span className="text-body-small font-medium text-text-tertiary dark:text-gray-300">
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
<Image src={arrowIcon} alt="" width={20} height={24} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ProtocolInformation() {
|
||||
const { t } = useApp();
|
||||
|
||||
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">
|
||||
<h3 className="text-body-large font-bold text-text-primary dark:text-white">
|
||||
{t("protocol.title")}
|
||||
</h3>
|
||||
<div className="flex flex-col gap-2">
|
||||
<ProtocolLink
|
||||
icon="/component-17.svg"
|
||||
label={t("protocol.whitepaper")}
|
||||
arrowIcon="/component-18.svg"
|
||||
/>
|
||||
<ProtocolLink
|
||||
icon="/component-19.svg"
|
||||
label={t("protocol.documentation")}
|
||||
arrowIcon="/component-110.svg"
|
||||
/>
|
||||
<ProtocolLink
|
||||
icon="/component-111.svg"
|
||||
label={t("protocol.github")}
|
||||
arrowIcon="/component-112.svg"
|
||||
/>
|
||||
</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";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
interface RewardStatProps {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
function RewardStat({ label, value }: RewardStatProps) {
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-1.5">
|
||||
<span
|
||||
className="text-[24px] font-bold leading-[130%] dark:text-white"
|
||||
style={{ color: "#111827", letterSpacing: "-0.005em" }}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
<span
|
||||
className="text-[10px] font-bold uppercase leading-[150%] text-center dark:text-gray-400"
|
||||
style={{ color: "#9ca1af", letterSpacing: "0.05em" }}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Season1Rewards() {
|
||||
const { t } = useApp();
|
||||
return (
|
||||
<div
|
||||
className="rounded-3xl border flex flex-col relative overflow-hidden"
|
||||
style={{
|
||||
background:
|
||||
"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)",
|
||||
paddingTop: "20px",
|
||||
paddingBottom: "20px",
|
||||
paddingLeft: "24px",
|
||||
paddingRight: "24px",
|
||||
}}
|
||||
>
|
||||
{/* Background Decoration */}
|
||||
<div
|
||||
className="absolute"
|
||||
style={{ opacity: 0.5, right: "-15px", bottom: "-20px" }}
|
||||
>
|
||||
<Image
|
||||
src="/component-113.svg"
|
||||
alt=""
|
||||
width={120}
|
||||
height={144}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Content Container */}
|
||||
<div className="flex flex-row items-center relative z-10" style={{ gap: "400px" }}>
|
||||
{/* Left: Header and Description */}
|
||||
<div className="flex flex-col gap-2 flex-shrink-0">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-3">
|
||||
<h3
|
||||
className="text-[20px] font-bold leading-[140%] dark:text-white"
|
||||
style={{ color: "#111827" }}
|
||||
>
|
||||
{t("rewards.season1")}
|
||||
</h3>
|
||||
<div
|
||||
className="rounded-full px-2.5 py-1 flex items-center"
|
||||
style={{ backgroundColor: "#111827" }}
|
||||
>
|
||||
<span
|
||||
className="text-[10px] font-bold leading-4"
|
||||
style={{ color: "#fcfcfd" }}
|
||||
>
|
||||
{t("rewards.live")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<p
|
||||
className="text-body-small font-medium dark:text-gray-400"
|
||||
style={{ color: "#9ca1af" }}
|
||||
>
|
||||
{t("rewards.earnPoints")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Right: Stats */}
|
||||
<div className="flex items-center justify-between flex-1">
|
||||
<RewardStat label={t("rewards.yourPoints")} value="-" />
|
||||
<RewardStat label={t("rewards.badgeBoost")} value="-" />
|
||||
<RewardStat label={t("rewards.referrals")} value="-" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
interface RewardStatProps {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
function RewardStat({ label, value }: RewardStatProps) {
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-1.5">
|
||||
<span
|
||||
className="text-[24px] font-bold leading-[130%] dark:text-white"
|
||||
style={{ color: "#111827", letterSpacing: "-0.005em" }}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
<span
|
||||
className="text-[10px] font-bold uppercase leading-[150%] text-center dark:text-gray-400"
|
||||
style={{ color: "#9ca1af", letterSpacing: "0.05em" }}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Season1Rewards() {
|
||||
const { t } = useApp();
|
||||
return (
|
||||
<div
|
||||
className="rounded-3xl border flex flex-col relative overflow-hidden"
|
||||
style={{
|
||||
background:
|
||||
"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)",
|
||||
paddingTop: "20px",
|
||||
paddingBottom: "20px",
|
||||
paddingLeft: "24px",
|
||||
paddingRight: "24px",
|
||||
}}
|
||||
>
|
||||
{/* Background Decoration */}
|
||||
<div
|
||||
className="absolute"
|
||||
style={{ opacity: 0.5, right: "-15px", bottom: "-20px" }}
|
||||
>
|
||||
<Image
|
||||
src="/component-113.svg"
|
||||
alt=""
|
||||
width={120}
|
||||
height={144}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Content Container */}
|
||||
<div className="flex flex-row items-center relative z-10" style={{ gap: "400px" }}>
|
||||
{/* Left: Header and Description */}
|
||||
<div className="flex flex-col gap-2 flex-shrink-0">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-3">
|
||||
<h3
|
||||
className="text-[20px] font-bold leading-[140%] dark:text-white"
|
||||
style={{ color: "#111827" }}
|
||||
>
|
||||
{t("rewards.season1")}
|
||||
</h3>
|
||||
<div
|
||||
className="rounded-full px-2.5 py-1 flex items-center"
|
||||
style={{ backgroundColor: "#111827" }}
|
||||
>
|
||||
<span
|
||||
className="text-[10px] font-bold leading-4"
|
||||
style={{ color: "#fcfcfd" }}
|
||||
>
|
||||
{t("rewards.live")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<p
|
||||
className="text-body-small font-medium dark:text-gray-400"
|
||||
style={{ color: "#9ca1af" }}
|
||||
>
|
||||
{t("rewards.earnPoints")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Right: Stats */}
|
||||
<div className="flex items-center justify-between flex-1">
|
||||
<RewardStat label={t("rewards.yourPoints")} value="-" />
|
||||
<RewardStat label={t("rewards.badgeBoost")} value="-" />
|
||||
<RewardStat label={t("rewards.referrals")} value="-" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,80 +1,80 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useState } from "react";
|
||||
import NavItem from "./NavItem";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
export default function Sidebar() {
|
||||
const { t } = useApp();
|
||||
const [activeItem, setActiveItem] = useState("Assets");
|
||||
|
||||
const navigationItems = [
|
||||
{ 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-swap.svg", label: t("nav.swap"), key: "Swap", path: "/swap" },
|
||||
{ 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-ecosystem.svg", label: t("nav.ecosystem"), key: "Ecosystem", path: "/ecosystem" },
|
||||
{ icon: "/icon-points.svg", label: t("nav.points"), key: "Points", path: "/points" },
|
||||
];
|
||||
|
||||
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">
|
||||
{/* Logo */}
|
||||
<div className="w-full h-10">
|
||||
<Image
|
||||
src="/logo.svg"
|
||||
alt="ASSETX Logo"
|
||||
width={174}
|
||||
height={40}
|
||||
className="w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="flex flex-col gap-2 w-[174px]">
|
||||
{navigationItems.map((item) => (
|
||||
<NavItem
|
||||
key={item.key}
|
||||
icon={item.icon}
|
||||
label={item.label}
|
||||
isActive={activeItem === item.key}
|
||||
onClick={() => setActiveItem(item.key)}
|
||||
/>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* Spacer */}
|
||||
<div className="flex-1" />
|
||||
|
||||
{/* Global TVL Section */}
|
||||
<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]">
|
||||
<p className="text-text-tertiary dark:text-gray-400 text-[10px] font-medium leading-[150%] tracking-[0.01em]">
|
||||
{t("nav.globalTVL")}
|
||||
</p>
|
||||
<p className="text-text-primary dark:text-white text-base font-extrabold leading-[150%] font-jetbrains">
|
||||
$465,000,000
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* FAQs Link */}
|
||||
<div className="rounded-xl flex items-center h-[42px] mt-8">
|
||||
<div className="flex items-center gap-0">
|
||||
<Image
|
||||
src="/icon-faq.png"
|
||||
alt="FAQ"
|
||||
width={24}
|
||||
height={24}
|
||||
className="object-cover"
|
||||
/>
|
||||
<span className="text-text-primary dark:text-white text-sm font-bold leading-[150%]">
|
||||
{t("nav.faqs")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { useState } from "react";
|
||||
import NavItem from "./NavItem";
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
export default function Sidebar() {
|
||||
const { t } = useApp();
|
||||
const [activeItem, setActiveItem] = useState("Assets");
|
||||
|
||||
const navigationItems = [
|
||||
{ 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-swap.svg", label: t("nav.swap"), key: "Swap", path: "/swap" },
|
||||
{ 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-ecosystem.svg", label: t("nav.ecosystem"), key: "Ecosystem", path: "/ecosystem" },
|
||||
{ icon: "/icon-points.svg", label: t("nav.points"), key: "Points", path: "/points" },
|
||||
];
|
||||
|
||||
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">
|
||||
{/* Logo */}
|
||||
<div className="w-full h-10">
|
||||
<Image
|
||||
src="/logo.svg"
|
||||
alt="ASSETX Logo"
|
||||
width={174}
|
||||
height={40}
|
||||
className="w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="flex flex-col gap-2 w-[174px]">
|
||||
{navigationItems.map((item) => (
|
||||
<NavItem
|
||||
key={item.key}
|
||||
icon={item.icon}
|
||||
label={item.label}
|
||||
isActive={activeItem === item.key}
|
||||
onClick={() => setActiveItem(item.key)}
|
||||
/>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* Spacer */}
|
||||
<div className="flex-1" />
|
||||
|
||||
{/* Global TVL Section */}
|
||||
<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]">
|
||||
<p className="text-text-tertiary dark:text-gray-400 text-[10px] font-medium leading-[150%] tracking-[0.01em]">
|
||||
{t("nav.globalTVL")}
|
||||
</p>
|
||||
<p className="text-text-primary dark:text-white text-base font-extrabold leading-[150%] font-jetbrains">
|
||||
$465,000,000
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* FAQs Link */}
|
||||
<div className="rounded-xl flex items-center h-[42px] mt-8">
|
||||
<div className="flex items-center gap-0">
|
||||
<Image
|
||||
src="/icon-faq.png"
|
||||
alt="FAQ"
|
||||
width={24}
|
||||
height={24}
|
||||
className="object-cover"
|
||||
/>
|
||||
<span className="text-text-primary dark:text-white text-sm font-bold leading-[150%]">
|
||||
{t("nav.faqs")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
"use client";
|
||||
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
interface StatCardProps {
|
||||
label: string;
|
||||
value: string;
|
||||
change?: string;
|
||||
changeColor?: string;
|
||||
valueColor?: string;
|
||||
}
|
||||
|
||||
function StatCard({
|
||||
label,
|
||||
value,
|
||||
change,
|
||||
changeColor = "text-green-500",
|
||||
valueColor
|
||||
}: StatCardProps) {
|
||||
const getValueColor = () => {
|
||||
if (valueColor) return valueColor;
|
||||
if (label.includes("APY") || label.includes("年化")) return "#ff6900";
|
||||
return "#111827";
|
||||
};
|
||||
|
||||
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">
|
||||
<p className="text-caption-tiny font-bold text-text-tertiary dark:text-gray-400 uppercase tracking-wider">
|
||||
{label}
|
||||
</p>
|
||||
<p className="text-heading-h3 font-bold dark:text-white" style={{ color: getValueColor() }}>
|
||||
{value}
|
||||
</p>
|
||||
{change && (
|
||||
<p className={`text-caption-tiny font-medium ${changeColor} dark:text-gray-400`}>
|
||||
{change}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function StatsCards() {
|
||||
const { t } = useApp();
|
||||
|
||||
return (
|
||||
<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.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={t("stats.yourBalance")} value="0.00" change="$0.00 USD" changeColor="text-text-tertiary" />
|
||||
<StatCard
|
||||
label={t("stats.yourEarnings")}
|
||||
value="$0.00"
|
||||
change="All Time"
|
||||
changeColor="text-text-tertiary"
|
||||
valueColor="#10b981"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
interface StatCardProps {
|
||||
label: string;
|
||||
value: string;
|
||||
change?: string;
|
||||
changeColor?: string;
|
||||
valueColor?: string;
|
||||
}
|
||||
|
||||
function StatCard({
|
||||
label,
|
||||
value,
|
||||
change,
|
||||
changeColor = "text-green-500",
|
||||
valueColor
|
||||
}: StatCardProps) {
|
||||
const getValueColor = () => {
|
||||
if (valueColor) return valueColor;
|
||||
if (label.includes("APY") || label.includes("年化")) return "#ff6900";
|
||||
return "#111827";
|
||||
};
|
||||
|
||||
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">
|
||||
<p className="text-caption-tiny font-bold text-text-tertiary dark:text-gray-400 uppercase tracking-wider">
|
||||
{label}
|
||||
</p>
|
||||
<p className="text-heading-h3 font-bold dark:text-white" style={{ color: getValueColor() }}>
|
||||
{value}
|
||||
</p>
|
||||
{change && (
|
||||
<p className={`text-caption-tiny font-medium ${changeColor} dark:text-gray-400`}>
|
||||
{change}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function StatsCards() {
|
||||
const { t } = useApp();
|
||||
|
||||
return (
|
||||
<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.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={t("stats.yourBalance")} value="0.00" change="$0.00 USD" changeColor="text-text-tertiary" />
|
||||
<StatCard
|
||||
label={t("stats.yourEarnings")}
|
||||
value="$0.00"
|
||||
change="All Time"
|
||||
changeColor="text-text-tertiary"
|
||||
valueColor="#10b981"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,55 +1,43 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
interface Tab {
|
||||
id: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface TabNavigationProps {
|
||||
tabs: Tab[];
|
||||
defaultActiveId?: string;
|
||||
onTabChange?: (tabId: string) => void;
|
||||
}
|
||||
|
||||
export default function TabNavigation({
|
||||
tabs,
|
||||
defaultActiveId,
|
||||
onTabChange,
|
||||
}: TabNavigationProps) {
|
||||
const [activeTab, setActiveTab] = useState(defaultActiveId || tabs[0]?.id);
|
||||
|
||||
const handleTabClick = (tabId: string) => {
|
||||
setActiveTab(tabId);
|
||||
onTabChange?.(tabId);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-8">
|
||||
{tabs.map((tab) => {
|
||||
const isActive = activeTab === tab.id;
|
||||
return (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => handleTabClick(tab.id)}
|
||||
className="flex flex-col gap-2 items-start"
|
||||
>
|
||||
<span
|
||||
className={`text-sm font-bold leading-[150%] ${
|
||||
isActive ? "text-text-primary dark:text-white" : "text-text-tertiary dark:text-gray-400"
|
||||
}`}
|
||||
>
|
||||
{tab.label}
|
||||
</span>
|
||||
<div
|
||||
className={`self-stretch border-t-2 -mt-[2px] ${
|
||||
isActive ? "border-text-primary dark:border-white" : "border-transparent"
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import { Tabs, Tab } from "@heroui/react";
|
||||
|
||||
interface TabItem {
|
||||
id: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface TabNavigationProps {
|
||||
tabs: TabItem[];
|
||||
defaultActiveId?: string;
|
||||
onTabChange?: (tabId: string) => void;
|
||||
}
|
||||
|
||||
export default function TabNavigation({
|
||||
tabs,
|
||||
defaultActiveId,
|
||||
onTabChange,
|
||||
}: TabNavigationProps) {
|
||||
const handleSelectionChange = (key: React.Key) => {
|
||||
onTabChange?.(key.toString());
|
||||
};
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
selectedKey={defaultActiveId || tabs[0]?.id}
|
||||
onSelectionChange={handleSelectionChange}
|
||||
variant="underlined"
|
||||
classNames={{
|
||||
base: "w-auto",
|
||||
tabList: "gap-8 w-auto p-0",
|
||||
cursor: "bg-text-primary dark:bg-white",
|
||||
tab: "px-0 h-auto",
|
||||
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",
|
||||
}}
|
||||
>
|
||||
{tabs.map((tab) => (
|
||||
<Tab key={tab.id} title={tab.label} />
|
||||
))}
|
||||
</Tabs>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,46 +1,53 @@
|
||||
"use client";
|
||||
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
|
||||
export default function ThemeSwitch() {
|
||||
const { theme, setTheme } = useApp();
|
||||
|
||||
const toggleTheme = () => {
|
||||
setTheme(theme === "light" ? "dark" : "light");
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
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"
|
||||
>
|
||||
{theme === "light" ? (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path
|
||||
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"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
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="#111827"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path
|
||||
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="#111827"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import { useApp } from "@/contexts/AppContext";
|
||||
import { Button } from "@heroui/react";
|
||||
|
||||
export default function ThemeSwitch() {
|
||||
const { theme, setTheme } = useApp();
|
||||
|
||||
const toggleTheme = () => {
|
||||
setTheme(theme === "light" ? "dark" : "light");
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
isIconOnly
|
||||
variant="bordered"
|
||||
onPress={toggleTheme}
|
||||
className="bg-bg-surface dark:bg-gray-800 border-border-normal dark:border-gray-700 min-w-10 h-10 rounded-lg"
|
||||
aria-label={`Switch to ${theme === "light" ? "dark" : "light"} mode`}
|
||||
>
|
||||
{theme === "light" ? (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path
|
||||
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"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="text-text-primary dark:text-white"
|
||||
/>
|
||||
<path
|
||||
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"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
className="text-text-primary dark:text-white"
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path
|
||||
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"
|
||||
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";
|
||||
import Breadcrumb from "./Breadcrumb";
|
||||
import LanguageSwitch from "./LanguageSwitch";
|
||||
import ThemeSwitch from "./ThemeSwitch";
|
||||
|
||||
export default function TopBar() {
|
||||
const breadcrumbItems = [
|
||||
{ label: "ASSETX" },
|
||||
{ label: "Product" },
|
||||
{ label: "Detail" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between w-full">
|
||||
{/* Left: Breadcrumb */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Breadcrumb items={breadcrumbItems} />
|
||||
</div>
|
||||
|
||||
{/* Right: Actions */}
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Language Switch */}
|
||||
<LanguageSwitch />
|
||||
|
||||
{/* Theme Switch */}
|
||||
<ThemeSwitch />
|
||||
|
||||
{/* Wallet Button */}
|
||||
<button className="bg-bg-surface rounded-lg border border-border-normal px-2 py-2 flex items-center justify-center h-10">
|
||||
<Image src="/icon-wallet.svg" alt="Wallet" width={20} height={20} />
|
||||
<Image
|
||||
src="/icon-notification.svg"
|
||||
alt="Notification"
|
||||
width={14}
|
||||
height={14}
|
||||
className="ml-1"
|
||||
/>
|
||||
</button>
|
||||
|
||||
{/* Address Button */}
|
||||
<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">
|
||||
<Image src="/icon-copy.svg" alt="Copy" width={16} height={16} />
|
||||
<span className="text-white text-sm font-bold font-jetbrains">
|
||||
0x12...4F82
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { Button } from "@heroui/react";
|
||||
import Breadcrumb from "./Breadcrumb";
|
||||
import LanguageSwitch from "./LanguageSwitch";
|
||||
import ThemeSwitch from "./ThemeSwitch";
|
||||
|
||||
export default function TopBar() {
|
||||
const breadcrumbItems = [
|
||||
{ label: "ASSETX" },
|
||||
{ label: "Product" },
|
||||
{ label: "Detail" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between w-full">
|
||||
{/* Left: Breadcrumb */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Breadcrumb items={breadcrumbItems} />
|
||||
</div>
|
||||
|
||||
{/* Right: Actions */}
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Language Switch */}
|
||||
<LanguageSwitch />
|
||||
|
||||
{/* Theme Switch */}
|
||||
<ThemeSwitch />
|
||||
|
||||
{/* Wallet Button */}
|
||||
<Button
|
||||
isIconOnly
|
||||
variant="bordered"
|
||||
className="bg-bg-surface border-border-normal min-w-10 h-10 rounded-lg"
|
||||
>
|
||||
<div className="flex items-center justify-center">
|
||||
<Image src="/icon-wallet.svg" alt="Wallet" width={20} height={20} />
|
||||
<Image
|
||||
src="/icon-notification.svg"
|
||||
alt="Notification"
|
||||
width={14}
|
||||
height={14}
|
||||
className="ml-1"
|
||||
/>
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
{/* 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",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev -H 0.0.0.0",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "^15.1.4",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "^15.1.4",
|
||||
"postcss": "^8.4.49",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "asset-dashboard-next",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev -H 0.0.0.0",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroui/react": "^2.8.8",
|
||||
"@heroui/theme": "^2.4.26",
|
||||
"framer-motion": "^12.29.2",
|
||||
"next": "^15.1.4",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^8",
|
||||
"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";
|
||||
|
||||
export default {
|
||||
darkMode: "class",
|
||||
content: [
|
||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
background: "var(--background)",
|
||||
foreground: "var(--foreground)",
|
||||
'bg-subtle': '#f9fafb',
|
||||
'bg-surface': '#ffffff',
|
||||
'border-normal': '#e5e7eb',
|
||||
'border-gray': '#f3f4f6',
|
||||
'text-primary': '#111827',
|
||||
'text-tertiary': '#9ca1af',
|
||||
'fill-secondary-click': '#f3f4f6',
|
||||
},
|
||||
fontFamily: {
|
||||
inter: ['var(--font-inter)', 'Inter', 'sans-serif'],
|
||||
jetbrains: ['var(--font-jetbrains)', 'JetBrains Mono', 'monospace'],
|
||||
},
|
||||
fontSize: {
|
||||
'caption-tiny': ['12px', { lineHeight: '150%', letterSpacing: '0.01em' }],
|
||||
'body-small': ['14px', { lineHeight: '150%' }],
|
||||
'body-default': ['16px', { lineHeight: '150%' }],
|
||||
'body-large': ['18px', { lineHeight: '150%' }],
|
||||
'heading-h3': ['24px', { lineHeight: '130%', letterSpacing: '-0.005em' }],
|
||||
'heading-h2': ['32px', { lineHeight: '120%', letterSpacing: '-0.01em' }],
|
||||
},
|
||||
fontWeight: {
|
||||
regular: '400',
|
||||
medium: '500',
|
||||
bold: '700',
|
||||
extrabold: '800',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
} satisfies Config;
|
||||
import type { Config } from "tailwindcss";
|
||||
import { heroui } from "@heroui/theme";
|
||||
|
||||
export default {
|
||||
darkMode: "class",
|
||||
content: [
|
||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
background: "var(--background)",
|
||||
foreground: "var(--foreground)",
|
||||
'bg-subtle': '#f9fafb',
|
||||
'bg-surface': '#ffffff',
|
||||
'border-normal': '#e5e7eb',
|
||||
'border-gray': '#f3f4f6',
|
||||
'text-primary': '#111827',
|
||||
'text-tertiary': '#9ca1af',
|
||||
'fill-secondary-click': '#f3f4f6',
|
||||
},
|
||||
fontFamily: {
|
||||
inter: ['var(--font-inter)', 'Inter', 'sans-serif'],
|
||||
jetbrains: ['var(--font-jetbrains)', 'JetBrains Mono', 'monospace'],
|
||||
},
|
||||
fontSize: {
|
||||
'caption-tiny': ['12px', { lineHeight: '150%', letterSpacing: '0.01em' }],
|
||||
'body-small': ['14px', { lineHeight: '150%' }],
|
||||
'body-default': ['16px', { lineHeight: '150%' }],
|
||||
'body-large': ['18px', { lineHeight: '150%' }],
|
||||
'heading-h3': ['24px', { lineHeight: '130%', letterSpacing: '-0.005em' }],
|
||||
'heading-h2': ['32px', { lineHeight: '120%', letterSpacing: '-0.01em' }],
|
||||
},
|
||||
fontWeight: {
|
||||
regular: '400',
|
||||
medium: '500',
|
||||
bold: '700',
|
||||
extrabold: '800',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [heroui()],
|
||||
} satisfies Config;
|
||||
|
||||
Reference in New Issue
Block a user