← back to Ken

kalshi-dash/src/setup/SetupWizard.jsx

120 lines

import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { checkSession, post, api } from '../api';
import Welcome from './steps/Welcome';
import ConnectKalshi from './steps/ConnectKalshi';
import TestConnection from './steps/TestConnection';
import BrowseMarkets from './steps/BrowseMarkets';
import FetchWeather from './steps/FetchWeather';
import RunPredictions from './steps/RunPredictions';
import RiskProfile from './steps/RiskProfile';
import GoLive from './steps/GoLive';

const STEPS = [
  { label: 'Shalom', icon: '1' },
  { label: 'Keys', icon: '2' },
  { label: 'Test It', icon: '3' },
  { label: 'Snoop', icon: '4' },
  { label: 'Weather', icon: '5' },
  { label: 'Predict', icon: '6' },
  { label: 'Limits', icon: '7' },
  { label: 'Mazel Tov', icon: '8' },
];

const COMPS = [Welcome, ConnectKalshi, TestConnection, BrowseMarkets, FetchWeather, RunPredictions, RiskProfile, GoLive];

export default function SetupWizard() {
  const nav = useNavigate();
  const [authed, setAuthed] = useState(false);
  const [step, setStep] = useState(0);
  const [data, setData] = useState({
    keys: null,
    connectionOk: false,
    marketsLoaded: 0,
    weatherFetched: false,
    predictionsRun: false,
    riskProfile: null,
  });

  useEffect(() => {
    checkSession().then(ok => {
      if (!ok) { nav('/login'); return; }
      setAuthed(true);
      api('/setup').then(r => {
        if (r.setup_complete) { nav('/'); return; }
        if (r.setup_progress) {
          setStep(r.setup_progress.step || 0);
          setData(d => ({ ...d, ...r.setup_progress.data }));
        }
      }).catch(() => {});
    });
  }, []);

  useEffect(() => {
    if (authed && step > 0) {
      post('/setup', { action: 'save_progress', progress: { step, data } }).catch(() => {});
    }
  }, [step]);

  function next() { if (step < STEPS.length - 1) setStep(s => s + 1); }
  function back() { if (step > 0) setStep(s => s - 1); }
  function goTo(i) { if (i <= step) setStep(i); }
  function update(partial) { setData(d => ({ ...d, ...partial })); }
  async function complete() {
    await post('/setup', { action: 'complete', progress: { step: STEPS.length - 1, data } });
    nav('/');
  }

  if (!authed) return <div className="flex items-center justify-center min-h-screen"><div className="animate-spin w-8 h-8 border-2 border-amber-500 border-t-transparent rounded-full" /></div>;

  const StepComponent = COMPS[step];

  return (
    <div className="min-h-screen flex flex-col" style={{ background: 'var(--bg)' }}>
      {/* Header */}
      <div className="flex items-center gap-3 px-6 py-4 border-b border-gray-800">
        <div className="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0" style={{ background: 'linear-gradient(135deg,#f59e0b,#f97316)' }}>
          <span className="text-black font-bold text-sm">K</span>
        </div>
        <span className="font-bold text-sm kalshi-gradient">KEN SETUP — SIT, WE'LL TALK</span>
      </div>

      {/* Stepper */}
      <div className="px-6 py-5">
        <div className="max-w-3xl mx-auto flex items-center">
          {STEPS.map((s, i) => (
            <React.Fragment key={i}>
              <button onClick={() => goTo(i)} disabled={i > step} className="flex flex-col items-center gap-1.5 group" style={{ minWidth: 56 }}>
                <div className={`w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold transition-all ${
                  i < step ? 'bg-green-500 text-white' :
                  i === step ? 'bg-amber-500 text-black' :
                  'bg-gray-800 text-gray-500'
                } ${i <= step ? 'cursor-pointer' : 'cursor-default'}`}
                  style={i === step ? { animation: 'pg 2s infinite' } : {}}
                >
                  {i < step ? (
                    <svg className="w-4 h-4" fill="none" stroke="currentColor" strokeWidth="2.5" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" /></svg>
                  ) : s.icon}
                </div>
                <span className={`text-[10px] font-medium ${
                  i < step ? 'text-green-400' : i === step ? 'text-amber-400' : 'text-gray-600'
                }`}>{s.label}</span>
              </button>
              {i < STEPS.length - 1 && (
                <div className={`h-0.5 flex-1 transition-colors ${i < step ? 'bg-green-500' : 'bg-gray-800'}`} />
              )}
            </React.Fragment>
          ))}
        </div>
      </div>

      {/* Content */}
      <div className="flex-1 flex items-start justify-center px-6 pb-10">
        <div className="w-full max-w-2xl" key={step}>
          <StepComponent data={data} update={update} next={next} back={back} complete={complete} step={step} />
        </div>
      </div>
    </div>
  );
}