hailsbop commited on
Commit
b3fbe46
·
verified ·
1 Parent(s): f539c73

Upload pages/index.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. pages/index.js +71 -118
pages/index.js CHANGED
@@ -1,140 +1,93 @@
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
  }
 
1
  import { useState, useEffect } from 'react';
2
  import Header from '../components/Header';
3
+ import Sidebar from '../components/Sidebar';
4
+ import EmailList from '../components/EmailList';
5
+ import EmailDetail from '../components/EmailDetail';
6
+ import { Search, Plus, Filter, CheckCircle2, AlertCircle } from 'lucide-react';
7
 
8
+ const MOCK_EMAILS = [
9
+ { id: '1', sender: 'Google Cloud', subject: 'Your monthly billing statement', body: 'Hello, your Google Cloud statement for October is now available for review...', date: '10:45 AM', read: false, label: 'Finance', priority: 'high' },
10
+ { id: '2', sender: 'LinkedIn', subject: 'New connection request from Sarah', body: 'Sarah Jenkins wants to connect with you. She is a Senior Engineer at TechCorp...', date: 'Yesterday', read: true, label: 'Social', priority: 'low' },
11
+ { id: '3', sender: 'GitHub', subject: '[Security] Dependabot alert for your repo', body: 'Dependabot has found a vulnerability in one of your dependencies. Please update...', date: 'Oct 24', read: false, label: 'Work', priority: 'high' },
12
+ { id: '4', sender: 'Netflix', subject: 'New arrival: Stranger Things Season 5', body: 'The wait is finally over. Dive back into the Upside Down now on Netflix...', date: 'Oct 23', read: true, label: 'Promotions', priority: 'low' },
13
+ { id: '5', sender: 'Amazon', subject: 'Your package has been delivered', body: 'Good news! Your order #123-456 has been delivered to your front porch...', date: 'Oct 22', read: true, label: 'Finance', priority: 'medium' },
14
+ ];
 
 
 
 
15
 
16
  export default function Home() {
17
+ const [emails, setEmails] = useState(MOCK_EMAILS);
18
+ const [selectedId, setSelectedId] = useState(null);
19
+ const [searchQuery, setSearchQuery] = useState('');
20
+ const [activeFilter, setActiveFilter] = useState('All');
 
21
  const [notification, setNotification] = useState(null);
22
 
23
+ const filteredEmails = emails.filter(email => {
24
+ const matchesSearch = email.sender.toLowerCase().includes(searchQuery.toLowerCase()) ||
25
+ email.subject.toLowerCase().includes(searchQuery.toLowerCase());
26
+ const matchesFilter = activeFilter === 'All' || email.label === activeFilter;
27
+ return matchesSearch && matchesFilter;
28
+ });
29
 
30
+ const markAsRead = (id) => {
31
+ setEmails(prev => prev.map(e => e.id === id ? { ...e, read: true } : e));
 
 
 
 
 
32
  };
33
 
34
+ const deleteEmail = (id) => {
35
+ setEmails(prev => prev.filter(e => e.id !== id));
36
+ if (selectedId === id) setSelectedId(null);
37
+ setNotification({ type: 'success', message: 'Email moved to trash' });
38
+ setTimeout(() => setNotification(null), 3000);
39
  };
40
 
41
+ const selectedEmail = emails.find(e => e.id === selectedId);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
  return (
44
+ <div className="flex h-screen overflow-hidden bg-slate-50">
45
+ <Sidebar
46
+ activeFilter={activeFilter}
47
+ setActiveFilter={setActiveFilter}
48
+ />
49
 
50
+ <div className="flex-1 flex flex-col min-w-0">
51
+ <Header
52
+ searchQuery={searchQuery}
53
+ setSearchQuery={setSearchQuery}
54
+ />
55
+
56
+ <main className="flex-1 flex overflow-hidden">
57
+ <EmailList
58
+ emails={filteredEmails}
59
+ selectedId={selectedId}
60
+ setSelectedId={(id) => {
61
+ setSelectedId(id);
62
+ markAsRead(id);
 
 
 
 
 
63
 
64
+ />
65
+
66
+ <div className="flex-1 bg-white border-l border-slate-200 overflow-y-auto">
67
+ {selectedEmail ? (
68
+ <EmailDetail
69
+ email={selectedEmail}
70
+ onDelete={() => deleteEmail(selectedEmail.id)}
71
+ />
72
+ ) : (
73
+ <div className="h-full flex flex-col items-center justify-center text-slate-400 p-8 text-center">
74
+ <div className="bg-slate-100 p-6 rounded-full mb-4">
75
+ <Search size={48} className="text-slate-300" />
76
+ </div>
77
+ <h3 className="text-lg font-medium text-slate-600">No email selected</h3>
78
+ <p className="max-w-xs">Select an email from the list to read its contents or manage the conversation.</p>
79
+ </div>
80
+ )}
81
  </div>
82
+ </main>
83
+ </div>
 
 
 
 
 
 
 
84
 
85
+ {notification && (
86
+ <div className="fixed bottom-6 left-1/2 -translate-x-1/2 z-50 flex items-center gap-3 px-4 py-3 rounded-lg shadow-2xl animate-bounce bg-slate-900 text-white border border-slate-700">
87
+ {notification.type === 'success' ? <CheckCircle2 size={18} className="text-green-400" /> : <AlertCircle size={18} className="text-red-400" />}
88
+ <span className="text-sm font-medium">{notification.message}</span>
 
 
 
 
 
 
 
 
 
 
 
89
  </div>
90
+ )}
91
  </div>
92
  );
93
  }