import { useState, useEffect, useCallback } from 'react'; import { X, Search, UserPlus, Shield, Trash2, Check } from 'lucide-react'; import { api } from '../../api/client'; import { useCollaborationStore } from '../../store/collaborationStore'; interface ShareModalProps { projectId: string; isOpen: boolean; onClose: () => void; } interface UserResult { id: string; username: string; } interface CollaboratorResult { id: string; username: string; permission: string; created_at: number; } export default function ShareModal({ projectId, isOpen, onClose }: ShareModalProps) { const [searchQuery, setSearchQuery] = useState(''); const [searchResults, setSearchResults] = useState([]); const [searching, setSearching] = useState(false); const [collaborators, setCollaborators] = useState([]); const [owner, setOwner] = useState<{ id: string; username: string } | null>(null); const [addingUser, setAddingUser] = useState(null); const [error, setError] = useState(null); const connected = useCollaborationStore((s) => s.connected); const fetchCollaborators = useCallback(async () => { try { const res = await api.request<{ owner: any; collaborators: CollaboratorResult[] }>('GET', `/projects/${projectId}/collaborators`); setOwner(res.owner); setCollaborators(res.collaborators); if (res.owner) { useCollaborationStore.getState().setOwner(res.owner); } } catch {} }, [projectId]); useEffect(() => { if (isOpen) { fetchCollaborators(); setSearchQuery(''); setSearchResults([]); setError(null); } }, [isOpen, fetchCollaborators]); useEffect(() => { if (searchQuery.length < 2) { setSearchResults([]); return; } const timer = setTimeout(async () => { setSearching(true); try { const res = await api.request<{ users: UserResult[] }>('GET', `/projects/collaborators/search?username=${encodeURIComponent(searchQuery)}`); setSearchResults(res.users); } catch { setSearchResults([]); } finally { setSearching(false); } }, 300); return () => clearTimeout(timer); }, [searchQuery]); const handleAddCollaborator = async (username: string) => { setAddingUser(username); setError(null); try { await api.request('POST', `/projects/${projectId}/collaborators`, { username, permission: 'edit' }); await fetchCollaborators(); setSearchQuery(''); setSearchResults([]); } catch (err: any) { setError(err.message || 'Failed to add collaborator'); } finally { setAddingUser(null); } }; const handleUpdatePermission = async (userId: string, permission: string) => { try { await api.request('PUT', `/projects/${projectId}/collaborators/${userId}`, { permission }); await fetchCollaborators(); } catch {} }; const handleRemoveCollaborator = async (userId: string) => { try { await api.request('DELETE', `/projects/${projectId}/collaborators/${userId}`); await fetchCollaborators(); } catch {} }; if (!isOpen) return null; return (

Share Project

{connected && ( Live )}
setSearchQuery(e.target.value)} placeholder="Search users by username..." className="w-full bg-surface-800 border border-surface-600 rounded-lg pl-9 pr-3 py-2 text-sm text-white placeholder-surface-500 focus:outline-none focus:border-primary-500" /> {searching && (
)}
{searchResults.length > 0 && (
{searchResults.map((user) => ( ))}
)}
{error && (
{error}
)}
{owner && (
{owner.username.charAt(0).toUpperCase()}
{owner.username} Owner
admin
)} {collaborators.map((collab) => (
{collab.username.charAt(0).toUpperCase()}
{collab.username}
))} {!owner && collaborators.length === 0 && (

No collaborators yet. Search for users to share this project.

)}
); }