import {Check, Copy, UsersRound, X} from 'lucide-react' import {type FormEvent, useEffect, useState} from 'react' import {Link, useParams} from 'react-router-dom' import {api} from '../../api/client' import type {ParticipantMeeting, ScheduledInterview} from '../../types' import {ErrorAlert, Loading} from '../../components/ui/AsyncState' import {DateAndPlace, InterviewCard} from './components/InterviewCard' import {message} from "../../components/actions"; export function InboxPage() { const [items, setItems] = useState([]) const [error, setError] = useState('') const [loading, setLoading] = useState(true) useEffect(() => { void api.inbox .list() .then(setItems) .catch((value) => setError(message(value))) .finally(() => setLoading(false)) }, []) async function respond(id: number, status: 'accepted' | 'declined') { setError('') try { const updated = await api.inbox.respond(id, status) setItems((current) => current.map((item) => (item.id === id ? updated : item))) } catch (value) { setError(message(value)) } } return ( <>
Invitations

Inbox

Respond to interview invitations assigned to you.

{error && } {loading ? ( ) : (
{items.map((item) => (
))} {!items.length && (

Inbox zero

You have no interview invitations.

)}
)} ) } export function ScheduledInterviewPage() { const id = Number(useParams().id) const [interview, setInterview] = useState() const [document, setDocument] = useState('') const [notes, setNotes] = useState([]) const [error, setError] = useState('') const [saving, setSaving] = useState(false) const [linkCopied, setLinkCopied] = useState(false) const loadedInterviewID = interview?.id useEffect(() => { let active = true void Promise.all([api.scheduledInterviews.get(id), api.scheduledInterviews.notes(id)]) .then(([item, loadedNotes]) => { if (!active) return setInterview(item) setDocument(item.sharedDocument) setNotes(loadedNotes) }) .catch((value) => active && setError(message(value))) return () => { active = false } }, [id]) useEffect(() => { if (!loadedInterviewID) return const controller = new AbortController() void api.scheduledInterviews .subscribe( id, (event) => { if (event.type === 'note.created') { setNotes((current) => current.some((item) => item.id === event.note.id) ? current : [...current, event.note], ) } if (event.type === 'document.updated') { setDocument(event.sharedDocument) setInterview((current) => current ? {...current, sharedDocument: event.sharedDocument} : current, ) } }, controller.signal, (value) => setError(message(value)), ) .catch((value) => { if (!controller.signal.aborted) setError(message(value)) }) return () => controller.abort() }, [id, loadedInterviewID]) async function setStatus(status: string) { setError('') try { setInterview(await api.scheduledInterviews.setStatus(id, status)) } catch (value) { setError(message(value)) } } async function saveDocument(event: FormEvent) { event.preventDefault() setSaving(true) setError('') try { setInterview(await api.scheduledInterviews.updateDocument(id, document)) } catch (value) { setError(message(value)) } finally { setSaving(false) } } async function addNote(event: FormEvent) { event.preventDefault() const form = event.currentTarget const body = String(new FormData(form).get('body')).trim() if (!body) return try { const note = await api.scheduledInterviews.addNote(id, body) setNotes((current) => current.some((item) => item.id === note.id) ? current : [...current, note], ) form.reset() } catch (value) { setError(message(value)) } } async function copyParticipantLink(url: string) { setError('') try { await navigator.clipboard.writeText(url) setLinkCopied(true) } catch { setError('Could not copy the participant interview link') } } if (!interview && !error) return if (!interview) return return ( <> ← Calendar
Interviewer session

{interview.participantName}

{interview.jobTitle}

{interview.status}
{error && }
{interview.status !== 'started' && interview.status !== 'completed' && ( )} {interview.status === 'started' && ( )}
Visible to candidate

Shared documentation