realtimecursor
Version:
Real-time collaboration system with cursor tracking and approval workflow
269 lines (252 loc) • 12.7 kB
JSX
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;