Files
assetx/components/ProductCard.tsx
default b98500e61f feat: refactor Product page to match prototype design
Redesigned ProductCard and Product page based on original prototype:

ProductCard Component:
- Added gradient background matching prototype style
- Copied all SVG icons from prototype (US flags, HK flag, etc.)
- Implemented proper icon display with white background container
- Added category color variants (blue, green, purple)
- Risk level indicators with accurate colors
- Styled progress bar matching prototype
- Proper spacing and typography from CSS

Visual Elements:
- Radial gradient backgrounds on cards
- Glass-morphism effect with transparency
- Shadow and border effects from prototype
- Icon container with white background and shadow ring
- Category badges with colored backgrounds

Assets Added:
- frame-9230.svg, frame-9231.svg (US flag icons)
- hk0.svg (HK flag icon)
- chart-square1.svg
- All component SVG files from prototype
- View toggle icons

Styling:
- Followed prototype's style.css specifications
- Maintained exact color values and gradients
- Proper border radius and spacing
- Typography matching prototype

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-30 05:35:08 +00:00

230 lines
7.4 KiB
TypeScript

"use client";
import Image from "next/image";
interface ProductCardProps {
name: string;
category: string;
categoryColor: "blue" | "green" | "purple";
iconType: "us-flag-1" | "hk-flag" | "us-flag-2";
yieldAPY: string;
poolCap: string;
maturity: string;
risk: string;
riskLevel: 1 | 2 | 3;
lockUp: string;
circulatingSupply: string;
poolCapacityPercent: number;
onInvest?: () => void;
}
export default function ProductCard({
name,
category,
categoryColor,
iconType,
yieldAPY,
poolCap,
maturity,
risk,
riskLevel,
lockUp,
circulatingSupply,
poolCapacityPercent,
onInvest,
}: ProductCardProps) {
const getCategoryStyle = () => {
switch (categoryColor) {
case "blue":
return "bg-[rgba(59,130,246,0.1)] border-[rgba(59,130,246,0.3)]";
case "green":
return "bg-[rgba(34,197,94,0.1)] border-[rgba(34,197,94,0.3)]";
case "purple":
return "bg-[rgba(168,85,247,0.1)] border-[rgba(168,85,247,0.3)]";
default:
return "bg-[rgba(59,130,246,0.1)] border-[rgba(59,130,246,0.3)]";
}
};
const getRiskBarColor = () => {
switch (riskLevel) {
case 1:
return "#10b981"; // green
case 2:
return "#f59e0b"; // yellow/orange
case 3:
return "#ef4444"; // red
default:
return "#9ca3af";
}
};
const getIconSrc = () => {
switch (iconType) {
case "us-flag-1":
return "/frame-9230.svg";
case "hk-flag":
return "/hk0.svg";
case "us-flag-2":
return "/frame-9231.svg";
default:
return "/frame-9230.svg";
}
};
return (
<div
className="rounded-3xl border border-[rgba(255,255,255,0.6)] p-6 flex-1 relative overflow-hidden"
style={{
background: `
radial-gradient(closest-side, rgba(186, 230, 253, 0.15) 0%, rgba(0, 0, 0, 0) 50%),
radial-gradient(closest-side, rgba(199, 210, 254, 0.15) 0%, rgba(0, 0, 0, 0) 50%),
linear-gradient(to left, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.4))
`,
boxShadow: "0px 8px 32px 0px rgba(0, 0, 0, 0.03)",
}}
>
{/* Product Header */}
<div className="pb-6 border-b-0">
<div className="flex items-center gap-4">
{/* Icon Container */}
<div
className="flex items-center justify-center w-16 h-16 rounded-2xl border border-[rgba(255,255,255,0.8)]"
style={{
background: "rgba(255, 255, 255, 0.4)",
boxShadow: "0px 0px 0px 4px rgba(255, 255, 255, 0.1)",
}}
>
<div className="relative w-12 h-12">
<Image
src={getIconSrc()}
alt={name}
width={48}
height={48}
className="w-full h-full object-contain"
/>
</div>
</div>
{/* Title and Category */}
<div className="flex flex-col gap-0">
<h3 className="text-lg font-bold text-[#111827] dark:text-white leading-[150%]">
{name}
</h3>
<div
className={`inline-flex self-start px-3 py-1 rounded-full border mt-1 ${getCategoryStyle()}`}
>
<span className="text-xs font-medium text-[#111827] dark:text-white">
{category}
</span>
</div>
</div>
</div>
</div>
{/* Metrics Section */}
<div className="py-6 flex gap-8">
<div className="flex flex-col gap-1">
<span className="text-sm text-[#6b7280] dark:text-gray-400">
Yield APY
</span>
<span className="text-base font-bold text-[#111827] dark:text-white">
{yieldAPY}
</span>
</div>
<div className="flex flex-col gap-1">
<span className="text-sm text-[#6b7280] dark:text-gray-400">
Pool CaP
</span>
<span className="text-base font-bold text-[#111827] dark:text-white">
{poolCap}
</span>
</div>
</div>
{/* Details Grid */}
<div className="flex gap-6 pb-6">
<div className="flex flex-col gap-4 flex-1">
<div className="flex flex-col gap-1">
<span className="text-sm text-[#6b7280] dark:text-gray-400">
Maturity
</span>
<span className="text-sm font-medium text-[#111827] dark:text-white">
{maturity}
</span>
</div>
<div className="flex flex-col gap-1">
<span className="text-sm text-[#6b7280] dark:text-gray-400">
Lock-Up
</span>
<span className="text-sm font-medium text-[#111827] dark:text-white">
{lockUp}
</span>
</div>
</div>
<div className="flex flex-col gap-4 flex-1">
<div className="flex flex-col gap-1">
<span className="text-sm text-[#6b7280] dark:text-gray-400">
Risk
</span>
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-[#111827] dark:text-white">
{risk}
</span>
<div className="flex items-center gap-1">
{[1, 2, 3].map((level) => (
<div
key={level}
className="w-1 h-3 rounded-sm"
style={{
backgroundColor:
level <= riskLevel ? getRiskBarColor() : "#d1d5db",
}}
/>
))}
</div>
</div>
</div>
<div className="flex flex-col gap-1">
<span className="text-sm text-[#6b7280] dark:text-gray-400">
Circulating supply
</span>
<span className="text-sm font-medium text-[#111827] dark:text-white">
{circulatingSupply}
</span>
</div>
</div>
</div>
{/* Pool Capacity & Button */}
<div className="pt-6 border-t border-[rgba(0,0,0,0.05)]">
<div className="flex items-end gap-6">
<div className="flex-1 flex flex-col gap-2">
<div className="flex items-center justify-between">
<span className="text-sm text-[#6b7280] dark:text-gray-400">
Pool Capacity
</span>
<span className="text-sm font-medium text-[#111827] dark:text-white">
{poolCapacityPercent}% Filled
</span>
</div>
<div className="w-full h-2 bg-[#e5e7eb] dark:bg-gray-700 rounded-full overflow-hidden">
<div
className="h-full bg-[#111827] dark:bg-blue-500 rounded-full transition-all"
style={{ width: `${poolCapacityPercent}%` }}
/>
</div>
</div>
<button
onClick={onInvest}
className="px-6 py-3 bg-[#111827] dark:bg-blue-600 text-white font-bold text-sm rounded-xl hover:bg-gray-800 dark:hover:bg-blue-700 transition-colors whitespace-nowrap"
>
Invest
</button>
</div>
</div>
</div>
);
}