387a1c26e3
清理已跟踪原始日志与个人本地配置 修复前后端有效门禁、测试和构建问题 补齐生产 Jenkins、SpacetimeDB 本地命令与文档约束 收紧图片编辑器状态、附件与媒体引用测试 排除已下线旧创作入口测试并清理 warning
149 lines
5.0 KiB
TypeScript
149 lines
5.0 KiB
TypeScript
import type { ProfileTaskCenterResponse } from '../../../packages/shared/src/contracts/runtime';
|
|
import { PlatformActionButton } from '../common/PlatformActionButton';
|
|
import { PlatformAsyncStatePanel } from '../common/PlatformAsyncStatePanel';
|
|
import { PlatformEmptyState } from '../common/PlatformEmptyState';
|
|
import { PlatformProfileContentRow } from '../common/PlatformProfileContentRow';
|
|
import { PlatformProfileSkeletonList } from '../common/PlatformProfileSkeletonList';
|
|
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
|
|
import {
|
|
buildProfileTaskProgressLabel,
|
|
getProfileTaskClaimButtonLabel,
|
|
getProfileTaskStatusLabel,
|
|
selectProfileTaskCenterTasks,
|
|
} from '../rpg-entry/rpgEntryProfileTaskViewModel';
|
|
import { PlatformProfileModalShell } from './PlatformProfileModalShell';
|
|
|
|
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"
|
|
>
|
|
{success ? (
|
|
<PlatformStatusMessage
|
|
tone="success"
|
|
surface="profile"
|
|
size="xs"
|
|
className="rounded-2xl font-semibold"
|
|
>
|
|
{success}
|
|
</PlatformStatusMessage>
|
|
) : null}
|
|
<PlatformAsyncStatePanel
|
|
errorState={
|
|
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
|
|
}
|
|
isLoading={isLoading}
|
|
loadingState={
|
|
<PlatformProfileSkeletonList
|
|
count={2}
|
|
containerClassName="space-y-3"
|
|
itemClassName="h-20 rounded-2xl bg-white/10"
|
|
/>
|
|
}
|
|
isEmpty={tasks.length === 0}
|
|
emptyState={
|
|
<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 (
|
|
<PlatformProfileContentRow
|
|
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>
|
|
</PlatformProfileContentRow>
|
|
);
|
|
})}
|
|
</div>
|
|
</PlatformAsyncStatePanel>
|
|
</PlatformProfileModalShell>
|
|
);
|
|
}
|