Files
assetx/components/PerformanceAnalysis.tsx
default 9e0dd1d278 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>
2026-01-30 03:49:53 +00:00

208 lines
8.0 KiB
TypeScript

"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>
);
}