feat(subscription): add VIP subscription page
Add a new /subscription route showing three VIP membership plans (Monthly, Quarterly, Annual) with a pink→peach banner, per-plan gradient price bars, a benefits card, a mock activate CTA, and an agreement checkbox. - New reusable <Checkbox /> in src/app/_components/core for the legal agreement toggle - Hardcoded SUBSCRIPTION_PLANS constant in src/data/constants - New SubscriptionScreen composed of 7 colocated components (back link, user row, banner, plan card, benefits card, CTA button) with CSS Modules matching the existing design-token system - Route registered as protected and reached from /sidebar "Membership" - Mock CTA: window.alert + router.push(/chat) — no payment calls Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"use client";
|
||||
/**
|
||||
* 订阅套餐卡片
|
||||
*
|
||||
* 视觉规格(与设计稿对齐):
|
||||
* - 卡片顶部:套餐名(12px / secondary)
|
||||
* - 中部:大字价格(28px / 300)+ 划线原价(12px / line-through / 999)
|
||||
* - 底部 28px 高渐变条:白字 12px/600 每日折算价
|
||||
* - 选中/高亮:2px 粉色边框 + 浅粉阴影
|
||||
* - 三个卡片底部渐变不同:粉 → 紫红 → 紫
|
||||
*/
|
||||
import type { SubscriptionPlan, SubscriptionPlanId } from "@/data/constants/subscription-plans";
|
||||
|
||||
import styles from "./subscription-plan-card.module.css";
|
||||
|
||||
const PER_DAY_GRADIENTS: Record<SubscriptionPlanId, string> = {
|
||||
monthly: "linear-gradient(90deg, #ff7ab8 0%, #ff5d9c 100%)",
|
||||
quarterly: "linear-gradient(90deg, #d16bff 0%, #ff5da3 100%)",
|
||||
annual: "linear-gradient(90deg, #9a4dff 0%, #c44dff 100%)",
|
||||
};
|
||||
|
||||
export interface SubscriptionPlanCardProps {
|
||||
plan: SubscriptionPlan;
|
||||
selected: boolean;
|
||||
onClick: () => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function SubscriptionPlanCard({
|
||||
plan,
|
||||
selected,
|
||||
onClick,
|
||||
className,
|
||||
}: SubscriptionPlanCardProps) {
|
||||
const classes = [
|
||||
styles.card,
|
||||
selected ? styles.selected : "",
|
||||
!selected && plan.highlight ? styles.highlight : "",
|
||||
className,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
|
||||
const perDayStyle = { background: PER_DAY_GRADIENTS[plan.id] };
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
aria-pressed={selected}
|
||||
aria-label={`${plan.name} plan, $${plan.price}`}
|
||||
className={classes}
|
||||
>
|
||||
<p className={styles.name}>{plan.name}</p>
|
||||
<div className={styles.priceBlock}>
|
||||
<span className={styles.currency}>$</span>
|
||||
<span className={styles.price}>{plan.price}</span>
|
||||
</div>
|
||||
<p className={styles.originalPrice}>${plan.originalPrice}</p>
|
||||
<div className={styles.perDayBar} style={perDayStyle}>
|
||||
{plan.perDay}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user