UNPKG

sourabhrealtime

Version:

ROBUST RICH TEXT EDITOR: Single-pane contentEditable with direct text selection formatting, speech features, undo/redo, professional UI - Perfect TipTap alternative

281 lines (233 loc) 8.07 kB
// Project management with tracking and collaboration export class ProjectManager { constructor() { this.projects = new Map(); this.projectMembers = new Map(); this.projectInvitations = new Map(); this.projectActivity = new Map(); this.projectVersions = new Map(); } // Create new project createProject(projectData, creatorId) { const project = { id: 'proj_' + Math.random().toString(36).substr(2, 9), name: projectData.name, description: projectData.description, createdBy: creatorId, createdAt: Date.now(), updatedAt: Date.now(), status: 'active', content: '<h1>Welcome to your new project!</h1><p>Start collaborating...</p>', version: 1, settings: { allowComments: true, allowEditing: true, trackChanges: true, autoSave: true } }; this.projects.set(project.id, project); // Add creator as admin this.addProjectMember(project.id, creatorId, 'admin'); // Initialize activity tracking this.projectActivity.set(project.id, []); this.projectVersions.set(project.id, [{ version: 1, content: project.content, createdBy: creatorId, createdAt: Date.now(), changes: 'Project created' }]); this.logActivity(project.id, creatorId, 'project_created', { projectName: project.name }); return project; } // Add member to project addProjectMember(projectId, userId, role = 'editor') { if (!this.projectMembers.has(projectId)) { this.projectMembers.set(projectId, new Map()); } const members = this.projectMembers.get(projectId); members.set(userId, { userId, role, joinedAt: Date.now(), lastActivity: Date.now(), permissions: this.getRolePermissions(role) }); this.logActivity(projectId, userId, 'member_added', { role }); } // Get role permissions getRolePermissions(role) { const permissions = { admin: ['read', 'write', 'delete', 'invite', 'manage'], editor: ['read', 'write', 'comment'], viewer: ['read', 'comment'], commenter: ['read', 'comment'] }; return permissions[role] || permissions.viewer; } // Send project invitation sendInvitation(projectId, invitedUserId, invitedByUserId, role = 'editor') { const project = this.projects.get(projectId); if (!project) return null; const invitation = { id: 'inv_' + Math.random().toString(36).substr(2, 9), projectId, projectName: project.name, invitedUserId, invitedByUserId, role, status: 'pending', createdAt: Date.now(), expiresAt: Date.now() + (7 * 24 * 60 * 60 * 1000) // 7 days }; if (!this.projectInvitations.has(invitedUserId)) { this.projectInvitations.set(invitedUserId, []); } this.projectInvitations.get(invitedUserId).push(invitation); this.logActivity(projectId, invitedByUserId, 'invitation_sent', { invitedUserId, role }); return invitation; } // Accept invitation acceptInvitation(invitationId, userId) { const userInvitations = this.projectInvitations.get(userId) || []; const invitation = userInvitations.find(inv => inv.id === invitationId); if (!invitation || invitation.status !== 'pending') { return { success: false, message: 'Invalid invitation' }; } if (invitation.expiresAt < Date.now()) { return { success: false, message: 'Invitation expired' }; } // Add user to project this.addProjectMember(invitation.projectId, userId, invitation.role); // Update invitation status invitation.status = 'accepted'; invitation.acceptedAt = Date.now(); this.logActivity(invitation.projectId, userId, 'invitation_accepted', { role: invitation.role }); return { success: true, project: this.projects.get(invitation.projectId) }; } // Update project content with version tracking updateContent(projectId, userId, newContent, changes = '') { const project = this.projects.get(projectId); if (!project) return false; const member = this.projectMembers.get(projectId)?.get(userId); if (!member || !member.permissions.includes('write')) { return false; } // Create new version const newVersion = project.version + 1; project.content = newContent; project.version = newVersion; project.updatedAt = Date.now(); // Store version history const versions = this.projectVersions.get(projectId) || []; versions.push({ version: newVersion, content: newContent, createdBy: userId, createdAt: Date.now(), changes: changes || 'Content updated' }); this.projectVersions.set(projectId, versions); // Update member activity member.lastActivity = Date.now(); this.logActivity(projectId, userId, 'content_updated', { version: newVersion, changes }); return true; } // Log project activity logActivity(projectId, userId, action, metadata = {}) { if (!this.projectActivity.has(projectId)) { this.projectActivity.set(projectId, []); } const activity = { id: 'act_' + Math.random().toString(36).substr(2, 9), projectId, userId, action, metadata, timestamp: Date.now() }; const activities = this.projectActivity.get(projectId); activities.unshift(activity); // Add to beginning // Keep only last 1000 activities if (activities.length > 1000) { activities.splice(1000); } } // Get user's projects getUserProjects(userId) { const userProjects = []; for (const [projectId, members] of this.projectMembers.entries()) { if (members.has(userId)) { const project = this.projects.get(projectId); const member = members.get(userId); if (project) { userProjects.push({ ...project, userRole: member.role, userPermissions: member.permissions, memberCount: members.size, lastActivity: member.lastActivity }); } } } return userProjects.sort((a, b) => b.updatedAt - a.updatedAt); } // Get user's invitations getUserInvitations(userId) { return this.projectInvitations.get(userId) || []; } // Get project activity getProjectActivity(projectId, limit = 50) { const activities = this.projectActivity.get(projectId) || []; return activities.slice(0, limit); } // Get project members getProjectMembers(projectId) { const members = this.projectMembers.get(projectId); if (!members) return []; return Array.from(members.values()); } // Get project versions getProjectVersions(projectId, limit = 10) { const versions = this.projectVersions.get(projectId) || []; return versions.slice(-limit).reverse(); } // Check user permission hasPermission(projectId, userId, permission) { const member = this.projectMembers.get(projectId)?.get(userId); return member?.permissions.includes(permission) || false; } // Get project analytics getProjectAnalytics(projectId) { const project = this.projects.get(projectId); const members = this.projectMembers.get(projectId); const activities = this.projectActivity.get(projectId) || []; const versions = this.projectVersions.get(projectId) || []; if (!project || !members) return null; const now = Date.now(); const dayMs = 24 * 60 * 60 * 1000; const weekMs = 7 * dayMs; return { totalMembers: members.size, totalVersions: versions.length, totalActivities: activities.length, activitiesLastWeek: activities.filter(a => now - a.timestamp < weekMs).length, activitiesLastDay: activities.filter(a => now - a.timestamp < dayMs).length, activeMembers: Array.from(members.values()).filter(m => now - m.lastActivity < weekMs).length, lastUpdated: project.updatedAt, createdAt: project.createdAt }; } }