- 新增充值账单任务兑换码共享组件并补齐组件级测试 - 让 RpgEntryHomeView 改为复用新的 profile 弹层组件并删除内联实现 - 更新 PlatformUiKit 收口文档与团队共享记忆记录新的组件沉淀
144 lines
4.7 KiB
TypeScript
144 lines
4.7 KiB
TypeScript
import type { ProfileTaskCenterResponse } from '../../../packages/shared/src/contracts/runtime';
|
||
import { PlatformActionButton } from '../common/PlatformActionButton';
|
||
import { PlatformEmptyState } from '../common/PlatformEmptyState';
|
||
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
|
||
import { PlatformSubpanel } from '../common/PlatformSubpanel';
|
||
import { PlatformProfileModalShell } from './PlatformProfileModalShell';
|
||
import {
|
||
buildProfileTaskProgressLabel,
|
||
getProfileTaskClaimButtonLabel,
|
||
getProfileTaskStatusLabel,
|
||
selectProfileTaskCenterTasks,
|
||
} from '../rpg-entry/rpgEntryProfileTaskViewModel';
|
||
|
||
export type PlatformProfileTaskCenterModalProps = {
|
||
center: ProfileTaskCenterResponse | null;
|
||
isLoading: boolean;
|
||
error: string | null;
|
||
success: string | null;
|
||
claimingTaskId: string | null;
|
||
fallbackBalance: number;
|
||
onClose: () => void;
|
||
onRetry: () => void;
|
||
onClaim: (taskId: string) => void;
|
||
};
|
||
|
||
/**
|
||
* 个人中心每日任务弹窗。
|
||
* 复用任务中心 view model,保持原有任务筛选、状态文案和领取交互不变。
|
||
*/
|
||
export function PlatformProfileTaskCenterModal({
|
||
center,
|
||
isLoading,
|
||
error,
|
||
success,
|
||
claimingTaskId,
|
||
fallbackBalance,
|
||
onClose,
|
||
onRetry,
|
||
onClaim,
|
||
}: PlatformProfileTaskCenterModalProps) {
|
||
const tasks = selectProfileTaskCenterTasks(center?.tasks ?? []);
|
||
const walletBalance = center?.walletBalance ?? fallbackBalance;
|
||
|
||
return (
|
||
<PlatformProfileModalShell
|
||
title="每日任务"
|
||
description={`${walletBalance}泥点`}
|
||
onClose={onClose}
|
||
closeLabel="关闭每日任务"
|
||
panelClassName="platform-recharge-modal !max-w-md rounded-[1.4rem]"
|
||
bodyClassName="space-y-3 px-5 py-5"
|
||
>
|
||
{error ? (
|
||
<PlatformStatusMessage
|
||
tone="error"
|
||
surface="profile"
|
||
size="xs"
|
||
className="rounded-2xl font-semibold"
|
||
>
|
||
<div>{error}</div>
|
||
<PlatformActionButton
|
||
surface="profile"
|
||
size="xs"
|
||
className="mt-3"
|
||
onClick={onRetry}
|
||
>
|
||
重新加载
|
||
</PlatformActionButton>
|
||
</PlatformStatusMessage>
|
||
) : null}
|
||
{success ? (
|
||
<PlatformStatusMessage
|
||
tone="success"
|
||
surface="profile"
|
||
size="xs"
|
||
className="rounded-2xl font-semibold"
|
||
>
|
||
{success}
|
||
</PlatformStatusMessage>
|
||
) : null}
|
||
{isLoading ? (
|
||
<div className="space-y-3">
|
||
{Array.from({ length: 2 }).map((_, index) => (
|
||
<div
|
||
key={index}
|
||
className="h-20 animate-pulse rounded-2xl bg-white/10"
|
||
/>
|
||
))}
|
||
</div>
|
||
) : tasks.length === 0 ? (
|
||
<PlatformEmptyState surface="subpanel" size="inline">
|
||
暂无任务
|
||
</PlatformEmptyState>
|
||
) : (
|
||
<div className="space-y-3">
|
||
{tasks.map((task) => {
|
||
const isClaimable = task.status === 'claimable';
|
||
const isClaiming = claimingTaskId === task.taskId;
|
||
const progressLabel = buildProfileTaskProgressLabel(task);
|
||
|
||
return (
|
||
<PlatformSubpanel
|
||
as="div"
|
||
key={task.taskId}
|
||
radius="sm"
|
||
padding="md"
|
||
>
|
||
<div className="flex min-w-0 items-start justify-between gap-3">
|
||
<div className="min-w-0">
|
||
<div className="text-base font-black text-[var(--platform-text-strong)]">
|
||
{task.title}
|
||
</div>
|
||
<div className="mt-1 text-xs font-semibold text-[var(--platform-text-soft)]">
|
||
{progressLabel}
|
||
</div>
|
||
</div>
|
||
<div className="shrink-0 text-right">
|
||
<div className="text-sm font-black text-[var(--platform-text-strong)]">
|
||
+{task.rewardPoints}
|
||
</div>
|
||
<div className="mt-1 text-[11px] font-semibold text-[var(--platform-text-soft)]">
|
||
{getProfileTaskStatusLabel(task.status)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<PlatformActionButton
|
||
surface="profile"
|
||
fullWidth
|
||
size="sm"
|
||
className="mt-3 disabled:opacity-50"
|
||
disabled={!isClaimable || Boolean(claimingTaskId)}
|
||
onClick={() => onClaim(task.taskId)}
|
||
>
|
||
{getProfileTaskClaimButtonLabel(task, isClaiming)}
|
||
</PlatformActionButton>
|
||
</PlatformSubpanel>
|
||
);
|
||
})}
|
||
</div>
|
||
)}
|
||
</PlatformProfileModalShell>
|
||
);
|
||
}
|