<!DOCTYPE html>

<html lang="es">

<head>

  <meta charset="UTF-8">

  <script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>

  <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

  <script src="https://cdn.tailwindcss.com"></script>

  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

  <style>

    body { background: #020617; color: #f8fafc; font-family: ui-monospace, monospace; }

    ::-webkit-scrollbar { width: 4px; height: 4px; }

    ::-webkit-scrollbar-thumb { background: #1e293b; border-radius: 10px; }

  </style>

</head>

<body>

  <div id="root"></div>

  <script type="text/babel">

    const { useState, useEffect } = React;

    

    // ⚠️ CAMBIA ESTO POR LA URL DE TU WORKER ORQUESTADOR

    const ORCHESTRATOR_URL = "https://metadatamanager.rodrimm3006.workers.dev";


    const App = () => {

      const [metadata, setMetadata] = useState({});

      const [query, setQuery] = useState("SELECT * FROM users_data LIMIT 10");

      const [results, setResults] = useState([]);

      const [loading, setLoading] = useState(false);

      const [progress, setProgress] = useState({ done: 0, total: 0 });


      const fetchMetadata = async () => {

        try {

          const res = await fetch(`${ORCHESTRATOR_URL}/metadata`);

          const data = await res.json();

          setMetadata(data);

          setProgress(prev => ({ ...prev, total: Object.keys(data).length }));

        } catch (e) { console.error("Error cargando metadatos", e); }

      };


      const run = async () => {

        if (!query.trim()) return;

        setLoading(true); 

        setResults([]); 

        setProgress(prev => ({ ...prev, done: 0 }));

        

        try {

          const res = await fetch(`${ORCHESTRATOR_URL}/query`, { 

            method: 'POST', 

            headers: { 'Content-Type': 'application/json' },

            body: JSON.stringify({ sql: query }) 

          });

          

          if (!res.ok) {

            const err = await res.json();

            throw new Error(err.error || "Error de red");

          }


          const reader = res.body.getReader();

          const decoder = new TextDecoder();

          

          while (true) {

            const {done, value} = await reader.read();

            if (done) break;

            

            const lines = decoder.decode(value).split('\n').filter(Boolean);

            lines.forEach(line => {

              try {

                const data = JSON.parse(line);

                setResults(prev => [...prev, data]);

                setProgress(p => ({ ...p, done: p.done + 1 }));

              } catch (parseErr) {}

            });

          }

        } catch (e) { alert(e.message); }

        setLoading(false);

      };


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


      return (

        <div className="flex h-screen overflow-hidden text-[13px]">

          <aside className="w-72 bg-slate-950 border-r border-slate-900 p-6 flex flex-col shadow-2xl z-10">

            <h1 className="text-blue-500 font-black italic tracking-tighter text-lg uppercase mb-1">Shard_Master</h1>

            <p className="text-[10px] text-emerald-500 font-bold mb-8">PAGES_V2.0 // DISTRIBUTED</p>

            

            <div className="flex-1 overflow-y-auto">

              {Object.entries(metadata).map(([shard, tables]) => (

                <div key={shard} className="mb-6">

                  <div className="text-[10px] text-slate-500 font-bold mb-2 uppercase tracking-widest flex items-center gap-2">

                    <span className="w-1.5 h-1.5 bg-blue-500 rounded-full animate-pulse"></span>

                    {shard.split(':')[1]}

                  </div>

                  {tables.map(t => (

                    <div key={t.name} onClick={() => setQuery(\`SELECT * FROM \${t.name} LIMIT 5\`)} className="pl-3 py-1 text-slate-400 hover:text-white cursor-pointer border-l border-slate-900 hover:border-blue-500 transition-all text-[11px]">

                      {t.name}

                    </div>

                  ))}

                </div>

              ))}

            </div>

          </aside>


          <main className="flex-1 flex flex-col bg-[#020617]">

            <div className="p-8 border-b border-slate-900 bg-slate-900/20">

              <div className="flex gap-4">

                <input 

                  value={query} 

                  onChange={e => setQuery(e.target.value)} 

                  className="bg-slate-950 px-4 py-3 rounded-lg flex-1 border border-slate-800 outline-none focus:border-blue-500 transition-colors text-blue-300 font-medium" 

                />

                <button onClick={run} disabled={loading} className="bg-blue-600 hover:bg-blue-500 text-white px-8 rounded-lg font-bold text-[11px] uppercase tracking-wider disabled:opacity-50 transition-all shadow-lg shadow-blue-900/20">

                  {loading ? 'Processing...' : 'Run Query'}

                </button>

              </div>

              

              {/* Barra de progreso de lotes */}

              <div className="mt-4 flex items-center gap-4 text-[10px] font-bold tracking-widest uppercase">

                <div className="text-slate-500">Shards Processed: <span className={progress.done === progress.total ? "text-emerald-500" : "text-blue-500"}>{progress.done} / {progress.total}</span></div>

                {loading && <div className="text-blue-500 animate-pulse">Streaming data batches...</div>}

              </div>

            </div>


            <div className="flex-1 overflow-auto p-8 pt-6 grid grid-cols-1 xl:grid-cols-2 gap-6 items-start auto-rows-max">

              {results.map((r, i) => (

                <div key={i} className="bg-slate-950 border border-slate-800 rounded-xl overflow-hidden shadow-2xl animate-in slide-in-from-bottom-4 duration-300">

                  <div className="bg-slate-900/80 px-4 py-2 flex justify-between items-center border-b border-slate-800">

                    <span className="text-[10px] font-black text-slate-400 uppercase tracking-widest">{r.shard}</span>

                    <span className="text-[10px] text-emerald-500 font-mono bg-emerald-500/10 px-2 py-0.5 rounded">{r.time}ms</span>

                  </div>

                  <div className="p-0 overflow-x-auto">

                    {r.error ? (

                      <div className="p-4 text-red-400 text-xs">{r.error}</div>

                    ) : r.data && r.data.length > 0 ? (

                      <table className="w-full text-left text-[11px]">

                        <thead>

                          <tr className="bg-slate-900/30 text-slate-500 border-b border-slate-800 text-[9px] uppercase tracking-tighter">

                            {Object.keys(r.data[0]).map(k => <th key={k} className="px-4 py-3">{k}</th>)}

                          </tr>

                        </thead>

                        <tbody className="divide-y divide-slate-800/50">

                          {r.data.map((row, idx) => (

                            <tr key={idx} className="hover:bg-blue-500/10 transition-colors">

                              {Object.values(row).map((v, ki) => <td key={ki} className="px-4 py-2.5 text-slate-300">{String(v)}</td>)}

                            </tr>

                          ))}

                        </tbody>

                      </table>

                    ) : (

                      <div className="p-4 text-slate-600 text-xs italic">0 rows returned</div>

                    )}

                  </div>

                </div>

              ))}

            </div>

          </main>

        </div>

      );

    };


    ReactDOM.createRoot(document.getElementById('root')).render(<App />);

  </script>

</body>

</html>