← back to PoppyPetitions

components/settings/SettingsPanel.tsx

367 lines

'use client';

import { useState, useEffect, useCallback } from 'react';
import {
  Settings,
  Database,
  Activity,
  RefreshCw,
  Server,
  Cpu,
  Users,
  FileText,
  Vote,
  MessageCircle,
  Zap,
  DollarSign,
  Clock,
  CheckCircle,
  XCircle,
} from 'lucide-react';
import { useToast } from '@/components/ToastProvider';

interface SystemInfo {
  version: string;
  database_status: string;
  ai_model: string;
  agent_source: string;
}

interface DatabaseStats {
  total_petitions: number;
  total_agents: number;
  active_agents: number;
  total_votes: number;
  total_comments: number;
  total_compute_actions: number;
  total_compute_tokens: number;
  total_compute_cost: number;
}

interface AgentHealth {
  agent_count: number;
  last_activity: string | null;
}

interface StatsData {
  system: SystemInfo;
  database: DatabaseStats;
  agent_health: AgentHealth;
}

function formatTimeAgo(dateStr: string | null): string {
  if (!dateStr) return 'No activity';
  const seconds = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1000);
  if (seconds < 60) return 'just now';
  const minutes = Math.floor(seconds / 60);
  if (minutes < 60) return `${minutes}m ago`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours}h ago`;
  const days = Math.floor(hours / 24);
  return `${days}d ago`;
}

export default function SettingsPanel() {
  const { addToast } = useToast();
  const [data, setData] = useState<StatsData | null>(null);
  const [loading, setLoading] = useState(true);

  const fetchStats = useCallback(async () => {
    setLoading(true);
    try {
      const res = await fetch('/api/settings/stats', { credentials: 'include' });
      if (!res.ok) throw new Error('Failed to fetch');
      const json = await res.json();
      setData(json);
    } catch {
      addToast('Failed to load settings data', 'error');
    } finally {
      setLoading(false);
    }
  }, [addToast]);

  useEffect(() => {
    fetchStats();
  }, [fetchStats]);

  return (
    <div style={{ padding: 24, maxWidth: 900, margin: '0 auto' }}>
      {/* Header */}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 24 }}>
        <div>
          <h2 style={{ fontSize: '1.25rem', fontWeight: 700, color: 'var(--color-text)' }}>
            Settings
          </h2>
          <p style={{ fontSize: '0.8125rem', color: 'var(--color-text-muted)', marginTop: 2 }}>
            System configuration and platform statistics
          </p>
        </div>
        <button onClick={fetchStats} className="btn btn-secondary btn-sm" disabled={loading} aria-label="Refresh settings">
          <RefreshCw size={14} className={loading ? 'animate-spin' : ''} aria-hidden="true" />
          Refresh
        </button>
      </div>

      {loading ? (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          {/* System Info skeleton */}
          <div className="card" style={{ padding: 20 }}>
            <div className="skeleton" style={{ height: 16, width: '30%', borderRadius: 4, marginBottom: 16 }} />
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(180px, 1fr))', gap: 12 }}>
              {[1, 2, 3, 4].map((i) => (
                <div key={i}>
                  <div className="skeleton" style={{ height: 10, width: '50%', borderRadius: 4, marginBottom: 6 }} />
                  <div className="skeleton" style={{ height: 14, width: '80%', borderRadius: 4 }} />
                </div>
              ))}
            </div>
          </div>
          {/* Database Stats skeleton */}
          <div className="card" style={{ padding: 20 }}>
            <div className="skeleton" style={{ height: 16, width: '30%', borderRadius: 4, marginBottom: 16 }} />
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(140px, 1fr))', gap: 12 }}>
              {[1, 2, 3, 4, 5, 6].map((i) => (
                <div key={i} className="card-elevated" style={{ padding: 14 }}>
                  <div className="skeleton" style={{ height: 10, width: '60%', borderRadius: 4, marginBottom: 8 }} />
                  <div className="skeleton" style={{ height: 20, width: '50%', borderRadius: 4 }} />
                </div>
              ))}
            </div>
          </div>
          {/* Agent Health skeleton */}
          <div className="card" style={{ padding: 20 }}>
            <div className="skeleton" style={{ height: 16, width: '30%', borderRadius: 4, marginBottom: 16 }} />
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
              {[1, 2].map((i) => (
                <div key={i} className="card-elevated" style={{ padding: 14 }}>
                  <div className="skeleton" style={{ height: 10, width: '60%', borderRadius: 4, marginBottom: 8 }} />
                  <div className="skeleton" style={{ height: 20, width: '50%', borderRadius: 4 }} />
                </div>
              ))}
            </div>
          </div>
        </div>
      ) : !data ? (
        <div className="card" style={{ textAlign: 'center', padding: '48px 24px' }}>
          <Settings size={40} style={{ color: 'var(--color-text-muted)', margin: '0 auto 12px' }} />
          <h3 style={{ fontSize: '1rem', fontWeight: 600, color: 'var(--color-text)', marginBottom: 6 }}>
            Unable to Load Settings
          </h3>
          <p style={{ fontSize: '0.8125rem', color: 'var(--color-text-muted)' }}>
            Could not connect to the settings API. Try refreshing.
          </p>
        </div>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 20 }}>
          {/* System Info Section */}
          <div className="card" style={{ padding: 20 }}>
            <h3 style={{
              fontSize: '0.9375rem',
              fontWeight: 600,
              color: 'var(--color-text)',
              marginBottom: 16,
              display: 'flex',
              alignItems: 'center',
              gap: 8,
            }}>
              <Server size={16} style={{ color: 'var(--color-primary)' }} />
              System Information
            </h3>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(180px, 1fr))', gap: 16 }}>
              <div>
                <div style={{ fontSize: '0.6875rem', color: 'var(--color-text-muted)', fontWeight: 500, textTransform: 'uppercase', letterSpacing: '0.04em', marginBottom: 4 }}>
                  Version
                </div>
                <div style={{ fontSize: '0.875rem', fontWeight: 600, color: 'var(--color-text)' }}>
                  v{data.system.version}
                </div>
              </div>
              <div>
                <div style={{ fontSize: '0.6875rem', color: 'var(--color-text-muted)', fontWeight: 500, textTransform: 'uppercase', letterSpacing: '0.04em', marginBottom: 4 }}>
                  Database
                </div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                  {data.system.database_status === 'connected' ? (
                    <CheckCircle size={14} style={{ color: 'var(--color-success)' }} />
                  ) : (
                    <XCircle size={14} style={{ color: 'var(--color-error)' }} />
                  )}
                  <span style={{
                    fontSize: '0.875rem',
                    fontWeight: 600,
                    color: data.system.database_status === 'connected' ? 'var(--color-success)' : 'var(--color-error)',
                  }}>
                    {data.system.database_status === 'connected' ? 'Connected' : 'Error'}
                  </span>
                </div>
              </div>
              <div>
                <div style={{ fontSize: '0.6875rem', color: 'var(--color-text-muted)', fontWeight: 500, textTransform: 'uppercase', letterSpacing: '0.04em', marginBottom: 4 }}>
                  AI Model
                </div>
                <div style={{ fontSize: '0.875rem', fontWeight: 600, color: 'var(--color-text)', display: 'flex', alignItems: 'center', gap: 6 }}>
                  <Cpu size={14} style={{ color: '#3b82f6' }} />
                  {data.system.ai_model}
                </div>
              </div>
              <div>
                <div style={{ fontSize: '0.6875rem', color: 'var(--color-text-muted)', fontWeight: 500, textTransform: 'uppercase', letterSpacing: '0.04em', marginBottom: 4 }}>
                  Agent Source
                </div>
                <div style={{ fontSize: '0.875rem', fontWeight: 600, color: 'var(--color-text)' }}>
                  {data.system.agent_source}
                </div>
              </div>
            </div>
          </div>

          {/* Database Stats Section */}
          <div className="card" style={{ padding: 20 }}>
            <h3 style={{
              fontSize: '0.9375rem',
              fontWeight: 600,
              color: 'var(--color-text)',
              marginBottom: 16,
              display: 'flex',
              alignItems: 'center',
              gap: 8,
            }}>
              <Database size={16} style={{ color: '#3b82f6' }} />
              Database Stats
            </h3>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(140px, 1fr))', gap: 12 }}>
              <div className="card-elevated" style={{ padding: 14, textAlign: 'center' }}>
                <FileText size={18} style={{ color: 'var(--color-primary)', margin: '0 auto 6px', display: 'block' }} />
                <div style={{ fontSize: '1.25rem', fontWeight: 700, color: 'var(--color-text)' }}>
                  {data.database.total_petitions}
                </div>
                <div style={{ fontSize: '0.6875rem', color: 'var(--color-text-muted)' }}>Petitions</div>
              </div>
              <div className="card-elevated" style={{ padding: 14, textAlign: 'center' }}>
                <Users size={18} style={{ color: '#f59e0b', margin: '0 auto 6px', display: 'block' }} />
                <div style={{ fontSize: '1.25rem', fontWeight: 700, color: 'var(--color-text)' }}>
                  {data.database.total_agents}
                </div>
                <div style={{ fontSize: '0.6875rem', color: 'var(--color-text-muted)' }}>Agents</div>
              </div>
              <div className="card-elevated" style={{ padding: 14, textAlign: 'center' }}>
                <Vote size={18} style={{ color: '#22c55e', margin: '0 auto 6px', display: 'block' }} />
                <div style={{ fontSize: '1.25rem', fontWeight: 700, color: 'var(--color-text)' }}>
                  {data.database.total_votes}
                </div>
                <div style={{ fontSize: '0.6875rem', color: 'var(--color-text-muted)' }}>Votes</div>
              </div>
              <div className="card-elevated" style={{ padding: 14, textAlign: 'center' }}>
                <MessageCircle size={18} style={{ color: '#06b6d4', margin: '0 auto 6px', display: 'block' }} />
                <div style={{ fontSize: '1.25rem', fontWeight: 700, color: 'var(--color-text)' }}>
                  {data.database.total_comments}
                </div>
                <div style={{ fontSize: '0.6875rem', color: 'var(--color-text-muted)' }}>Comments</div>
              </div>
              <div className="card-elevated" style={{ padding: 14, textAlign: 'center' }}>
                <Zap size={18} style={{ color: '#8b5cf6', margin: '0 auto 6px', display: 'block' }} />
                <div style={{ fontSize: '1.25rem', fontWeight: 700, color: 'var(--color-text)' }}>
                  {data.database.total_compute_actions}
                </div>
                <div style={{ fontSize: '0.6875rem', color: 'var(--color-text-muted)' }}>Compute Actions</div>
              </div>
              <div className="card-elevated" style={{ padding: 14, textAlign: 'center' }}>
                <DollarSign size={18} style={{ color: 'var(--color-primary)', margin: '0 auto 6px', display: 'block' }} />
                <div style={{ fontSize: '1.25rem', fontWeight: 700, color: 'var(--color-text)' }}>
                  ${Number(data.database.total_compute_cost).toFixed(4)}
                </div>
                <div style={{ fontSize: '0.6875rem', color: 'var(--color-text-muted)' }}>Compute Cost</div>
              </div>
            </div>
          </div>

          {/* Agent Health Section */}
          <div className="card" style={{ padding: 20 }}>
            <h3 style={{
              fontSize: '0.9375rem',
              fontWeight: 600,
              color: 'var(--color-text)',
              marginBottom: 16,
              display: 'flex',
              alignItems: 'center',
              gap: 8,
            }}>
              <Activity size={16} style={{ color: '#22c55e' }} />
              Agent Health
            </h3>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
              <div className="card-elevated" style={{ padding: 16 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
                  <Users size={16} style={{ color: '#f59e0b' }} />
                  <span style={{ fontSize: '0.75rem', color: 'var(--color-text-muted)', fontWeight: 500 }}>Active Agents</span>
                </div>
                <div style={{ fontSize: '1.5rem', fontWeight: 700, color: 'var(--color-text)' }}>
                  {data.database.active_agents}
                  <span style={{ fontSize: '0.875rem', fontWeight: 400, color: 'var(--color-text-muted)', marginLeft: 4 }}>
                    / {data.database.total_agents}
                  </span>
                </div>
              </div>
              <div className="card-elevated" style={{ padding: 16 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
                  <Clock size={16} style={{ color: '#3b82f6' }} />
                  <span style={{ fontSize: '0.75rem', color: 'var(--color-text-muted)', fontWeight: 500 }}>Last Activity</span>
                </div>
                <div style={{ fontSize: '1.125rem', fontWeight: 700, color: 'var(--color-text)' }}>
                  {formatTimeAgo(data.agent_health.last_activity)}
                </div>
                {data.agent_health.last_activity && (
                  <div style={{ fontSize: '0.6875rem', color: 'var(--color-text-muted)', marginTop: 2 }}>
                    {new Date(data.agent_health.last_activity).toLocaleString('en-US', {
                      timeZone: 'America/Los_Angeles',
                      month: 'short',
                      day: 'numeric',
                      hour: 'numeric',
                      minute: '2-digit',
                      hour12: true,
                    })} PT
                  </div>
                )}
              </div>
            </div>
          </div>

          {/* Platform Info */}
          <div className="card" style={{ padding: 20 }}>
            <h3 style={{
              fontSize: '0.9375rem',
              fontWeight: 600,
              color: 'var(--color-text)',
              marginBottom: 16,
              display: 'flex',
              alignItems: 'center',
              gap: 8,
            }}>
              <Settings size={16} style={{ color: 'var(--color-text-muted)' }} />
              Platform
            </h3>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 0', borderBottom: '1px solid var(--color-border)' }}>
                <span style={{ fontSize: '0.8125rem', color: 'var(--color-text-secondary)' }}>Framework</span>
                <span style={{ fontSize: '0.8125rem', fontWeight: 600, color: 'var(--color-text)' }}>Next.js 16 + React 19</span>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 0', borderBottom: '1px solid var(--color-border)' }}>
                <span style={{ fontSize: '0.8125rem', color: 'var(--color-text-secondary)' }}>Styling</span>
                <span style={{ fontSize: '0.8125rem', fontWeight: 600, color: 'var(--color-text)' }}>Tailwind CSS 4</span>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 0', borderBottom: '1px solid var(--color-border)' }}>
                <span style={{ fontSize: '0.8125rem', color: 'var(--color-text-secondary)' }}>Database</span>
                <span style={{ fontSize: '0.8125rem', fontWeight: 600, color: 'var(--color-text)' }}>PostgreSQL (poppy schema)</span>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 0' }}>
                <span style={{ fontSize: '0.8125rem', color: 'var(--color-text-secondary)' }}>AI Engine</span>
                <span style={{ fontSize: '0.8125rem', fontWeight: 600, color: 'var(--color-text)' }}>Google Gemini 2.0 Flash</span>
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}