feat(chat): render role-bound payment guidance

This commit is contained in:
Codex
2026-07-27 18:14:04 +08:00
parent 662e5e67ea
commit ca9cac21f1
49 changed files with 882 additions and 81 deletions
@@ -17,11 +17,13 @@
* - 不直接管理 state machinechat 机器不感知 UI 层)
*/
import { useAppNavigator } from "@/router/use-app-navigator";
import type { PaymentGuidance } from "@/data/schemas/chat";
export interface ChatInsufficientCreditsBannerProps {
title?: string;
description?: string;
ctaLabel?: string;
ctaLabel?: string | null;
guidance?: PaymentGuidance | null;
/**
* 自定义点击回调(不传则默认走统一订阅导航)
* - 测试时可传 mock
@@ -34,9 +36,11 @@ export function ChatInsufficientCreditsBanner({
title = "Insufficient credits\nTop up to continue chatting",
description,
ctaLabel = "Top up credits to continue",
guidance = null,
onUnlock,
}: ChatInsufficientCreditsBannerProps) {
const navigator = useAppNavigator();
const activeGuidance = guidance;
const titleLines = title.split("\n");
const handleClick = () => {
@@ -53,6 +57,7 @@ export function ChatInsufficientCreditsBanner({
role="status"
aria-live="polite"
data-testid="chat-insufficient-credits-banner"
data-payment-guidance-id={activeGuidance?.guidanceId}
>
<p className="m-0 mb-(--spacing-sm,8px) text-(length:--responsive-card-title,18px) font-semibold leading-[1.4] text-white opacity-95">
{titleLines.map((line, index) => (
@@ -67,7 +72,12 @@ export function ChatInsufficientCreditsBanner({
{description}
</p>
) : null}
<button
{activeGuidance?.copy ? (
<p className="mx-auto mb-(--spacing-lg,16px) mt-0 max-w-[min(100%,360px)] text-(length:--responsive-caption,14px) font-semibold leading-[1.4] text-white">
{activeGuidance.copy}
</p>
) : null}
{ctaLabel ? <button
type="button"
data-analytics-key="chat.topup"
data-analytics-label="Top up chat credits"
@@ -76,7 +86,7 @@ export function ChatInsufficientCreditsBanner({
aria-label={ctaLabel}
>
{ctaLabel}
</button>
</button> : null}
</div>
);
}