'use client'; import { useEffect, useMemo, useState } from 'react'; import { Check, Copy, Search, Send } from 'lucide-react'; import { useAuth } from '@/lib/hooks/useAuth'; import { organizationApi, type CounterpartItemDto, type CounterpartSearchResultDto, } from '@/lib/api/organization'; import { Button } from '@/components/ui/common/Button'; import { Input } from '@/components/ui/common/Input'; import type { ApiError } from '@/types/api'; type StoredInviteLink = { invitationId: string; ownerEmail: string; invitationUrl: string; }; function inviteLinksStorageKey(orgId: string): string { return `counterpartInviteLinks:${orgId}`; } function readStoredInviteLinks(orgId: string): Record { if (typeof window === 'undefined') return {}; try { const raw = window.localStorage.getItem(inviteLinksStorageKey(orgId)); if (!raw) return {}; const parsed = JSON.parse(raw) as Record; return parsed && typeof parsed === 'object' ? parsed : {}; } catch { return {}; } } function writeStoredInviteLinks(orgId: string, links: Record) { if (typeof window === 'undefined') return; window.localStorage.setItem(inviteLinksStorageKey(orgId), JSON.stringify(links)); } function formatApiMessage(err: unknown): string { if (!err || typeof err !== 'object') return 'Something went wrong'; const m = (err as ApiError).message; if (Array.isArray(m)) return m.join(', '); if (typeof m === 'string') return m; return 'Something went wrong'; } export default function OrganizationsPage() { const { currentOrganization } = useAuth(); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const [searchTerm, setSearchTerm] = useState(''); const [searchLoading, setSearchLoading] = useState(false); const [results, setResults] = useState([]); const [selectedOrgId, setSelectedOrgId] = useState(null); const [linkLoading, setLinkLoading] = useState(false); const [items, setItems] = useState([]); const [manualOrganizationName, setManualOrganizationName] = useState(''); const [manualOwnerEmail, setManualOwnerEmail] = useState(''); const [manualPhone, setManualPhone] = useState(''); const [inviteLoading, setInviteLoading] = useState(false); const [copiedId, setCopiedId] = useState(null); const [pendingInviteLinks, setPendingInviteLinks] = useState>({}); const counterpartLabel = currentOrganization?.type === 'LAB' ? 'Clinic' : 'Lab'; const tabLabel = currentOrganization?.type === 'LAB' ? 'Clinics' : 'Labs'; const selectedResult = useMemo( () => results.find((r) => r.id === selectedOrgId) ?? null, [results, selectedOrgId], ); async function loadList() { setLoading(true); setError(''); try { const res = await organizationApi.list(); setItems(res.data.items); } catch (e) { setError(formatApiMessage(e)); } finally { setLoading(false); } } useEffect(() => { if (!currentOrganization?.id) return; setPendingInviteLinks(readStoredInviteLinks(currentOrganization.id)); }, [currentOrganization?.id]); useEffect(() => { void loadList(); }, []); useEffect(() => { if (!success) return; const t = setTimeout(() => setSuccess(''), 4000); return () => clearTimeout(t); }, [success]); async function runSearch() { setSearchLoading(true); setError(''); setSelectedOrgId(null); try { const res = await organizationApi.search(searchTerm.trim()); setResults(res.data); } catch (e) { setError(formatApiMessage(e)); } finally { setSearchLoading(false); } } async function createLink() { if (!selectedOrgId) return; setLinkLoading(true); setError(''); try { await organizationApi.createLink(selectedOrgId); setSuccess(`${counterpartLabel} link request created`); setResults([]); setSelectedOrgId(null); setSearchTerm(''); await loadList(); } catch (e) { setError(formatApiMessage(e)); } finally { setLinkLoading(false); } } async function sendInvite() { setInviteLoading(true); setError(''); try { const res = await organizationApi.invite({ organizationName: manualOrganizationName.trim(), ownerEmail: manualOwnerEmail.trim(), phone: manualPhone.trim() || undefined, }); if (currentOrganization?.id) { const nextLinks = { ...pendingInviteLinks, [res.data.invitationId]: { invitationId: res.data.invitationId, ownerEmail: manualOwnerEmail.trim().toLowerCase(), invitationUrl: res.data.invitationUrl, }, }; setPendingInviteLinks(nextLinks); writeStoredInviteLinks(currentOrganization.id, nextLinks); } setSuccess(`Invitation link created for ${manualOwnerEmail.trim()}`); setManualOrganizationName(''); setManualOwnerEmail(''); setManualPhone(''); await loadList(); } catch (e) { setError(formatApiMessage(e)); } finally { setInviteLoading(false); } } if (!currentOrganization) { return

Loading organization...

; } return (

{tabLabel}

Search subscribed {tabLabel.toLowerCase()}, request link access, or invite a new{' '} {counterpartLabel.toLowerCase()} owner.

{error && (
{error}
)} {success && (
{success}
)}

Search existing {tabLabel}

setSearchTerm(e.target.value)} placeholder={`Search by ${counterpartLabel.toLowerCase()} name, email, or phone`} />
{results.length > 0 && (
{results.map((r) => ( ))}
)}

Invite owner (not yet subscribed)

setManualOrganizationName(e.target.value)} /> setManualOwnerEmail(e.target.value)} /> setManualPhone(e.target.value)} />

Requests and invitations

{loading ? (

Loading list...

) : items.length === 0 ? (

No records yet.

) : (
{items.map((item) => ( ))}
Type Organization Owner email Status Action
{item.kind === 'LINK' ? 'Link request' : 'Invitation'} {item.organizationName} {item.ownerEmail} {item.status} {item.kind === 'INVITATION' && item.status === 'PENDING' && pendingInviteLinks[item.id]?.invitationUrl && ( )}
)}
); }