hailsbop commited on
Commit
d36868b
·
verified ·
1 Parent(s): 3e098e5

Upload pages/index.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. pages/index.js +140 -0
pages/index.js ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState, useEffect } from 'react';
2
+ import Header from '../components/Header';
3
+ import ThreadList from '../components/ThreadList';
4
+ import ExportControls from '../components/ExportControls';
5
+ import { Database, FileJson, FileText, RefreshCw, CheckCircle, AlertCircle } from 'lucide-react';
6
+
7
+ // Mock data generator to simulate Perplexity threads
8
+ const generateMockThreads = (count) => {
9
+ return Array.from({ length: count }).map((_, i) => ({
10
+ id: `thread-${Date.now()}-${i}`,
11
+ title: `Research Topic #${i + 1}: Artificial Intelligence in ${['Medicine', 'Finance', 'Education', 'Climate'][i % 4]}`,
12
+ date: new Date(Date.now() - i * 86400000).toLocaleDateString(),
13
+ query: `How does AI impact ${['healthcare', 'banking', 'schools', 'environment'][i % 4]}?`,
14
+ citations: Math.floor(Math.random() * 15) + 2,
15
+ status: 'completed'
16
+ }));
17
+ };
18
+
19
+ export default function Home() {
20
+ const [threads, setThreads] = useState([]);
21
+ const [loading, setLoading] = useState(false);
22
+ const [exporting, setExporting] = useState(false);
23
+ const [exportProgress, setExportProgress] = useState(0);
24
+ const [selectedThreads, setSelectedThreads] = useState([]);
25
+ const [notification, setNotification] = useState(null);
26
+
27
+ useEffect(() => {
28
+ loadThreads();
29
+ }, []);
30
+
31
+ const loadThreads = async () => {
32
+ setLoading(true);
33
+ // Simulate API delay
34
+ setTimeout(() => {
35
+ setThreads(generateMockThreads(20));
36
+ setLoading(false);
37
+ }, 1000);
38
+ };
39
+
40
+ const toggleSelection = (id) => {
41
+ setSelectedThreads(prev =>
42
+ prev.includes(id) ? prev.filter(t => t !== id) : [...prev, id]
43
+ );
44
+ };
45
+
46
+ const handleExport = async (format) => {
47
+ if (selectedThreads.length === 0) {
48
+ setNotification({ type: 'error', message: 'Please select at least one thread.' });
49
+ return;
50
+ }
51
+
52
+ setExporting(true);
53
+ setExportProgress(0);
54
+
55
+ // Simulate batch processing
56
+ const totalSteps = 5;
57
+ for (let i = 1; i <= totalSteps; i++) {
58
+ await new Promise(r => setTimeout(r, 400));
59
+ setExportProgress((i / totalSteps) * 100);
60
+ }
61
+
62
+ // Trigger download
63
+ const dataToExport = threads.filter(t => selectedThreads.includes(t.id));
64
+ const content = format === 'json'
65
+ ? JSON.stringify(dataToExport, null, 2)
66
+ : dataToExport.map(t => `${t.id},${t.title},${t.query}`).join('\n');
67
+
68
+ const blob = new Blob([content], { type: format === 'json' ? 'application/json' : 'text/csv' });
69
+ const url = window.URL.createObjectURL(blob);
70
+ const a = document.createElement('a');
71
+ a.href = url;
72
+ a.download = `perplexity-batch-${Date.now()}.${format}`;
73
+ a.click();
74
+ window.URL.removeObjectURL(url);
75
+
76
+ setExporting(false);
77
+ setNotification({ type: 'success', message: `Successfully exported ${selectedThreads.length} threads as ${format.toUpperCase()}.` });
78
+ setSelectedThreads([]);
79
+ };
80
+
81
+ return (
82
+ <div className="min-h-screen flex flex-col font-sans">
83
+ <Header />
84
+
85
+ <main className="flex-1 p-6 max-w-7xl mx-auto w-full">
86
+ {/* Header Section */}
87
+ <div className="mb-8 flex flex-col md:flex-row md:items-center justify-between gap-4">
88
+ <div>
89
+ <h1 className="text-3xl font-bold text-white mb-2">Thread Exporter</h1>
90
+ <p className="text-gray-400">Manage and export your Perplexity research threads in batches.</p>
91
+ </div>
92
+ <div className="flex gap-3">
93
+ <button
94
+ onClick={loadThreads}
95
+ disabled={loading}
96
+ className="flex items-center gap-2 px-4 py-2 bg-slate-800 hover:bg-slate-700 text-white rounded-lg transition-colors disabled:opacity-50"
97
+ >
98
+ <RefreshCw size={18} className={loading ? 'animate-spin' : ''} />
99
+ Refresh
100
+ </button>
101
+ </div>
102
+ </div>
103
+
104
+ {/* Notification Toast */}
105
+ {notification && (
106
+ <div className={`mb-6 p-4 rounded-lg flex items-center gap-3 ${notification.type === 'success' ? 'bg-green-900/50 text-green-200 border border-green-800' : 'bg-red-900/50 text-red-200 border border-red-800'}`}>
107
+ {notification.type === 'success' ? <CheckCircle size={20} /> : <AlertCircle size={20} />}
108
+ {notification.message}
109
+ <button onClick={() => setNotification(null)} className="ml-auto hover:opacity-70">✕</button>
110
+ </div>
111
+ )}
112
+
113
+ {/* Controls */}
114
+ <ExportControls
115
+ count={selectedThreads.length}
116
+ onExport={handleExport}
117
+ exporting={exporting}
118
+ progress={exportProgress}
119
+ />
120
+
121
+ {/* Content Grid */}
122
+ <div className="mt-6">
123
+ {loading ? (
124
+ <div className="flex justify-center py-20">
125
+ <div className="animate-pulse bg-slate-800 h-4 w-4 rounded-full"></div>
126
+ <div className="animate-pulse bg-slate-800 h-4 w-4 rounded-full ml-2"></div>
127
+ <div className="animate-pulse bg-slate-800 h-4 w-4 rounded-full ml-2"></div>
128
+ </div>
129
+ ) : (
130
+ <ThreadList
131
+ threads={threads}
132
+ selectedIds={selectedThreads}
133
+ onSelect={toggleSelection}
134
+ />
135
+ )}
136
+ </div>
137
+ </main>
138
+ </div>
139
+ );
140
+ }