Files
cozsweet-frontend-nextjs/src/app/chat/components/chat-quota-exhausted-banner.tsx
T

80 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
/**
* ChatQuotaExhaustedBanner 游客配额耗尽横幅
*
* 何时显示:
* - 后端返回 lockDetail.reason="daily_limit" 且 showUpgrade=true
*
* 视觉规格(与设计稿对齐):
* - 粉→品红渐变背景(与 splash 渐变一致)
* - 大字号粉色/白色提示文案("The limit for free chat times has been reached today"
* - 粉→品红渐变 pill 按钮"Unlock your membership to continue" → 跳 /subscription
*
* 业务关系:
* - 与 `src/app/sidebar/components/vip-benefits-card.tsx` 的 "Activate VIP Membership" 行为一致
* - 与 `src/app/sidebar/components/voice-package-card.tsx` 的 "Buy Package" 行为一致
*
* 不做的事:
* - 不直接管理 state machinechat 机器不感知 UI 层)
* - 不显示具体剩余分钟数(已用 total<=0 表达"耗尽"足够)
*/
import { useRouter } from "next/navigation";
import { ROUTES } from "@/router/routes";
import styles from "./chat-quota-exhausted-banner.module.css";
export interface ChatQuotaExhaustedBannerProps {
title?: string;
ctaLabel?: string;
/**
* 自定义点击回调(不传则默认 router.push(ROUTES.subscription)
* - 测试时可传 mock
* - 嵌入其他场景时(如 nested overlay)可传自定义
*/
onUnlock?: () => void;
}
export function ChatQuotaExhaustedBanner({
title = "The limit for free chat times\nhas been reached today",
ctaLabel = "Unlock your membership to continue",
onUnlock,
}: ChatQuotaExhaustedBannerProps) {
const router = useRouter();
const titleLines = title.split("\n");
const handleClick = () => {
if (onUnlock) {
onUnlock();
return;
}
router.push(`${ROUTES.subscription}?type=vip`);
};
return (
<div
className={styles.banner}
role="status"
aria-live="polite"
data-testid="chat-quota-exhausted-banner"
>
<p className={styles.text}>
{titleLines.map((line, index) => (
<span key={`${line}-${index}`}>
{line}
{index < titleLines.length - 1 ? <br /> : null}
</span>
))}
</p>
<button
type="button"
className={styles.cta}
onClick={handleClick}
aria-label={ctaLabel}
>
{ctaLabel}
</button>
</div>
);
}