← back to Ken

src/app/dashboard/settings/page.js

131 lines

'use client';
import { useState, useEffect } from 'react';

export default function SettingsPage() {
  const [health, setHealth] = useState(null);
  const [testResult, setTestResult] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('/api/health').then(r => r.json()).then(d => { setHealth(d); setLoading(false); }).catch(() => setLoading(false));
  }, []);

  async function testSlack() {
    try {
      const res = await fetch('/api/alerts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'test' }) });
      const data = await res.json();
      setTestResult(data);
    } catch (err) { setTestResult({ error: err.message }); }
  }

  async function testNWS() {
    try {
      const res = await fetch('/api/weather', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'test_nws' }) });
      const data = await res.json();
      alert(data.success ? 'NWS API connected successfully!' : 'NWS API error: ' + (data.error || 'Unknown'));
    } catch (err) { alert('Error: ' + err.message); }
  }

  async function testPolymarket() {
    try {
      const res = await fetch('/api/markets', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'test_api' }) });
      const data = await res.json();
      alert(data.success ? `Polymarket API connected! Found ${data.market_count} markets.` : 'API error: ' + (data.error || 'Unknown'));
    } catch (err) { alert('Error: ' + err.message); }
  }

  async function runWorker(worker) {
    try {
      const res = await fetch('/api/workers', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'run', worker }) });
      const data = await res.json();
      alert(data.message || JSON.stringify(data));
    } catch (err) { alert('Error: ' + err.message); }
  }

  if (loading) return <div className="flex items-center justify-center h-64"><div className="animate-spin w-8 h-8 border-2 border-blue-500 border-t-transparent rounded-full" /></div>;

  return (
    <div className="space-y-6">
      <div>
        <h1 className="text-2xl font-bold">Settings</h1>
        <p className="text-gray-500 text-sm">System configuration and connectivity tests</p>
      </div>

      {/* System Status */}
      <div className="card">
        <h2 className="font-semibold mb-3">System Status</h2>
        <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
          {[
            { label: 'Database', status: health?.db_connected, detail: 'PostgreSQL' },
            { label: 'NWS API', status: 'unknown', detail: 'api.weather.gov' },
            { label: 'Polymarket', status: 'unknown', detail: 'gamma-api.polymarket.com' },
            { label: 'Slack', status: health?.slack_configured ? 'ok' : 'no_token', detail: 'U03TV3UC2V7' },
          ].map(s => (
            <div key={s.label} className="bg-gray-800/30 rounded-lg p-3">
              <div className="flex items-center gap-2 mb-1">
                <span className={`w-2 h-2 rounded-full ${s.status === true || s.status === 'ok' ? 'bg-green-500' : s.status === 'unknown' ? 'bg-yellow-500' : 'bg-red-500'}`} />
                <span className="text-sm font-medium">{s.label}</span>
              </div>
              <p className="text-xs text-gray-500">{s.detail}</p>
            </div>
          ))}
        </div>
      </div>

      {/* Connectivity Tests */}
      <div className="card">
        <h2 className="font-semibold mb-3">Connectivity Tests</h2>
        <div className="flex gap-3">
          <button onClick={testNWS} className="btn btn-primary">Test NWS API</button>
          <button onClick={testPolymarket} className="btn btn-primary">Test Polymarket</button>
          <button onClick={testSlack} className="btn btn-primary">Test Slack Alert</button>
        </div>
        {testResult && (
          <pre className="mt-3 bg-gray-900 rounded-lg p-3 text-xs text-gray-300 overflow-auto max-h-48">
            {JSON.stringify(testResult, null, 2)}
          </pre>
        )}
      </div>

      {/* Manual Worker Triggers */}
      <div className="card">
        <h2 className="font-semibold mb-3">Manual Worker Triggers</h2>
        <div className="flex flex-wrap gap-3">
          <button onClick={() => runWorker('market_ingest')} className="btn btn-primary">Ingest Markets</button>
          <button onClick={() => runWorker('weather_fetch')} className="btn btn-primary">Fetch Weather</button>
          <button onClick={() => runWorker('run_predictions')} className="btn btn-primary">Run Predictions</button>
          <button onClick={() => runWorker('risk_scan')} className="btn btn-primary">Risk Scan</button>
          <button onClick={() => runWorker('paper_trade')} className="btn btn-primary">Paper Trade Cycle</button>
        </div>
      </div>

      {/* Architecture Info */}
      <div className="card">
        <h2 className="font-semibold mb-3">Architecture</h2>
        <div className="grid grid-cols-2 gap-4 text-sm">
          <div>
            <p className="text-gray-500 text-xs uppercase mb-2">Data Sources</p>
            <ul className="space-y-1">
              <li className="flex items-center gap-2"><span className="badge badge-blue">NWS</span> Live US forecasts (api.weather.gov)</li>
              <li className="flex items-center gap-2"><span className="badge badge-purple">GEFS</span> Ensemble forecasts (AWS)</li>
              <li className="flex items-center gap-2"><span className="badge badge-green">Gamma</span> Polymarket markets</li>
              <li className="flex items-center gap-2"><span className="badge badge-yellow">CLOB</span> Orderbook data</li>
            </ul>
          </div>
          <div>
            <p className="text-gray-500 text-xs uppercase mb-2">Model Pipeline</p>
            <ul className="space-y-1">
              <li>1. Market parsing + event extraction</li>
              <li>2. Ensemble exceedance probability</li>
              <li>3. Climatological prior</li>
              <li>4. Bayesian combination + calibration</li>
              <li>5. Edge gate + risk sizing</li>
              <li>6. Paper/live execution</li>
            </ul>
          </div>
        </div>
      </div>
    </div>
  );
}