Spaces:
Build error
Build error
Upload pages/index.js with huggingface_hub
Browse files- 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
|
| 4 |
-
import
|
| 5 |
-
import
|
|
|
|
| 6 |
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
citations: Math.floor(Math.random() * 15) + 2,
|
| 15 |
-
status: 'completed'
|
| 16 |
-
}));
|
| 17 |
-
};
|
| 18 |
|
| 19 |
export default function Home() {
|
| 20 |
-
const [
|
| 21 |
-
const [
|
| 22 |
-
const [
|
| 23 |
-
const [
|
| 24 |
-
const [selectedThreads, setSelectedThreads] = useState([]);
|
| 25 |
const [notification, setNotification] = useState(null);
|
| 26 |
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
-
const
|
| 32 |
-
|
| 33 |
-
// Simulate API delay
|
| 34 |
-
setTimeout(() => {
|
| 35 |
-
setThreads(generateMockThreads(20));
|
| 36 |
-
setLoading(false);
|
| 37 |
-
}, 1000);
|
| 38 |
};
|
| 39 |
|
| 40 |
-
const
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
);
|
|
|
|
| 44 |
};
|
| 45 |
|
| 46 |
-
const
|
| 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="
|
| 83 |
-
<
|
|
|
|
|
|
|
|
|
|
| 84 |
|
| 85 |
-
<
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
<
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
<RefreshCw size={18} className={loading ? 'animate-spin' : ''} />
|
| 99 |
-
Refresh
|
| 100 |
-
</button>
|
| 101 |
-
</div>
|
| 102 |
-
</div>
|
| 103 |
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
<div className=
|
| 107 |
-
{
|
| 108 |
-
|
| 109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
</div>
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
{/* Controls */}
|
| 114 |
-
<ExportControls
|
| 115 |
-
count={selectedThreads.length}
|
| 116 |
-
onExport={handleExport}
|
| 117 |
-
exporting={exporting}
|
| 118 |
-
progress={exportProgress}
|
| 119 |
-
/>
|
| 120 |
|
| 121 |
-
|
| 122 |
-
<div className="
|
| 123 |
-
{
|
| 124 |
-
|
| 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 |
-
|
| 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 |
}
|