// Finish → Co-teachers: let other accounts edit this study with you. You make a single-use invite link; // whoever opens it (signed in) can edit the study, and you can remove them any time. Edits from both of you // sync through the account; if you both change the same study, "Combine both" keeps everything. import { useCallback, useEffect, useState } from 'react'; import { createEditInvite, getCollaborators, removeCollaborator } from '../syncService.js'; const btn = 'rounded-xl border border-[var(--r-line)] px-4 py-2 text-sm hover:bg-black/5 disabled:opacity-50'; export default function CoTeachers({ projectId, sharedBy, onLeave }) { const [state, setState] = useState({ status: 'loading', collaborators: [], pendingInvites: 0 }); const [invite, setInvite] = useState(null); // { url, expiresAt } const [busy, setBusy] = useState(false); const [copied, setCopied] = useState(false); const [error, setError] = useState(''); const load = useCallback(async () => { if (!projectId || sharedBy !== undefined) return; const r = await getCollaborators(projectId); setState(r.ok ? { status: 'ready', ...r.data } : { status: 'unavailable', collaborators: [], pendingInvites: 0 }); }, [projectId, sharedBy]); useEffect(() => { setInvite(null); load(); }, [load]); // Someone else's study that you were invited to edit if (sharedBy !== undefined) { return (

Shared with you

{sharedBy || 'Its owner'} invited you to edit this study. Your changes sync to them, and theirs to you.

); } const makeInvite = async () => { setBusy(true); setError(''); const r = await createEditInvite(projectId); setBusy(false); if (!r.ok) { setError(r.error ?? 'Could not make the link.'); return; } setInvite({ url: `${window.location.origin}${window.location.pathname}?join=${r.data.token}`, expiresAt: r.data.expiresAt }); load(); }; const copy = async () => { try { await navigator.clipboard.writeText(invite.url); setCopied(true); window.setTimeout(() => setCopied(false), 1800); } catch { setError('Couldn’t copy. Select the link and copy it.'); } }; const remove = async (userId, email) => { if (!window.confirm(`Remove ${email}? They will no longer be able to edit this study.`)) return; const r = await removeCollaborator(projectId, userId); if (r.ok) load(); else setError(r.error ?? 'Could not remove them.'); }; return (

Co-teachers

Let someone else edit this study with you. They need an account here. Each invite link works once and lasts 7 days.

{state.status === 'unavailable' &&

Invites need your account and a connection, and the study has to have synced once. Try again in a moment.

} {state.status === 'ready' && ( <> {state.collaborators.length > 0 && ( )} {invite ? (
e.target.select()} className="min-w-0 flex-1 truncate rounded-lg border border-[var(--r-line)] bg-[var(--r-bg)] px-3 py-2 text-xs text-[var(--r-sub)]" />

Send it to one person. It stops working once they use it, or on {new Date(invite.expiresAt).toLocaleDateString()}.

) : ( )} {state.pendingInvites > 0 && !invite &&

{state.pendingInvites} invite{state.pendingInvites === 1 ? '' : 's'} waiting to be used.

} )} {error &&

{error}

}
); }