UNPKG

realtimecursor

Version:

Real-time collaboration system with cursor tracking and approval workflow

269 lines (252 loc) 12.7 kB
import React, { useState, useEffect } from 'react'; import { Plus, FileText, Users, Clock, Zap, ArrowRight } from 'lucide-react'; import RealTimeEditor from './RealTimeEditor'; const DocumentLauncher = ({ user }) => { const [documents, setDocuments] = useState([]); const [showCreateForm, setShowCreateForm] = useState(false); const [newDocTitle, setNewDocTitle] = useState(''); const [activeDocument, setActiveDocument] = useState(null); const [loading, setLoading] = useState(false); useEffect(() => { loadDocuments(); }, []); const loadDocuments = async () => { try { const response = await fetch('http://localhost:3001/api/documents'); const data = await response.json(); setDocuments(data.documents || []); } catch (error) { console.error('Failed to load documents:', error); } }; const createDocument = async (e) => { e.preventDefault(); if (!newDocTitle.trim()) return; try { setLoading(true); const response = await fetch('http://localhost:3001/api/documents', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ title: newDocTitle.trim() }) }); if (response.ok) { const newDoc = await response.json(); setDocuments([newDoc, ...documents]); setNewDocTitle(''); setShowCreateForm(false); // Automatically open the new document setActiveDocument(newDoc.id); } } catch (error) { console.error('Failed to create document:', error); alert('Failed to create document'); } finally { setLoading(false); } }; const openDocument = (documentId) => { setActiveDocument(documentId); }; const closeDocument = () => { setActiveDocument(null); loadDocuments(); // Refresh the list }; if (activeDocument) { return ( <RealTimeEditor user={user} documentId={activeDocument} onBack={closeDocument} /> ); } return ( <div className="min-h-screen bg-gradient-to-br from-indigo-50 via-white to-purple-50"> <div className="max-w-7xl mx-auto px-4 py-8"> {/* Header */} <div className="mb-8"> <div className="bg-white/80 backdrop-blur-xl rounded-3xl shadow-2xl border border-white/20 p-8"> <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-6"> <div className="flex items-center space-x-6"> <div className="relative"> <div className="w-20 h-20 bg-gradient-to-br from-indigo-600 via-purple-600 to-pink-600 rounded-2xl flex items-center justify-center shadow-xl"> <Zap className="w-10 h-10 text-white" /> </div> <div className="absolute -top-2 -right-2 w-8 h-8 bg-green-500 rounded-full flex items-center justify-center"> <span className="text-white text-sm font-bold"></span> </div> </div> <div> <h1 className="text-4xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent"> Real-Time Collaboration </h1> <p className="text-xl text-gray-600 mt-2">Create and edit documents together in real-time</p> <div className="flex items-center space-x-4 mt-3"> <span className="inline-flex items-center px-4 py-2 bg-indigo-100 text-indigo-800 rounded-full text-sm font-semibold"> <FileText className="w-4 h-4 mr-2" /> {documents.length} Documents </span> <span className="inline-flex items-center px-4 py-2 bg-green-100 text-green-800 rounded-full text-sm font-semibold"> <Zap className="w-4 h-4 mr-2" /> Live Collaboration </span> </div> </div> </div> <button onClick={() => setShowCreateForm(true)} className="bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-700 hover:to-purple-700 text-white px-6 py-3 rounded-2xl font-semibold shadow-xl hover:shadow-2xl transform hover:scale-105 transition-all duration-200 flex items-center space-x-2" > <Plus className="w-5 h-5" /> <span>New Document</span> </button> </div> </div> </div> {/* Create Document Form */} {showCreateForm && ( <div className="mb-8"> <div className="bg-white/90 backdrop-blur-xl rounded-3xl shadow-2xl border border-white/20 p-8"> <h2 className="text-2xl font-bold text-gray-900 mb-6 flex items-center"> <Plus className="w-6 h-6 mr-3 text-indigo-500" /> Create New Document </h2> <form onSubmit={createDocument} className="space-y-6"> <input type="text" placeholder="Document Title" value={newDocTitle} onChange={(e) => setNewDocTitle(e.target.value)} className="w-full px-6 py-4 bg-white/50 border border-gray-200 rounded-2xl focus:outline-none focus:ring-4 focus:ring-indigo-500/20 focus:border-indigo-500 transition-all duration-200 text-lg" required autoFocus /> <div className="flex space-x-4"> <button type="submit" disabled={loading} className="bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-700 hover:to-purple-700 text-white px-8 py-4 rounded-2xl font-semibold shadow-xl hover:shadow-2xl transform hover:scale-105 transition-all duration-200 flex items-center space-x-2 disabled:opacity-50" > {loading ? ( <> <div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin"></div> <span>Creating...</span> </> ) : ( <> <Plus className="w-5 h-5" /> <span>Create Document</span> </> )} </button> <button type="button" onClick={() => setShowCreateForm(false)} className="bg-gray-500 hover:bg-gray-600 text-white px-8 py-4 rounded-2xl font-semibold shadow-lg hover:shadow-xl transition-all duration-200" > Cancel </button> </div> </form> </div> </div> )} {/* Documents Grid */} <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-8"> {documents.length === 0 ? ( <div className="col-span-full"> <div className="bg-white/60 backdrop-blur-xl rounded-3xl shadow-xl border border-white/20 p-12 text-center"> <div className="w-24 h-24 bg-gradient-to-br from-gray-200 to-gray-300 rounded-3xl flex items-center justify-center mx-auto mb-6"> <FileText className="w-12 h-12 text-gray-500" /> </div> <h3 className="text-2xl font-bold text-gray-900 mb-4">No Documents Yet</h3> <p className="text-gray-600 text-lg mb-6"> Create your first document to start collaborating in real-time </p> <button onClick={() => setShowCreateForm(true)} className="bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-700 hover:to-purple-700 text-white px-6 py-3 rounded-2xl font-semibold shadow-xl hover:shadow-2xl transform hover:scale-105 transition-all duration-200 flex items-center space-x-2 mx-auto" > <Plus className="w-5 h-5" /> <span>Create First Document</span> </button> </div> </div> ) : ( documents.map((doc) => ( <div key={doc.id} className="group"> <div className="bg-white/80 backdrop-blur-xl rounded-3xl shadow-xl border border-white/20 p-8 hover:shadow-2xl hover:scale-105 transition-all duration-300"> <div className="flex items-start justify-between mb-6"> <div className="w-16 h-16 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-2xl flex items-center justify-center shadow-lg"> <FileText className="w-8 h-8 text-white" /> </div> <div className="flex items-center space-x-2"> <div className="w-3 h-3 bg-green-500 rounded-full animate-pulse"></div> <span className="text-sm text-green-600 font-semibold">Live</span> </div> </div> <h3 className="text-2xl font-bold text-gray-900 mb-3">{doc.title}</h3> <div className="flex items-center justify-between mb-6"> <div className="flex items-center space-x-4"> <span className="flex items-center text-sm text-gray-500 bg-gray-100 px-3 py-2 rounded-full"> <Clock className="w-4 h-4 mr-2" /> {new Date(doc.updatedAt).toLocaleDateString()} </span> <span className="flex items-center text-sm text-gray-500 bg-gray-100 px-3 py-2 rounded-full"> <Zap className="w-4 h-4 mr-2" /> v{doc.version} </span> </div> </div> <button onClick={() => openDocument(doc.id)} className="w-full bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-700 hover:to-purple-700 text-white py-4 rounded-2xl font-semibold shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200 flex items-center justify-center space-x-2" > <span>Open Document</span> <ArrowRight className="w-5 h-5" /> </button> </div> </div> )) )} </div> {/* Features Section */} <div className="mt-16"> <div className="bg-white/60 backdrop-blur-xl rounded-3xl shadow-xl border border-white/20 p-8"> <h2 className="text-3xl font-bold text-gray-900 mb-8 text-center"> Real-Time Collaboration Features </h2> <div className="grid grid-cols-1 md:grid-cols-3 gap-8"> <div className="text-center"> <div className="w-16 h-16 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-2xl flex items-center justify-center mx-auto mb-4 shadow-lg"> <Users className="w-8 h-8 text-white" /> </div> <h3 className="text-xl font-bold text-gray-900 mb-2">Live Cursors</h3> <p className="text-gray-600">See where others are typing with colored cursors and user names</p> </div> <div className="text-center"> <div className="w-16 h-16 bg-gradient-to-br from-green-500 to-emerald-600 rounded-2xl flex items-center justify-center mx-auto mb-4 shadow-lg"> <Zap className="w-8 h-8 text-white" /> </div> <h3 className="text-xl font-bold text-gray-900 mb-2">Instant Sync</h3> <p className="text-gray-600">Changes appear instantly across all connected devices</p> </div> <div className="text-center"> <div className="w-16 h-16 bg-gradient-to-br from-purple-500 to-pink-600 rounded-2xl flex items-center justify-center mx-auto mb-4 shadow-lg"> <FileText className="w-8 h-8 text-white" /> </div> <h3 className="text-xl font-bold text-gray-900 mb-2">Auto Save</h3> <p className="text-gray-600">Never lose your work with automatic saving and version control</p> </div> </div> </div> </div> </div> </div> ); }; export default DocumentLauncher;