File size: 4,339 Bytes
c3bf538
 
 
 
 
 
 
 
 
8427d6e
 
c3bf538
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8427d6e
 
c3bf538
 
 
 
 
 
683938d
c3bf538
 
 
683938d
 
 
 
 
 
c3bf538
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
683938d
c3bf538
 
 
 
 
 
 
 
 
 
 
 
 
8427d6e
 
683938d
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
import React, { useState, useEffect } from 'react';
import Header from './components/Header';
import JobForm from './components/JobForm';
import JobStatusCard from './components/JobStatusCard';
import ResultsDisplay from './components/ResultsDisplay';
import LoadingSkeleton from './components/LoadingSkeleton';
import HistoryPanel from './components/HistoryPanel';
import { createJob, getJob } from './services/api';
import { XCircle } from 'lucide-react'; 

function App() {
  const [job, setJob] = useState(null);
  const [isLoading, setIsLoading] = useState(false); 
  const [isPolling, setIsPolling] = useState(false); 
  const [error, setError] = useState(null);

  const handleAnalysisRequest = async (ticker) => {
    setIsLoading(true);
    setIsPolling(true);
    setError(null);
    setJob(null);
    try {
      const response = await createJob(ticker);
      setJob(response.data);
    } catch (err) {
      setError('Failed to create job. Please check the API server and try again.');
      setIsLoading(false);
      setIsPolling(false);
    }
  };

  const handleSelectHistoryJob = (historyJob) => {
    setIsLoading(false);
    setIsPolling(false);
    setError(null);
    setJob(historyJob);
  }

  useEffect(() => {
    if (!job?.id || !isPolling) return;

    if (job.status !== 'PENDING') {
      setIsLoading(false);
    }

    const intervalId = setInterval(async () => {
      try {
        const response = await getJob(job.id);
        const updatedJob = response.data;
        setJob(updatedJob);

        if (updatedJob.status === 'SUCCESS' || updatedJob.status === 'FAILED') {
          clearInterval(intervalId);
          setIsPolling(false);
        }
      } catch (err) {
        setError('Failed to poll job status.');
        clearInterval(intervalId);
        setIsPolling(false);
      }
    }, 3000);

    return () => clearInterval(intervalId);
  }, [job, isPolling]);

  return (
    <div className="min-h-screen bg-gray-900 text-white font-sans">
      <Header />
      <HistoryPanel onSelectJob={handleSelectHistoryJob} />
      
      <main className="container mx-auto p-4 md:p-8">
        <div className="max-w-4xl mx-auto">
          <p className="text-lg text-gray-400 mb-4 text-center">
            Enter an Indian stock ticker to receive a comprehensive, AI-powered analysis.
          </p>
          
          {/* --- NEW, SIMPLE TEXT EXAMPLE LINE --- */}
          <p className="text-sm text-gray-500 text-center mb-8">
            e.g., RELIANCE.NS, TCS.NS, INFY.NS, HDFCBANK.NS
          </p>
          {/* --- END NEW LINE --- */}
          
          <JobForm onAnalyze={handleAnalysisRequest} isLoading={isLoading || isPolling} />
          
          {error && <div className="my-6 p-4 bg-red-900/50 rounded-lg text-red-300 text-center">{error}</div>}
          
          {isLoading && !job && <LoadingSkeleton />}
          
          {job && !isLoading && <JobStatusCard job={job} />}

          {job?.status === 'SUCCESS' && job.result && (
            <ResultsDisplay result={job.result} />
          )}

          {job?.status === 'FAILED' && job.result?.error && (
             <div className="mt-8 p-6 bg-gray-800/30 border border-red-500/30 rounded-lg text-center animate-fade-in">
               <XCircle className="w-16 h-16 text-red-400 mx-auto mb-4" />
               <h2 className="text-2xl font-bold text-red-300 mb-2">Analysis Failed</h2>
               <p className="text-gray-400 max-w-lg mx-auto">
                 We couldn't complete the analysis for <strong className="text-white">{job.ticker}</strong>.
                 This usually means the stock symbol is incorrect or not listed.
               </p>
               <p className="text-xs text-gray-500 mt-4">Please double-check the ticker and try again.</p>
               
               <details className="mt-6 text-left w-full max-w-lg mx-auto">
                 <summary className="cursor-pointer text-xs text-gray-500 hover:text-gray-400 focus:outline-none">Show technical details</summary>
                 <pre className="mt-2 bg-gray-900 p-4 rounded-md text-gray-400 text-xs whitespace-pre-wrap font-mono">
                   {job.result.error}
                 </pre>
               </details>
             </div>
          )}
        </div>
      </main>
    </div>
  );
}

export default App;