5uck1t's picture
Build a modern, production-ready web application that visually and functionally resembles Google Drive, but is specialized for document management, document collaboration, and document signing.
fcf32b9 verified
Raw History Blame Contribute Delete
8.61 kB
/** @jsx React.createElement */
/** @jsxFrag React.Fragment */
const { useState, useEffect } = React;
// Mock data
const mockDocuments = [
{
id: '1',
name: 'Contract Agreement.pdf',
type: 'pdf',
size: '2.4 MB',
lastModified: '2 hours ago',
status: 'pending',
sharedWith: ['john@example.com', 'jane@example.com'],
thumbnail: 'http://static.photos/document/320x240/1'
},
{
id: '2',
name: 'Project Proposal.docx',
type: 'doc',
size: '1.2 MB',
lastModified: '1 day ago',
status: 'draft',
sharedWith: [],
thumbnail: 'http://static.photos/document/320x240/2'
},
{
id: '3',
name: 'NDA Agreement.pdf',
type: 'pdf',
size: '3.1 MB',
lastModified: '1 week ago',
status: 'signed',
sharedWith: ['legal@example.com'],
thumbnail: 'http://static.photos/document/320x240/3'
},
{
id: '4',
name: 'Invoice March 2023.pdf',
type: 'pdf',
size: '1.8 MB',
lastModified: '3 days ago',
status: 'completed',
sharedWith: ['accounting@example.com'],
thumbnail: 'http://static.photos/document/320x240/4'
}
];
const App = () => {
const [documents, setDocuments] = useState(mockDocuments);
const [activeView, setActiveView] = useState('grid');
const [currentFolder, setCurrentFolder] = useState('my-documents');
const [showUploadModal, setShowUploadModal] = useState(false);
const [showSignatureModal, setShowSignatureModal] = useState(false);
const [selectedDocument, setSelectedDocument] = useState(null);
const [searchQuery, setSearchQuery] = useState('');
const filteredDocuments = documents.filter(doc =>
doc.name.toLowerCase().includes(searchQuery.toLowerCase())
);
const handleDocumentClick = (doc) => {
setSelectedDocument(doc);
};
const handleSignDocument = (doc) => {
setSelectedDocument(doc);
setShowSignatureModal(true);
};
const handleDeleteDocument = (id) => {
setDocuments(documents.filter(doc => doc.id !== id));
};
const handleShareDocument = (id) => {
// In a real app, this would open a share dialog
alert(`Sharing document ${id}`);
};
return (
<div className="flex flex-col h-screen">
<custom-navbar
onSearch={setSearchQuery}
onUploadClick={() => setShowUploadModal(true)}
/>
<div className="flex flex-1 overflow-hidden">
<custom-sidebar
currentFolder={currentFolder}
onChangeFolder={setCurrentFolder}
/>
<main className="flex-1 overflow-auto p-6">
<div className="flex justify-between items-center mb-6">
<h2 className="text-2xl font-semibold text-gray-800">
{currentFolder === 'my-documents' ? 'My Documents' :
currentFolder === 'shared' ? 'Shared with Me' :
currentFolder === 'pending' ? 'Pending Signatures' :
currentFolder === 'completed' ? 'Completed' : 'Trash'}
</h2>
<div className="flex space-x-2">
<button
onClick={() => setActiveView('grid')}
className={`p-2 rounded-full ${activeView === 'grid' ? 'bg-blue-100 text-blue-600' : 'text-gray-500 hover:bg-gray-100'}`}
>
<i data-feather="grid"></i>
</button>
<button
onClick={() => setActiveView('list')}
className={`p-2 rounded-full ${activeView === 'list' ? 'bg-blue-100 text-blue-600' : 'text-gray-500 hover:bg-gray-100'}`}
>
<i data-feather="list"></i>
</button>
</div>
</div>
{activeView === 'grid' ? (
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
{filteredDocuments.map(doc => (
<custom-document-card
key={doc.id}
document={doc}
onClick={() => handleDocumentClick(doc)}
onSign={() => handleSignDocument(doc)}
onDelete={() => handleDeleteDocument(doc.id)}
onShare={() => handleShareDocument(doc.id)}
/>
))}
</div>
) : (
<div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Modified</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Size</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{filteredDocuments.map(doc => (
<tr key={doc.id} className="hover:bg-gray-50 cursor-pointer" onClick={() => handleDocumentClick(doc)}>
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
<div className="flex-shrink-0 h-10 w-10">
{doc.type === 'pdf' ? (
<i data-feather="file-text" className="text-red-500"></i>
) : (
<i data-feather="file" className="text-blue-500"></i>
)}
</div>
<div className="ml-4">
<div className="text-sm font-medium text-gray-900">{doc.name}</div>
</div>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{doc.lastModified}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{doc.size}</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`status-badge status-${doc.status}`}>
{doc.status}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
<button
onClick={(e) => { e.stopPropagation(); handleSignDocument(doc); }}
className="text-blue-600 hover:text-blue-900 mr-3"
>
Sign
</button>
<button
onClick={(e) => { e.stopPropagation(); handleShareDocument(doc.id); }}
className="text-green-600 hover:text-green-900 mr-3"
>
Share
</button>
<button
onClick={(e) => { e.stopPropagation(); handleDeleteDocument(doc.id); }}
className="text-red-600 hover:text-red-900"
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</main>
</div>
{showUploadModal && (
<custom-file-upload
onClose={() => setShowUploadModal(false)}
onUploadComplete={(newDoc) => {
setDocuments([newDoc, ...documents]);
setShowUploadModal(false);
}}
/>
)}
{showSignatureModal && selectedDocument && (
<custom-signature-modal
document={selectedDocument}
onClose={() => setShowSignatureModal(false)}
onSignComplete={() => {
setDocuments(documents.map(doc =>
doc.id === selectedDocument.id ? {...doc, status: 'signed'} : doc
));
setShowSignatureModal(false);
}}
/>
)}
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));