← back to Dw Boardroom Governance

frontend/src/components/Ledger.tsx

80 lines

import { useState } from 'react';
import { api } from '../api';
import { useApi } from '../hooks/useApi';
import type { Decision } from '../types';

const STATUS_COLORS: Record<string, string> = {
  pending: '#f59e0b', approved: '#22c55e', rejected: '#ef4444',
  modified: '#3b82f6', deferred: '#7a7f96', auto_approved: '#06b6d4',
};

export default function Ledger() {
  const [filter, setFilter] = useState('');
  const { data: decisions, refresh } = useApi(() => api.getDecisions(), []);

  const filtered = (decisions || []).filter((d: Decision) =>
    !filter || d.status === filter
  );

  return (
    <div style={styles.card}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
        <h3 style={styles.cardTitle}>Decision Ledger</h3>
        <div style={{ display: 'flex', gap: 4 }}>
          <button style={{ ...styles.filterBtn, ...(filter === '' ? styles.activeFilter : {}) }} onClick={() => setFilter('')}>All</button>
          {Object.keys(STATUS_COLORS).map(s => (
            <button key={s} style={{ ...styles.filterBtn, ...(filter === s ? styles.activeFilter : {}) }} onClick={() => setFilter(s)}>
              {s.replace('_', ' ')}
            </button>
          ))}
        </div>
      </div>

      <div style={styles.table}>
        <div style={styles.tableHeader}>
          <span style={{ width: 140 }}>Date</span>
          <span style={{ flex: 1 }}>Topic</span>
          <span style={{ width: 30 }}>Type</span>
          <span style={{ width: 40 }}>Score</span>
          <span style={{ width: 90 }}>Status</span>
          <span style={{ width: 80 }}>Owner</span>
        </div>
        {filtered.length === 0 && <div style={styles.empty}>No decisions found.</div>}
        {filtered.map((d: Decision) => (
          <div key={d.id} style={styles.row}>
            <span style={{ width: 140, fontSize: 10, color: '#7a7f96' }}>
              {new Date(d.created_at).toLocaleString('en-US', { timeZone: 'America/Los_Angeles', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })}
            </span>
            <span style={{ flex: 1, fontSize: 12, fontWeight: 500 }}>{d.topic}</span>
            <span style={{ width: 30, fontSize: 11, fontWeight: 700, color: '#8b5cf6' }}>{d.decision_type}</span>
            <span style={{ width: 40, fontSize: 11, fontWeight: 700 }}>{d.score ?? '—'}</span>
            <span style={{ width: 90 }}>
              <span style={{ ...styles.statusBadge, background: (STATUS_COLORS[d.status] || '#7a7f96') + '22', color: STATUS_COLORS[d.status] }}>
                {d.status.replace('_', ' ')}
              </span>
            </span>
            <span style={{ width: 80, fontSize: 11, color: '#7a7f96' }}>{d.owner}</span>
          </div>
        ))}
      </div>

      <div style={{ marginTop: 8, fontSize: 11, color: '#7a7f96' }}>
        {filtered.length} decision(s) {filter && `(${filter})`}
        <button style={{ ...styles.filterBtn, marginLeft: 8 }} onClick={refresh}>Refresh</button>
      </div>
    </div>
  );
}

const styles: Record<string, React.CSSProperties> = {
  card: { background: '#1a1a2e', borderRadius: 12, padding: 20, border: '1px solid #252540' },
  cardTitle: { margin: 0, fontSize: 15, fontWeight: 700 },
  filterBtn: { padding: '3px 8px', borderRadius: 4, border: '1px solid #252540', background: 'transparent', color: '#7a7f96', fontSize: 10, cursor: 'pointer', fontFamily: 'inherit', textTransform: 'capitalize' as const },
  activeFilter: { background: '#8b5cf633', borderColor: '#8b5cf6', color: '#e8eaf0' },
  table: { display: 'flex', flexDirection: 'column', gap: 2 },
  tableHeader: { display: 'flex', gap: 8, padding: '8px 10px', fontSize: 10, fontWeight: 700, color: '#7a7f96', textTransform: 'uppercase' as const, borderBottom: '1px solid #252540' },
  row: { display: 'flex', gap: 8, padding: '8px 10px', alignItems: 'center', borderBottom: '1px solid #1a1a2e', background: '#12121f', borderRadius: 4 },
  statusBadge: { padding: '2px 8px', borderRadius: 4, fontSize: 10, fontWeight: 600 },
  empty: { color: '#7a7f96', fontSize: 12, padding: 20, textAlign: 'center' as const },
};