import { ArrowLeft, ArrowRight, BriefcaseBusiness, CalendarDays, Check, ChevronRight, CircleHelp, Clock3, Copy, FileText, LayoutDashboard, LogOut, Menu, Pencil, Plus, Search, Send, Sparkles, Trash2, Upload, UserRound, UsersRound, X, } from 'lucide-react' import { createContext, type FormEvent, type ReactNode, useContext, useEffect, useRef, useState, } from 'react' import { Link, NavLink, Navigate, Route, Routes, useLocation, useNavigate, useParams, } from 'react-router-dom' import { api, ApiError, authStorage, invalidateSession, SESSION_EXPIRED_EVENT } from '../api/client' import { CalendarPage, InboxPage, ParticipantMeetingPage, ScheduleInterviewForm, ScheduledInterviewPage, } from '../features/interviews' import { UsersPage } from '../features/users' import { ProfilePage } from '../features/users' import { AppFooter } from '../components/layout/AppFooter' import { WorkspaceUtilities } from '../components/layout/WorkspaceUtilities' import type { CandidateMatch, CreateJobInput, CreateParticipantInput, CreateQuestionInput, Interview, Invitation, Job, JobStatus, Participant, Question, QuestionType, ScheduledInterview, } from '../types' interface AppData { jobs: Job[] participants: Participant[] loading: boolean error: string createJob: (input: CreateJobInput) => Promise createParticipant: (input: CreateParticipantInput) => Promise reload: () => Promise } const DataContext = createContext(null) function useData() { const value = useContext(DataContext) if (!value) throw new Error('useData must be used within DataProvider') return value } function errorMessage(error: unknown) { return error instanceof ApiError || error instanceof Error ? error.message : 'Something went wrong' } function DataProvider({ children }: { children: ReactNode }) { const [jobs, setJobs] = useState([]) const [participants, setParticipants] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState('') async function reload() { setLoading(true) setError('') try { const [jobItems, participantItems] = await Promise.all([ api.jobs.list(), api.participants.list(), ]) setJobs(jobItems) setParticipants(participantItems) } catch (requestError) { setError(errorMessage(requestError)) } finally { setLoading(false) } } useEffect(() => { let active = true void Promise.all([api.jobs.list(), api.participants.list()]) .then(([jobItems, participantItems]) => { if (!active) return setJobs(jobItems) setParticipants(participantItems) }) .catch((requestError: unknown) => { if (active) setError(errorMessage(requestError)) }) .finally(() => { if (active) setLoading(false) }) return () => { active = false } }, []) async function createJob(input: CreateJobInput) { const created = await api.jobs.create(input) setJobs((current) => [created, ...current]) return created } async function createParticipant(input: CreateParticipantInput) { const created = await api.participants.create(input) setParticipants((current) => [created, ...current]) return created } return ( {children} ) } function Logo() { return ( GO GUESS Talent workspace ) } function LoginPage({ onLogin, sessionExpired }: { onLogin: () => void; sessionExpired: boolean }) { const [loading, setLoading] = useState(false) const [error, setError] = useState('') async function submit(event: FormEvent) { event.preventDefault() setLoading(true) setError('') const form = new FormData(event.currentTarget) try { const auth = await api.login(String(form.get('email')), String(form.get('password'))) authStorage.save(auth) onLogin() } catch (requestError) { setError(errorMessage(requestError)) } finally { setLoading(false) } } return (
Better hiring, clearly connected

Find the right person for every journey.

Bring roles, assessments, and candidate insight together in one workspace.

Skills-first matching Structured signals for confident decisions

Designed for fair, collaborative recruitment.

Welcome back

Sign in to your workspace

{sessionExpired && (
Your session is no longer active. Sign in again to continue.
)}

Use your work account to continue.

{error && }

Contact your administrator for access.

) } function AppLayout({ onLogout }: { onLogout: () => void }) { const [open, setOpen] = useState(false) const user = authStorage.user() return (
{open && (
} /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } />
) } function NavItem({ to, label, icon, close, }: { to: string label: string icon: ReactNode close: () => void }) { return ( (isActive ? 'nav-link active' : 'nav-link')} > {icon} {label} ) } function PageHeader({ eyebrow, title, description, action, }: { eyebrow: string title: string description?: string action?: ReactNode }) { return (
{eyebrow}

{title}

{description &&

{description}

}
{action}
) } function JobsPage() { const { jobs, loading, error, reload } = useData() const [query, setQuery] = useState('') const filtered = jobs.filter((job) => `${job.title} ${job.position} ${job.seniority} ${job.labels.join(' ')}` .toLowerCase() .includes(query.toLowerCase()), ) return ( <> Create job } /> {error && }
sum + job.questions.length, 0))} />
{filtered.length} jobs
{loading ? ( ) : (
{filtered.map((job) => ( ))}
Role Seniority Position Status Questions View
{job.title} {job.labels.join(' · ') || 'No labels'} {job.position} {job.questions.length}
)} {!loading && !filtered.length && !error && ( )}
) } function JobDetailPage() { const id = Number(useParams().jobId) const [job, setJob] = useState() const [matches, setMatches] = useState([]) const [invitations, setInvitations] = useState([]) const [scheduledInterviews, setScheduledInterviews] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState('') const [tab, setTab] = useState<'overview' | 'questions' | 'candidates' | 'invitations'>( 'overview', ) async function load() { setLoading(true) setError('') try { const [jobValue, candidates, invitationItems, scheduledItems] = await Promise.all([ api.jobs.get(id), api.jobs.candidates(id), api.jobs.invitations(id), api.jobs.interviews(id), ]) setJob(jobValue) setMatches(candidates) setInvitations(invitationItems) setScheduledInterviews(scheduledItems) } catch (requestError) { setError(errorMessage(requestError)) } finally { setLoading(false) } } useEffect(() => { let active = true void Promise.all([ api.jobs.get(id), api.jobs.candidates(id), api.jobs.invitations(id), api.jobs.interviews(id), ]) .then(([jobValue, candidates, invitationItems, scheduledItems]) => { if (!active) return setJob(jobValue) setMatches(candidates) setInvitations(invitationItems) setScheduledInterviews(scheduledItems) }) .catch((requestError: unknown) => { if (active) setError(errorMessage(requestError)) }) .finally(() => { if (active) setLoading(false) }) return () => { active = false } }, [id]) if (loading) return if (error) return if (!job) return return ( <> All jobs
{(['overview', 'questions', 'candidates', 'invitations'] as const).map((item) => ( ))}
{tab === 'overview' && (

About the role

{job.description}

Required skills

Additional skills

)} {tab === 'questions' && } {tab === 'candidates' && } {tab === 'invitations' && ( setInvitations((current) => [invitation, ...current])} onInvitationUpdated={(invitation) => setInvitations((current) => current.map((item) => (item.id === invitation.id ? invitation : item)), ) } onScheduled={(interview) => setScheduledInterviews((current) => [interview, ...current])} /> )} ) } function JobSettings({ job, onUpdated }: { job: Job; onUpdated: (job: Job) => void }) { const [status, setStatus] = useState(job.status) const [durationMinutes, setDurationMinutes] = useState(job.durationMinutes) const [saving, setSaving] = useState(false) const [error, setError] = useState('') async function save(event: FormEvent) { event.preventDefault() setSaving(true) setError('') try { onUpdated(await api.jobs.update(job.id, { status, durationMinutes })) } catch (requestError) { setError(errorMessage(requestError)) } finally { setSaving(false) } } return (
setDurationMinutes(Number(event.target.value))} /> {error && } ) } function JobQuestionsView({ job, onUpdated }: { job: Job; onUpdated: (job: Job) => void }) { const [query, setQuery] = useState('') const [results, setResults] = useState([]) const [error, setError] = useState('') const attached = new Set(job.questions.map((question) => question.id)) useEffect(() => { let active = true const timeout = window.setTimeout(() => { void api.questions .list(query) .then((questions) => { if (active) setResults(questions) }) .catch((requestError) => { if (active) setError(errorMessage(requestError)) }) }, 200) return () => { active = false window.clearTimeout(timeout) } }, [query]) async function attach(question: Question) { setError('') try { await api.jobs.attachQuestion(job.id, question.id) onUpdated({ ...job, questions: [...job.questions, question] }) } catch (requestError) { setError(errorMessage(requestError)) } } async function detach(question: Question) { setError('') try { await api.jobs.detachQuestion(job.id, question.id) onUpdated({ ...job, questions: job.questions.filter((item) => item.id !== question.id), }) } catch (requestError) { setError(errorMessage(requestError)) } } return (

Assessment questions

Detach removes only this job’s link to the library question.

{job.questions.map((question, index) => ( void detach(question)} aria-label={`Detach ${question.text}`} > Detach } /> ))} {!job.questions.length && ( )}

Question library

Search reusable, active questions.

Manage library
{error && }
{results .filter((question) => !question.deprecated && !attached.has(question.id)) .map((question) => ( void attach(question)} > Attach } /> ))}
) } function InvitationsView({ job, matches, invitations, scheduledInterviews, onInvited, onInvitationUpdated, onScheduled, }: { job: Job matches: CandidateMatch[] invitations: Invitation[] scheduledInterviews: ScheduledInterview[] onInvited: (invitation: Invitation) => void onInvitationUpdated: (invitation: Invitation) => void onScheduled: (interview: ScheduledInterview) => void }) { const [sendingId, setSendingId] = useState() const [review, setReview] = useState() const [reviewingId, setReviewingId] = useState() const [error, setError] = useState('') const [scheduling, setScheduling] = useState() const invitedIds = new Set(invitations.map((invitation) => invitation.participantId)) const eligible = matches.filter((match) => !invitedIds.has(match.participant.id)) async function send(participantId: number) { setSendingId(participantId) setError('') try { onInvited(await api.jobs.invite(job.id, participantId)) } catch (requestError) { setError(errorMessage(requestError)) } finally { setSendingId(undefined) } } async function copy(url: string) { try { await navigator.clipboard.writeText(url) } catch { setError('Could not copy the participant URL') } } async function reviewAnswers(invitationId: number) { setReviewingId(invitationId) setError('') try { setReview(await api.jobs.invitation(job.id, invitationId)) } catch (requestError) { setError(errorMessage(requestError)) } finally { setReviewingId(undefined) } } return (

Interview invitations

Invite an eligible candidate and share their private participant URL.

{eligible.map(({ participant }) => (
{participant.firstName} {participant.lastName} {participant.email}
))} {!eligible.length &&

All eligible candidates have been invited.

}
{error && }
{invitations.map((invitation) => (
{invitation.participantName} {invitation.participantEmail}
{invitation.participantUrl} {['accepted', 'completed'].includes(invitation.status) && ( )}
))} {!invitations.length && ( )}
{review && ( setReview(undefined)} onOutcome={(invitation) => { onInvitationUpdated(invitation) setReview((current) => current && { ...current, invitation }) }} /> )} {scheduling && ( setScheduling(undefined)} onCreated={(interview) => { onScheduled(interview) setScheduling(undefined) }} /> )}
{invitations .filter((invitation) => invitation.outcome === 'passed') .filter( (invitation) => !scheduledInterviews.some( (interview) => interview.participantId === invitation.participantId, ), ) .map((invitation) => ( ))} {scheduledInterviews.map((interview) => ( {interview.participantName} {new Date(interview.startsAt).toLocaleString()} · {interview.location} ))}
) } function InvitationAnswerReview({ interview, onClose, onOutcome, }: { interview: Interview onClose: () => void onOutcome: (invitation: Invitation) => void }) { const [saving, setSaving] = useState(false) const [error, setError] = useState('') async function decide(outcome: 'passed' | 'failed') { setSaving(true) setError('') try { onOutcome( await api.jobs.setInvitationOutcome( interview.invitation.jobId, interview.invitation.id, outcome, ), ) } catch (requestError) { setError(errorMessage(requestError)) } finally { setSaving(false) } } return (
Participant submission

{interview.invitation.participantName}

{interview.invitation.participantEmail}

{interview.invitation.status === 'completed' && (
Assessment decision

Record whether this candidate should proceed to a scheduled interview.

{interview.invitation.outcome && }
)} {error && }
{interview.job.questions.map((question, index) => { const answer = interview.answers[String(question.id)]?.trim() return (
{index + 1}
{question.type.replace('_', ' ')}

{question.text}

{question.type === 'code_review' && question.codeSnippet && ( )} {(question.type === 'multiple_choice' || question.type === 'radio') && question.options.length > 0 && (
Possible options {question.options.join(' · ')}
)} {(question.type === 'open' || question.type === 'code_review') && question.referenceAnswer && (
Reference answer

{question.referenceAnswer}

)}
Participant answer

{answer || 'Unanswered'}

) })}
) } function QuestionRow({ question, number, action, }: { question: Question number?: number action?: ReactNode }) { return (
{number ? {number} : }

{question.type.replace('_', ' ')}

{question.deprecated && }

{question.text}

{question.options.length > 0 && {question.options.join(' · ')}} {question.type === 'code_review' && question.codeSnippet && (
            {question.codeSnippet}
          
)} {question.referenceAnswer && (
Reference answer

{question.referenceAnswer}

)}
{action &&
{action}
}
) } function CandidateMatchView({ matches }: { matches: CandidateMatch[] }) { return (

Candidate match

Trait alignment calculated by the API.

Match insight
{matches.map((match, index) => (
{index + 1}
{match.participant.firstName} {match.participant.lastName} {match.participant.email}
Matched {match.matchedTraits.length}
Missing {match.missingTraits.length}
{Math.round(match.score)}% Overall match
))} {!matches.length && ( )}
) } function CreateJobPage() { const { createJob } = useData() const navigate = useNavigate() const [saving, setSaving] = useState(false) const [error, setError] = useState('') async function submit(event: FormEvent) { event.preventDefault() setSaving(true) setError('') const form = new FormData(event.currentTarget) const split = (name: string) => String(form.get(name)) .split(',') .map((item) => item.trim()) .filter(Boolean) try { const job = await createJob({ title: String(form.get('title')), description: String(form.get('description')), seniority: String(form.get('seniority')), position: String(form.get('position')), labels: split('labels'), requiredSkills: split('requiredSkills'), additionalSkills: split('additionalSkills'), }) navigate(`/jobs/${job.id}`) } catch (requestError) { setError(errorMessage(requestError)) setSaving(false) } } return ( <> Cancel

Role information

Role profile