import { memo, useEffect, useRef, useState } from 'react'; import { Button } from '@renderer/components/ui/button'; import { cn } from '@renderer/lib/utils'; import { X } from 'lucide-react'; import { ProvisioningProgressBlock } from './ProvisioningProgressBlock'; import { useTeamProvisioningPresentation } from './useTeamProvisioningPresentation'; import type { RetryFailedOpenCodeSecondaryLanesResult } from '@shared/types'; export interface TeamProvisioningPanelProps { teamName: string; surface?: 'raised' | 'flat'; dismissible?: boolean; className?: string; defaultLogsOpen?: boolean; } function formatOpenCodeSecondaryRetryResult( result: RetryFailedOpenCodeSecondaryLanesResult ): string { const parts: string[] = []; if (result.confirmed.length > 0) { parts.push(`${result.confirmed.length} confirmed`); } if (result.pending.length > 0) { parts.push(`${result.pending.length} pending`); } if (result.failed.length > 0) { parts.push(`${result.failed.length} failed`); } if (result.skipped.length > 0) { parts.push(`${result.skipped.length} skipped`); } return parts.length > 0 ? `OpenCode retry: ${parts.join(', ')}` : 'No retryable OpenCode failures'; } export const TeamProvisioningPanel = memo(function TeamProvisioningPanel({ teamName, surface = 'flat', dismissible = false, className, defaultLogsOpen, }: TeamProvisioningPanelProps): React.JSX.Element | null { const { presentation, cancelProvisioning, retryFailedOpenCodeSecondaryLanes, memberDiagnostics, runInstanceKey, } = useTeamProvisioningPresentation(teamName); const [dismissed, setDismissed] = useState(false); const [retryingOpenCode, setRetryingOpenCode] = useState(false); const [openCodeRetryMessage, setOpenCodeRetryMessage] = useState(null); const [openCodeRetryError, setOpenCodeRetryError] = useState(null); const lastActiveStepRef = useRef(-1); useEffect(() => { setDismissed(false); setRetryingOpenCode(false); setOpenCodeRetryMessage(null); setOpenCodeRetryError(null); }, [runInstanceKey]); if (!presentation || dismissed) { return null; } if (presentation.currentStepIndex >= 0 && !presentation.isFailed) { lastActiveStepRef.current = presentation.currentStepIndex; } const showRunningState = presentation.isActive || presentation.hasMembersStillJoining; const canRetryFailedOpenCode = !presentation.isActive && presentation.retryableOpenCodeSecondaryFailedCount > 0 && Boolean(retryFailedOpenCodeSecondaryLanes); const retryOpenCodeAction = canRetryFailedOpenCode ? (

{openCodeRetryError ?? openCodeRetryMessage ?? `${presentation.retryableOpenCodeSecondaryFailedCount} failed OpenCode teammate${ presentation.retryableOpenCodeSecondaryFailedCount === 1 ? '' : 's' } can be retried.`}

) : null; const block = ( = 0 ? lastActiveStepRef.current : 0 : undefined } loading={showRunningState} startedAt={presentation.progress.startedAt} teamName={teamName} runId={presentation.progress.runId} pid={presentation.progress.pid} cliLogsTail={presentation.progress.cliLogsTail} assistantOutput={presentation.progress.assistantOutput} launchDiagnostics={presentation.progress.launchDiagnostics} warnings={presentation.progress.warnings} memberDiagnostics={memberDiagnostics} defaultLiveOutputOpen={presentation.defaultLiveOutputOpen} defaultLogsOpen={defaultLogsOpen} onCancel={ presentation.canCancel && cancelProvisioning ? () => { void cancelProvisioning(presentation.progress.runId); } : null } successMessage={presentation.successMessage} successMessageSeverity={presentation.successMessageSeverity} onDismiss={ dismissible && presentation.isReady ? () => { setDismissed(true); } : null } className={!presentation.isFailed && !retryOpenCodeAction ? className : undefined} /> ); if (!presentation.isFailed && !retryOpenCodeAction) { return block; } return (
{presentation.isFailed ? (

{presentation.progress.message}

{dismissible ? ( ) : null}
) : null} {block} {retryOpenCodeAction}
); });