| import React, { useState, useEffect } from 'react'; | |
| import { getJobsHistory } from '../services/api'; | |
| import { History, LoaderCircle } from 'lucide-react'; | |
| function HistoryPanel({ onSelectJob }) { | |
| const [history, setHistory] = useState([]); | |
| const [isLoading, setIsLoading] = useState(false); | |
| const [isOpen, setIsOpen] = useState(false); | |
| const fetchHistory = () => { | |
| setIsLoading(true); | |
| getJobsHistory() | |
| .then(response => { | |
| setHistory(response.data.filter(job => job.status === 'SUCCESS' || job.status === 'FAILED')); | |
| }) | |
| .catch(error => console.error("Failed to fetch history:", error)) | |
| .finally(() => setIsLoading(false)); | |
| }; | |
| const togglePanel = () => { | |
| const newIsOpen = !isOpen; | |
| setIsOpen(newIsOpen); | |
| if (newIsOpen) { | |
| fetchHistory(); | |
| } | |
| }; | |
| const handleSelect = (job) => { | |
| onSelectJob(job); | |
| setIsOpen(false); | |
| } | |
| return ( | |
| <> | |
| <button | |
| onClick={togglePanel} | |
| className="fixed bottom-6 right-6 bg-green-600 hover:bg-green-700 text-white p-4 rounded-full shadow-lg z-50 transition-transform hover:scale-110 focus:outline-none focus:ring-2 focus:ring-green-400" | |
| aria-label="Toggle analysis history" | |
| > | |
| <History className="w-8 h-8" /> | |
| </button> | |
| {isOpen && <div onClick={() => setIsOpen(false)} className="fixed inset-0 bg-black/50 z-30 transition-opacity"></div>} | |
| <div className={`fixed top-0 right-0 h-full bg-gray-900 border-l border-gray-700 shadow-2xl z-40 transition-transform duration-500 ease-in-out ${isOpen ? 'translate-x-0' : 'translate-x-full'} w-full md:w-96`}> | |
| <div className="p-4 border-b border-gray-700 flex justify-between items-center"> | |
| <h2 className="text-xl font-bold">Analysis History</h2> | |
| <button onClick={() => setIsOpen(false)} className="text-gray-400 hover:text-white text-3xl">×</button> | |
| </div> | |
| <div className="p-4 overflow-y-auto h-[calc(100%-4rem)]"> | |
| {isLoading ? ( | |
| <div className="flex justify-center items-center h-full pt-20"> | |
| <LoaderCircle className="w-8 h-8 animate-spin text-green-400" /> | |
| </div> | |
| ) : history.length === 0 ? ( | |
| <div className="text-center pt-20"> | |
| <p className="text-gray-500">No past analyses found.</p> | |
| <p className="text-xs text-gray-600 mt-2">Complete an analysis to see it here.</p> | |
| </div> | |
| ) : ( | |
| <ul className="space-y-3"> | |
| {history.map(job => ( | |
| <li | |
| key={job.id} | |
| onClick={() => handleSelect(job)} | |
| className="p-3 bg-gray-800 rounded-md cursor-pointer hover:bg-green-800/50 border border-transparent hover:border-green-400/50 transition-all" | |
| > | |
| <p className="font-bold text-white">{job.ticker}</p> | |
| <div className="flex justify-between text-xs text-gray-400 mt-1"> | |
| <span>{new Date(job.created_at).toLocaleString('en-IN', { dateStyle: 'medium', timeStyle: 'short' })}</span> | |
| <span className={`font-semibold px-2 py-0.5 rounded-full ${job.status === 'SUCCESS' ? 'bg-green-900/50 text-green-300' : 'bg-red-900/50 text-red-300'}`}>{job.status}</span> | |
| </div> | |
| </li> | |
| ))} | |
| </ul> | |
| )} | |
| </div> | |
| </div> | |
| </> | |
| ); | |
| } | |
| export default HistoryPanel; |