79 lines
2.5 KiB
TypeScript
79 lines
2.5 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
|
|
import type { CustomWorldAgentOperationRecord } from '../../../packages/shared/src/contracts/customWorldAgent';
|
|
|
|
type CustomWorldAgentOperationBannerProps = {
|
|
operation: CustomWorldAgentOperationRecord | null;
|
|
};
|
|
|
|
export function CustomWorldAgentOperationBanner({
|
|
operation,
|
|
}: CustomWorldAgentOperationBannerProps) {
|
|
const [visibleOperation, setVisibleOperation] =
|
|
useState<CustomWorldAgentOperationRecord | null>(operation);
|
|
|
|
useEffect(() => {
|
|
setVisibleOperation(operation);
|
|
|
|
if (operation?.status !== 'completed') {
|
|
return;
|
|
}
|
|
|
|
const timeoutId = window.setTimeout(() => {
|
|
setVisibleOperation((current) =>
|
|
current?.operationId === operation.operationId ? null : current,
|
|
);
|
|
}, 1200);
|
|
|
|
return () => window.clearTimeout(timeoutId);
|
|
}, [operation]);
|
|
|
|
if (!visibleOperation) {
|
|
return null;
|
|
}
|
|
|
|
const isFailed = visibleOperation.status === 'failed';
|
|
const isRunning =
|
|
visibleOperation.status === 'running' || visibleOperation.status === 'queued';
|
|
// 操作横幅直接复用平台状态横幅,亮暗主题都从同一套 token 取色。
|
|
const bannerToneClass = isFailed
|
|
? 'platform-banner--danger'
|
|
: isRunning
|
|
? 'platform-banner--info'
|
|
: 'platform-banner--success';
|
|
const progressFillStyle = isFailed
|
|
? { background: 'linear-gradient(90deg, #fb7185 0%, #f43f5e 100%)' }
|
|
: isRunning
|
|
? { background: 'var(--platform-button-primary-fill)' }
|
|
: { background: 'linear-gradient(90deg, #86efac 0%, #34d399 100%)' };
|
|
|
|
return (
|
|
<div
|
|
className={`platform-remap-surface platform-banner rounded-[1.4rem] px-4 py-4 ${bannerToneClass}`}
|
|
>
|
|
<div className="flex items-center justify-between gap-3">
|
|
<div className="text-sm font-semibold">
|
|
{visibleOperation.phaseLabel}
|
|
</div>
|
|
<div className="text-xs opacity-80">
|
|
{Math.max(0, Math.min(100, Math.round(visibleOperation.progress)))}%
|
|
</div>
|
|
</div>
|
|
{visibleOperation.error ? (
|
|
<div className="mt-2 text-sm opacity-90">
|
|
{visibleOperation.error}
|
|
</div>
|
|
) : null}
|
|
<div className="platform-progress-track mt-3 h-2 overflow-hidden rounded-full">
|
|
<div
|
|
className="h-full rounded-full transition-[width] duration-300"
|
|
style={{
|
|
width: `${Math.max(8, Math.min(100, visibleOperation.progress))}%`,
|
|
...progressFillStyle,
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|