'use client'; import { useEffect, useMemo, useRef, useState } from 'react'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter, DialogDescription, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Share2, Clock, Users, Check } from 'lucide-react'; import { toast } from 'sonner'; interface ShareModalProps { analysisId: string; } interface DirectoryEntry { email: string; displayName: string | null; jobTitle: string | null; department: string | null; } interface RecentEntry { email: string; lastSharedAt: string; } interface RecipientsResponse { recent: RecentEntry[]; directory: DirectoryEntry[]; allowedDomains: string[]; } const MAX_DIRECTORY_VISIBLE = 12; function timeAgo(iso: string): string { const ms = Date.now() - new Date(iso).getTime(); const minute = 60_000; const hour = 60 * minute; const day = 24 * hour; if (ms < hour) return `${Math.max(1, Math.round(ms / minute))}m ago`; if (ms < day) return `${Math.round(ms / hour)}h ago`; return `${Math.round(ms / day)}d ago`; } export function ShareModal({ analysisId }: ShareModalProps) { const [open, setOpen] = useState(false); const [recipientEmail, setRecipientEmail] = useState(''); const [note, setNote] = useState(''); const [submitting, setSubmitting] = useState(false); const [recipients, setRecipients] = useState(null); const [recipientsError, setRecipientsError] = useState(null); const [showSuggestions, setShowSuggestions] = useState(false); const inputRef = useRef(null); // Fetch recipients lazily on first dialog open. useEffect(() => { if (!open) return; if (recipients !== null) return; let cancelled = false; (async () => { try { const res = await fetch('/api/analyzer/share/recipients'); if (!res.ok) { const data = (await res.json().catch(() => ({}))) as { error?: string }; throw new Error(data.error ?? `Request failed: ${res.status}`); } const data = (await res.json()) as RecipientsResponse; if (!cancelled) setRecipients(data); } catch (err) { if (!cancelled) { setRecipientsError( err instanceof Error ? err.message : 'Unknown error' ); } } })(); return () => { cancelled = true; }; }, [open, recipients]); const filteredDirectory = useMemo(() => { if (!recipients) return []; const q = recipientEmail.trim().toLowerCase(); if (!q) return recipients.directory.slice(0, MAX_DIRECTORY_VISIBLE); return recipients.directory .filter( (d) => d.email.toLowerCase().includes(q) || (d.displayName ?? '').toLowerCase().includes(q) ) .slice(0, MAX_DIRECTORY_VISIBLE); }, [recipients, recipientEmail]); const hasRecent = (recipients?.recent.length ?? 0) > 0; function pick(email: string): void { setRecipientEmail(email); setShowSuggestions(false); inputRef.current?.blur(); } async function handleSubmit(e: React.FormEvent): Promise { e.preventDefault(); setSubmitting(true); try { const res = await fetch(`/api/analyzer/analyses/${analysisId}/share`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ recipientEmail, note: note.trim() || undefined, }), }); const data = (await res.json().catch(() => ({}))) as { error?: string; message?: string; emailSent?: boolean; emailError?: string | null; }; if (!res.ok) { throw new Error(data.message ?? data.error ?? `Request failed: ${res.status}`); } if (data.emailSent === false) { toast.warning( `Share recorded for ${recipientEmail}, but email failed: ${data.emailError ?? 'unknown error'}` ); } else { toast.success(`Shared with ${recipientEmail}`); } setOpen(false); setRecipientEmail(''); setNote(''); } catch (err) { const msg = err instanceof Error ? err.message : 'Unknown error'; toast.error(msg); } finally { setSubmitting(false); } } // Reset transient state when the dialog closes. function onOpenChange(next: boolean): void { setOpen(next); if (!next) { setShowSuggestions(false); } } return ( Share this analysis {recipients?.allowedDomains.length ? `Allowed domains: ${recipients.allowedDomains.join(', ')}` : 'Recipient must be on an allowed domain.'}
{ setRecipientEmail(e.target.value); setShowSuggestions(true); }} onFocus={() => setShowSuggestions(true)} onBlur={() => { // Delay so a click on a suggestion lands before we hide. setTimeout(() => setShowSuggestions(false), 150); }} placeholder="Search teammates or type an email…" /> {showSuggestions && recipients !== null && (
{hasRecent && recipientEmail.trim().length === 0 && (
Recent
)} {hasRecent && recipientEmail.trim().length === 0 && recipients.recent.slice(0, 3).map((r) => ( ))}
Directory
{filteredDirectory.length === 0 ? (
No matching directory users.
) : ( filteredDirectory.map((d) => ( )) )}
)}
{recipientsError && (

Couldn’t load directory ({recipientsError}). Type any allowed-domain email to share.

)}