Seth0330 commited on
Commit
bb07183
·
verified ·
1 Parent(s): e4fb177

Update frontend/src/pages/History.jsx

Browse files
Files changed (1) hide show
  1. frontend/src/pages/History.jsx +100 -43
frontend/src/pages/History.jsx CHANGED
@@ -1,6 +1,6 @@
1
  // frontend/src/pages/History.jsx
2
 
3
- import React, { useState } from "react";
4
  import { motion, AnimatePresence } from "framer-motion";
5
  import {
6
  FileText,
@@ -43,14 +43,14 @@ import {
43
  DropdownMenuTrigger,
44
  } from "@/components/ui/dropdown-menu";
45
  import { cn } from "@/lib/utils";
 
46
 
47
- // minimal toast
48
  const toastSuccess = (msg) => {
49
  console.log(msg);
50
  };
51
 
52
- // Mock history data with detailed timing
53
- const mockHistory = [
54
  {
55
  id: 1,
56
  fileName: "Invoice_Acme_Corp_2024.pdf",
@@ -137,9 +137,7 @@ const mockHistory = [
137
  },
138
  errorMessage: "Unable to detect text in image. Image quality too low.",
139
  },
140
- ];
141
-
142
- const stageConfig = {
143
  uploading: { label: "Uploading", icon: Upload, color: "blue" },
144
  aiAnalysis: { label: "AI Analysis", icon: Cpu, color: "violet" },
145
  dataExtraction: { label: "Data Extraction", icon: TableProperties, color: "emerald" },
@@ -159,9 +157,32 @@ export default function History() {
159
  const [selectedStatus, setSelectedStatus] = useState("all");
160
  const [expandedReport, setExpandedReport] = useState(null);
161
  const [isExporting, setIsExporting] = useState(false);
162
-
163
- const filteredHistory = mockHistory.filter((item) => {
164
- const matchesSearch = item.fileName.toLowerCase().includes(searchQuery.toLowerCase());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  const matchesStatus = selectedStatus === "all" || item.status === selectedStatus;
166
  return matchesSearch && matchesStatus;
167
  });
@@ -479,32 +500,41 @@ export default function History() {
479
 
480
  {/* Stats Overview */}
481
  <div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
482
- {[
483
- {
484
- label: "Total Extractions",
485
- value: "247",
486
- change: "+12 today",
487
- color: "indigo",
488
- },
489
- {
490
- label: "Success Rate",
491
- value: "98.5%",
492
- change: "+0.3%",
493
- color: "emerald",
494
- },
495
- {
496
- label: "Avg. Processing Time",
497
- value: "2.8s",
498
- change: "-0.2s",
499
- color: "violet",
500
- },
501
- {
502
- label: "Fields Extracted",
503
- value: "4,521",
504
- change: "+156 today",
505
- color: "amber",
506
- },
507
- ].map((stat, index) => (
 
 
 
 
 
 
 
 
 
508
  <motion.div
509
  key={stat.label}
510
  initial={{ opacity: 0, y: 20 }}
@@ -518,11 +548,27 @@ export default function History() {
518
  {stat.change}
519
  </p>
520
  </motion.div>
521
- ))}
 
522
  </div>
523
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
  {/* History List */}
525
- <div className="space-y-4">
 
526
  {filteredHistory.map((item, index) => (
527
  <motion.div
528
  key={item.id}
@@ -857,12 +903,23 @@ export default function History() {
857
  ))}
858
  </div>
859
 
860
- {filteredHistory.length === 0 && (
861
- <div className="text-center py-16">
862
- <div className="h-20 w-20 mx-auto rounded-2xl bg-slate-100 flex items-center justify-center mb-4">
863
- <FileText className="h-10 w-10 text-slate-300" />
 
 
 
 
 
 
 
 
 
 
 
864
  </div>
865
- <p className="text-slate-500">No extractions found</p>
866
  </div>
867
  )}
868
  </div>
 
1
  // frontend/src/pages/History.jsx
2
 
3
+ import React, { useState, useEffect } from "react";
4
  import { motion, AnimatePresence } from "framer-motion";
5
  import {
6
  FileText,
 
43
  DropdownMenuTrigger,
44
  } from "@/components/ui/dropdown-menu";
45
  import { cn } from "@/lib/utils";
46
+ import { getHistory } from "@/services/api";
47
 
48
+ // minimal "toast"
49
  const toastSuccess = (msg) => {
50
  console.log(msg);
51
  };
52
 
53
+ const stageConfig = {
 
54
  {
55
  id: 1,
56
  fileName: "Invoice_Acme_Corp_2024.pdf",
 
137
  },
138
  errorMessage: "Unable to detect text in image. Image quality too low.",
139
  },
140
+ ]; // Keep for fallback/initial state
 
 
141
  uploading: { label: "Uploading", icon: Upload, color: "blue" },
142
  aiAnalysis: { label: "AI Analysis", icon: Cpu, color: "violet" },
143
  dataExtraction: { label: "Data Extraction", icon: TableProperties, color: "emerald" },
 
157
  const [selectedStatus, setSelectedStatus] = useState("all");
158
  const [expandedReport, setExpandedReport] = useState(null);
159
  const [isExporting, setIsExporting] = useState(false);
160
+ const [history, setHistory] = useState([]);
161
+ const [isLoading, setIsLoading] = useState(true);
162
+ const [error, setError] = useState(null);
163
+
164
+ // Fetch history on component mount
165
+ useEffect(() => {
166
+ const fetchHistory = async () => {
167
+ setIsLoading(true);
168
+ setError(null);
169
+ try {
170
+ const data = await getHistory();
171
+ setHistory(data);
172
+ } catch (err) {
173
+ console.error("Failed to fetch history:", err);
174
+ setError(err.message || "Failed to load history");
175
+ setHistory([]); // Fallback to empty array
176
+ } finally {
177
+ setIsLoading(false);
178
+ }
179
+ };
180
+
181
+ fetchHistory();
182
+ }, []);
183
+
184
+ const filteredHistory = history.filter((item) => {
185
+ const matchesSearch = item.fileName?.toLowerCase().includes(searchQuery.toLowerCase()) ?? false;
186
  const matchesStatus = selectedStatus === "all" || item.status === selectedStatus;
187
  return matchesSearch && matchesStatus;
188
  });
 
500
 
501
  {/* Stats Overview */}
502
  <div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
503
+ {(() => {
504
+ const total = history.length;
505
+ const completed = history.filter((h) => h.status === "completed").length;
506
+ const successRate = total > 0 ? ((completed / total) * 100).toFixed(1) : 0;
507
+ const avgTime = history.length > 0
508
+ ? history.reduce((sum, h) => sum + (h.totalTime || 0), 0) / history.length
509
+ : 0;
510
+ const totalFields = history.reduce((sum, h) => sum + (h.fieldsExtracted || 0), 0);
511
+
512
+ return [
513
+ {
514
+ label: "Total Extractions",
515
+ value: total.toString(),
516
+ change: "",
517
+ color: "indigo",
518
+ },
519
+ {
520
+ label: "Success Rate",
521
+ value: `${successRate}%`,
522
+ change: total > 0 ? `${completed}/${total} successful` : "No data",
523
+ color: "emerald",
524
+ },
525
+ {
526
+ label: "Avg. Processing Time",
527
+ value: avgTime >= 1000 ? `${(avgTime / 1000).toFixed(1)}s` : `${Math.round(avgTime)}ms`,
528
+ change: "",
529
+ color: "violet",
530
+ },
531
+ {
532
+ label: "Fields Extracted",
533
+ value: totalFields.toLocaleString(),
534
+ change: "",
535
+ color: "amber",
536
+ },
537
+ ].map((stat, index) => (
538
  <motion.div
539
  key={stat.label}
540
  initial={{ opacity: 0, y: 20 }}
 
548
  {stat.change}
549
  </p>
550
  </motion.div>
551
+ ));
552
+ })()}
553
  </div>
554
 
555
+ {/* Loading State */}
556
+ {isLoading && (
557
+ <div className="text-center py-16">
558
+ <motion.div
559
+ animate={{ rotate: 360 }}
560
+ transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
561
+ className="h-16 w-16 mx-auto rounded-2xl bg-indigo-100 flex items-center justify-center mb-4"
562
+ >
563
+ <Cpu className="h-8 w-8 text-indigo-600" />
564
+ </motion.div>
565
+ <p className="text-slate-500">Loading extraction history...</p>
566
+ </div>
567
+ )}
568
+
569
  {/* History List */}
570
+ {!isLoading && (
571
+ <div className="space-y-4">
572
  {filteredHistory.map((item, index) => (
573
  <motion.div
574
  key={item.id}
 
903
  ))}
904
  </div>
905
 
906
+ {filteredHistory.length === 0 && !error && (
907
+ <div className="text-center py-16">
908
+ <div className="h-20 w-20 mx-auto rounded-2xl bg-slate-100 flex items-center justify-center mb-4">
909
+ <FileText className="h-10 w-10 text-slate-300" />
910
+ </div>
911
+ <p className="text-slate-500 mb-2">
912
+ {history.length === 0
913
+ ? "No extraction history yet"
914
+ : "No extractions match your filters"}
915
+ </p>
916
+ {history.length === 0 && (
917
+ <p className="text-sm text-slate-400">
918
+ Upload a document to get started
919
+ </p>
920
+ )}
921
  </div>
922
+ )}
923
  </div>
924
  )}
925
  </div>