summaryrefslogblamecommitdiff
path: root/frontend/src/components/document/nodes/ContractBlockComponent.tsx
blob: 0d9a25aba8cca8f9edc972b7a621f307ad23ae57 (plain) (tree)




















































































































                                                                                                   
import React, { useEffect, useState } from 'react';
import './ContractBlock.css';

interface ContractBlockComponentProps {
  contractId: string;
  contractName: string;
}

interface ContractInfo {
  id: string;
  name: string;
  status: string;
  phase: string;
  contract_type: string;
}

const PHASE_COLORS: Record<string, string> = {
  planning: '#3b82f6',
  execution: '#f59e0b',
  review: '#8b5cf6',
  completed: '#10b981',
  failed: '#ef4444',
};

const STATUS_COLORS: Record<string, string> = {
  active: '#10b981',
  running: '#10b981',
  idle: '#f59e0b',
  paused: '#f59e0b',
  completed: '#10b981',
  failed: '#ef4444',
  archived: '#6b7280',
};

export function ContractBlockComponent({ contractId, contractName }: ContractBlockComponentProps) {
  const [contract, setContract] = useState<ContractInfo | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let cancelled = false;

    async function fetchContract() {
      try {
        const response = await fetch(`/api/v1/contracts/${contractId}`);
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        const data = await response.json();
        if (!cancelled) {
          setContract(data.contract || data);
          setError(null);
        }
      } catch (err) {
        if (!cancelled) {
          setError(err instanceof Error ? err.message : 'Failed to load');
        }
      } finally {
        if (!cancelled) setLoading(false);
      }
    }

    fetchContract();
    return () => { cancelled = true; };
  }, [contractId]);

  if (loading) {
    return (
      <div className="contract-block" contentEditable={false}>
        <div className="contract-block-loading">
          <div className="contract-block-spinner" />
          <span>Loading contract...</span>
        </div>
      </div>
    );
  }

  if (error) {
    return (
      <div className="contract-block contract-block--error" contentEditable={false}>
        <div className="contract-block-header">
          <span className="contract-block-icon">&#x1F4E6;</span>
          <span className="contract-block-name">{contractName}</span>
        </div>
        <div className="contract-block-error-msg">Unable to load: {error}</div>
      </div>
    );
  }

  const phase = contract?.phase?.toLowerCase() || 'unknown';
  const status = contract?.status?.toLowerCase() || 'unknown';
  const phaseColor = PHASE_COLORS[phase] || '#6b7280';
  const statusColor = STATUS_COLORS[status] || '#6b7280';

  return (
    <div className="contract-block" contentEditable={false}>
      <div className="contract-block-header">
        <span className="contract-block-icon">&#x1F4E6;</span>
        <span className="contract-block-name">{contract?.name || contractName}</span>
        <span
          className="contract-block-phase-badge"
          style={{ backgroundColor: phaseColor + '20', color: phaseColor }}
        >
          {phase}
        </span>
        <span
          className="contract-block-status-dot"
          style={{ backgroundColor: statusColor }}
          title={status}
        />
      </div>
      {contract?.contract_type && (
        <div className="contract-block-meta">
          <span className="contract-block-type">{contract.contract_type}</span>
        </div>
      )}
    </div>
  );
}