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>
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { Button } from "@heroui/react";
|
||||
|
||||
interface ProductCardProps {
|
||||
name: string;
|
||||
category: string;
|
||||
icon?: string;
|
||||
categoryColor: "blue" | "green" | "purple";
|
||||
iconType: "us-flag-1" | "hk-flag" | "us-flag-2";
|
||||
yieldAPY: string;
|
||||
poolCap: string;
|
||||
maturity: string;
|
||||
@@ -21,7 +21,8 @@ interface ProductCardProps {
|
||||
export default function ProductCard({
|
||||
name,
|
||||
category,
|
||||
icon,
|
||||
categoryColor,
|
||||
iconType,
|
||||
yieldAPY,
|
||||
poolCap,
|
||||
maturity,
|
||||
@@ -32,153 +33,196 @@ export default function ProductCard({
|
||||
poolCapacityPercent,
|
||||
onInvest,
|
||||
}: ProductCardProps) {
|
||||
const getRiskColor = (level: number) => {
|
||||
switch (level) {
|
||||
case 1:
|
||||
return "bg-green-500";
|
||||
case 2:
|
||||
return "bg-yellow-500";
|
||||
case 3:
|
||||
return "bg-red-500";
|
||||
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-gray-300";
|
||||
return "bg-[rgba(59,130,246,0.1)] border-[rgba(59,130,246,0.3)]";
|
||||
}
|
||||
};
|
||||
|
||||
const getCategoryColor = (cat: string) => {
|
||||
if (cat.includes("Quant")) {
|
||||
return "bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800 text-blue-700 dark:text-blue-300";
|
||||
} else if (cat.includes("Real Estate")) {
|
||||
return "bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800 text-green-700 dark:text-green-300";
|
||||
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 "bg-bg-subtle dark:bg-gray-700 border-border-normal dark:border-gray-600 text-text-tertiary dark:text-gray-400";
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-bg-surface dark:bg-gray-800 rounded-2xl border border-border-gray dark:border-gray-700 p-6 flex flex-col gap-6 hover:shadow-lg transition-shadow">
|
||||
<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="flex items-start gap-4">
|
||||
<div className="w-16 h-16 rounded-full bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center flex-shrink-0">
|
||||
{icon ? (
|
||||
<Image src={icon} alt={name} width={32} height={32} />
|
||||
) : (
|
||||
<span className="text-2xl">🏛️</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 flex-1">
|
||||
<h3 className="text-body-large font-bold text-text-primary dark:text-white leading-tight">
|
||||
{name}
|
||||
</h3>
|
||||
<div className="pb-6 border-b-0">
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Icon Container */}
|
||||
<div
|
||||
className={`inline-flex self-start px-3 py-1 rounded-full border text-caption-tiny font-medium ${getCategoryColor(
|
||||
category
|
||||
)}`}
|
||||
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)",
|
||||
}}
|
||||
>
|
||||
{category}
|
||||
<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>
|
||||
|
||||
{/* Product Metrics - Two Columns */}
|
||||
<div className="grid grid-cols-2 gap-x-8 gap-y-4">
|
||||
{/* Column 1 */}
|
||||
<div className="flex flex-col gap-4">
|
||||
{/* 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-body-small text-text-tertiary dark:text-gray-400">
|
||||
Yield APY
|
||||
</span>
|
||||
<span className="text-body-large font-bold text-green-600 dark:text-green-400">
|
||||
{yieldAPY}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-body-small text-text-tertiary dark:text-gray-400">
|
||||
<span className="text-sm text-[#6b7280] dark:text-gray-400">
|
||||
Maturity
|
||||
</span>
|
||||
<span className="text-body-default font-medium text-text-primary dark:text-white">
|
||||
<span className="text-sm font-medium text-[#111827] dark:text-white">
|
||||
{maturity}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-body-small text-text-tertiary dark:text-gray-400">
|
||||
<span className="text-sm text-[#6b7280] dark:text-gray-400">
|
||||
Lock-Up
|
||||
</span>
|
||||
<span className="text-body-default font-medium text-text-primary dark:text-white">
|
||||
<span className="text-sm font-medium text-[#111827] dark:text-white">
|
||||
{lockUp}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Column 2 */}
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-4 flex-1">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-body-small text-text-tertiary dark:text-gray-400">
|
||||
Pool CaP
|
||||
</span>
|
||||
<span className="text-body-large font-bold text-text-primary dark:text-white">
|
||||
{poolCap}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-body-small text-text-tertiary dark:text-gray-400">
|
||||
<span className="text-sm text-[#6b7280] dark:text-gray-400">
|
||||
Risk
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-body-default font-medium text-text-primary dark:text-white">
|
||||
<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.5 h-4 rounded-sm transition-colors ${
|
||||
level <= riskLevel
|
||||
? getRiskColor(riskLevel)
|
||||
: "bg-gray-300 dark:bg-gray-600"
|
||||
}`}
|
||||
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-body-small text-text-tertiary dark:text-gray-400">
|
||||
<span className="text-sm text-[#6b7280] dark:text-gray-400">
|
||||
Circulating supply
|
||||
</span>
|
||||
<span className="text-body-default font-medium text-text-primary dark:text-white">
|
||||
<span className="text-sm font-medium text-[#111827] dark:text-white">
|
||||
{circulatingSupply}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="border-t border-border-gray dark:border-gray-700" />
|
||||
|
||||
{/* Pool Capacity & Invest Button */}
|
||||
<div className="flex items-end justify-between gap-6">
|
||||
<div className="flex-1 flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-body-small text-text-tertiary dark:text-gray-400">
|
||||
Pool Capacity
|
||||
</span>
|
||||
<span className="text-body-small font-bold text-text-primary dark:text-white">
|
||||
{poolCapacityPercent}% Filled
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-full h-2.5 bg-bg-subtle dark:bg-gray-700 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-gradient-to-r from-blue-500 to-purple-600 rounded-full transition-all duration-300"
|
||||
style={{ width: `${poolCapacityPercent}%` }}
|
||||
/>
|
||||
{/* 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>
|
||||
<Button
|
||||
onPress={onInvest}
|
||||
className="px-8 py-3 bg-text-primary dark:bg-blue-600 text-white font-bold text-body-default rounded-xl h-auto min-w-[120px]"
|
||||
>
|
||||
Invest
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user