import React, { useState, useEffect } from 'react'; import { Lock, Globe, Users, UserPlus, Shield, Trash2, CheckCircle2, Copy, Key, X, RefreshCw, AlertCircle } from 'lucide-react'; import { api } from '../api/client'; export default function AccessManagementModal({ workspace, isOpen, onClose, onWorkspaceUpdated }) { const [accessData, setAccessData] = useState(null); const [loading, setLoading] = useState(true); const [visibility, setVisibility] = useState(workspace?.visibility || 'private'); const [savingVis, setSavingVis] = useState(false); // Invite state const [inviteEmail, setInviteEmail] = useState(''); const [inviteRole, setInviteRole] = useState('EDITOR'); const [inviting, setInviting] = useState(false); const [errorMsg, setErrorMsg] = useState(''); const [successMsg, setSuccessMsg] = useState(''); // Scoped Token state const [generatedToken, setGeneratedToken] = useState(''); const [copiedToken, setCopiedToken] = useState(false); useEffect(() => { if (isOpen && workspace?.id) { loadAccess(); } }, [isOpen, workspace?.id]); const loadAccess = async () => { setLoading(true); setErrorMsg(''); try { const data = await api.getWorkspaceAccess(workspace.id); setAccessData(data); setVisibility(data.visibility || 'private'); } catch (err) { setErrorMsg(`Could not load access data: ${err.message}`); } finally { setLoading(false); } }; const handleVisibilityChange = async (newVis) => { if (newVis === visibility) return; setSavingVis(true); setErrorMsg(''); setSuccessMsg(''); try { await api.updateWorkspaceVisibility(workspace.id, newVis); setVisibility(newVis); setSuccessMsg(`Workspace is now ${newVis.toUpperCase()}.`); if (onWorkspaceUpdated) { onWorkspaceUpdated({ ...workspace, visibility: newVis }); } } catch (err) { setErrorMsg(`Failed to update visibility: ${err.message}`); } finally { setSavingVis(false); } }; const handleInvite = async (e) => { e.preventDefault(); if (!inviteEmail.trim()) return; setInviting(true); setErrorMsg(''); setSuccessMsg(''); try { await api.addWorkspaceMember(workspace.id, inviteEmail.trim(), inviteRole); setInviteEmail(''); setSuccessMsg(`Collaborator added successfully.`); await loadAccess(); } catch (err) { setErrorMsg(`Could not add collaborator: ${err.message}`); } finally { setInviting(false); } }; const handleRemoveMember = async (memberId) => { try { await api.removeWorkspaceMember(workspace.id, memberId); setSuccessMsg('Collaborator removed.'); await loadAccess(); } catch (err) { setErrorMsg(`Failed to remove: ${err.message}`); } }; const handleUpdateRole = async (memberId, newRole) => { try { await api.updateWorkspaceMemberRole(workspace.id, memberId, newRole); setSuccessMsg(`Role updated to ${newRole}.`); await loadAccess(); } catch (err) { setErrorMsg(`Failed to update role: ${err.message}`); } }; const handleGenerateToken = () => { const token = `dw_ws_${workspace.id.replace(/-/g, '').slice(0, 12)}_${Math.random().toString(36).substring(2, 10)}`; setGeneratedToken(token); setCopiedToken(false); }; const handleCopyToken = () => { if (!generatedToken) return; navigator.clipboard.writeText(generatedToken); setCopiedToken(true); setTimeout(() => setCopiedToken(false), 2500); }; if (!isOpen) return null; return (
Control who can query, commit changes, and review Knowledge PRs in this repository.
Generate a dedicated workspace token for Claude, Cursor, or terminal CI pipelines.