summaryrefslogtreecommitdiff
path: root/frontend/src/components/document/nodes/ContractBlockComponent.tsx
blob: 0d9a25aba8cca8f9edc972b7a621f307ad23ae57 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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>
  );
}