File size: 5,801 Bytes
2ecc4a7
73905db
 
 
 
 
 
 
 
dc7b682
2ecc4a7
 
 
 
 
 
 
 
73905db
2ecc4a7
73905db
2ecc4a7
 
73905db
 
 
 
 
 
 
 
2ecc4a7
 
 
f4970d3
 
 
 
 
 
 
 
 
2ecc4a7
 
 
 
 
 
 
bf7338b
2ecc4a7
 
 
 
 
 
 
 
 
f4970d3
 
 
 
 
 
 
 
 
2ecc4a7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73905db
 
2ecc4a7
73905db
2ecc4a7
 
 
 
 
73905db
2ecc4a7
73905db
2ecc4a7
 
 
 
 
 
 
73905db
2ecc4a7
73905db
2ecc4a7
 
 
73905db
 
 
2ecc4a7
73905db
2ecc4a7
73905db
2ecc4a7
 
 
 
 
 
 
73905db
 
 
 
 
 
 
 
 
 
2ecc4a7
 
 
 
 
 
73905db
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import React, { useState, useEffect, useRef } from 'react';
import { Boxes, User, Lock } from 'lucide-react';
import Navbar from './components/Navbar';
import IngestionPage from './pages/IngestionPage';
import SearchPlaygroundPage from './pages/SearchPlaygroundPage';
import AdvancedRagPage from './pages/AdvancedRagPage';
import VectorDbPage from './pages/VectorDbPage';
import FundamentalsPage from './pages/FundamentalsPage';
import RagasEvalPage from './pages/RagasEvalPage';

import { usePipelineStore } from './store/pipelineStore';
import { useAuthStore } from './store/authStore';
import api from './api/client';

function App() {
  const { isAuthenticated, login, logout } = useAuthStore();
  const [username, setUsername] = useState('admin');
  const [password, setPassword] = useState('admin123');
  const [activeTab, setActiveTab] = useState('search');

  const { setDocuments, activeJob, addStep } = usePipelineStore();
  const ws = useRef(null);

  // Sync hash routing
  useEffect(() => {
    const hash = window.location.hash.replace('#', '');
    if (['ingest', 'search', 'advanced', 'vectordb', 'fundamentals', 'eval'].includes(hash)) {
      setActiveTab(hash);
    }
  }, []);

  // Load documents
  useEffect(() => {
    if (isAuthenticated) {
      api.get('/ingest/documents').then(res => {
        setDocuments(res.data);
        const { selectedDoc, setSelectedDoc } = usePipelineStore.getState();
        if (res.data && res.data.length > 0) {
          if (!selectedDoc || !res.data.some(d => d.id === selectedDoc.id)) {
            setSelectedDoc(res.data[0]);
          }
        }
      }).catch(console.error);
    }
  }, [isAuthenticated]);

  // WebSocket Connection for Pipeline Trace
  useEffect(() => {
    if (activeJob && isAuthenticated) {
      const token = sessionStorage.getItem('token');
      const apiBase = import.meta.env.VITE_API_BASE_URL || window.location.origin;
      const wsProtocol = apiBase.startsWith('https') ? 'wss' : 'ws';
      const wsHost = apiBase.replace(/^https?:\/\//, '');
      const wsUrl = `${wsProtocol}://${wsHost}/ws/pipeline/${activeJob}?token=${token}`;
      ws.current = new WebSocket(wsUrl);

      ws.current.onmessage = (event) => {
        const step = JSON.parse(event.data);
        addStep(step);
        if (step.step === 'DONE') {
          api.get('/ingest/documents').then(res => {
            setDocuments(res.data);
            if (res.data && res.data.length > 0) {
              const newDoc = step.metadata?.doc_id 
                ? res.data.find(d => d.id === step.metadata.doc_id) || res.data[0]
                : res.data[0];
              usePipelineStore.getState().setSelectedDoc(newDoc);
            }
          }).catch(console.error);
        }
      };

      return () => {
        if (ws.current) ws.current.close();
      };
    }
  }, [activeJob, isAuthenticated]);

  const handleAuth = async (e) => {
    e.preventDefault();
    await login(username, password);
  };

  if (!isAuthenticated) {
    return (
      <div className="min-h-screen bg-surface-900 flex items-center justify-center p-4">
        <div className="w-full max-w-md card space-y-8 p-10 shadow-2xl">
          <div className="text-center space-y-2">
            <div className="inline-block p-4 bg-accent-500/10 rounded-full mb-4 ring-1 ring-accent-500/20">
              <Boxes className="w-12 h-12 text-accent-500" />
            </div>
            <h1 className="text-3xl font-bold text-white tracking-tight">RAG PLATFORM</h1>
            <p className="text-gray-400 font-mono text-xs">Enterprise A-to-Z RAG Ecosystem v3.0</p>
          </div>

          <form className="space-y-4" onSubmit={handleAuth}>
            <div className="space-y-1">
              <label className="text-xs uppercase font-bold text-gray-500">Username</label>
              <div className="relative">
                <User className="absolute left-3 top-3 w-4 h-4 text-gray-500" />
                <input
                  type="text" value={username} onChange={e => setUsername(e.target.value)}
                  className="w-full input-field pl-10" placeholder="admin"
                />
              </div>
            </div>
            <div className="space-y-1">
              <label className="text-xs uppercase font-bold text-gray-500">Password</label>
              <div className="relative">
                <Lock className="absolute left-3 top-3 w-4 h-4 text-gray-500" />
                <input
                  type="password" value={password} onChange={e => setPassword(e.target.value)}
                  className="w-full input-field pl-10" placeholder="••••••••"
                />
              </div>
            </div>
            <button className="w-full btn-accent font-bold py-3 mt-4 hover:scale-[1.02] active:scale-[0.98]">
              Login to Platform
            </button>
          </form>

          <div className="text-center pt-4 border-t border-surface-700">
            <p className="text-xs text-gray-500 font-mono tracking-widest">DEFAULT CREDENTIALS: admin / admin123</p>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className="min-h-screen bg-surface-950 text-gray-100 pb-20 selection:bg-accent-500/30 font-sans">
      <Navbar activeTab={activeTab} setActiveTab={setActiveTab} onLogout={logout} />

      <main className="transition-all duration-300">
        {activeTab === 'ingest' && <IngestionPage />}
        {activeTab === 'search' && <SearchPlaygroundPage />}
        {activeTab === 'advanced' && <AdvancedRagPage />}
        {activeTab === 'vectordb' && <VectorDbPage />}
        {activeTab === 'fundamentals' && <FundamentalsPage />}
        {activeTab === 'eval' && <RagasEvalPage />}
      </main>
    </div>
  );
}

export default App;