{"$schema":"https:\/\/ui.particle.academy\/schema\/registry-item.json","name":"application-list","type":"registry:ui","title":"ApplicationList","description":"Applications against a posting, through the six-state status flow.","package":"job-board","dependencies":["@particle-academy\/react-fancy"],"registryDependencies":["format","types"],"files":[{"path":"components\/fancy\/application-list\/ApplicationList.tsx","content":"import { Badge, Button, Card, Heading, Select, Text } from '@particle-academy\/react-fancy';\r\nimport { APPLICATION_STATUS_OPTIONS, applicationStatusColor, formatPosted } from '..\/format';\r\nimport type { ApplicationStatus, JobApplication } from '..\/types';\r\n\r\nexport interface ApplicationListProps {\r\n    applications: JobApplication[];\r\n    \/** Employer moving a candidate along. Omit for a read-only list. *\/\r\n    onStatusChange?: (application: JobApplication, status: ApplicationStatus) => void;\r\n    \/** Candidate withdrawing their own application. *\/\r\n    onWithdraw?: (application: JobApplication) => void;\r\n    \/** Show which posting each application is for. Off within one posting. *\/\r\n    showPosting?: boolean;\r\n    \/** Show candidate identity. Off on the candidate's own list. *\/\r\n    showCandidate?: boolean;\r\n    busyId?: number | null;\r\n    emptyMessage?: string;\r\n    className?: string;\r\n}\r\n\r\n\/**\r\n * A list of applications, used from both sides: the employer reviewing\r\n * candidates, and the candidate tracking their own submissions.\r\n *\/\r\nexport function ApplicationList({\r\n    applications,\r\n    onStatusChange,\r\n    onWithdraw,\r\n    showPosting = true,\r\n    showCandidate = true,\r\n    busyId = null,\r\n    emptyMessage = 'No applications yet.',\r\n    className,\r\n}: ApplicationListProps) {\r\n    if (applications.length === 0) {\r\n        return (\r\n            <Card\r\n                variant=\"outlined\"\r\n                padding=\"lg\"\r\n                className={`!rounded-xl !border-secondary-200 !bg-white text-center ${className ?? ''}`}\r\n            >\r\n                <Text color=\"muted\">{emptyMessage}<\/Text>\r\n            <\/Card>\r\n        );\r\n    }\r\n\r\n    return (\r\n        <div className={`grid gap-3 ${className ?? ''}`}>\r\n            {applications.map((application) => {\r\n                const busy = busyId === application.id;\r\n                const submitted = formatPosted(application.submitted_at)?.replace('Posted', 'Applied');\r\n\r\n                return (\r\n                    <Card\r\n                        key={application.id}\r\n                        variant=\"outlined\"\r\n                        padding=\"lg\"\r\n                        className=\"!rounded-xl !border-secondary-200 !bg-white !shadow-sm\"\r\n                    >\r\n                        <div className=\"flex flex-wrap items-start justify-between gap-4\">\r\n                            <div className=\"min-w-0\">\r\n                                <Badge\r\n                                    color={applicationStatusColor(application.status)}\r\n                                    variant=\"soft\"\r\n                                    size=\"sm\"\r\n                                >\r\n                                    {application.status_label}\r\n                                <\/Badge>\r\n\r\n                                {showCandidate && application.candidate && (\r\n                                    <Heading\r\n                                        as=\"h3\"\r\n                                        size=\"lg\"\r\n                                        weight=\"bold\"\r\n                                        className=\"!mt-2 !text-secondary-900\"\r\n                                    >\r\n                                        {application.candidate.name ?? 'Candidate'}\r\n                                    <\/Heading>\r\n                                )}\r\n\r\n                                {showPosting && application.job_posting && (\r\n                                    <Text\r\n                                        size={showCandidate ? 'sm' : 'lg'}\r\n                                        weight={showCandidate ? undefined : 'bold'}\r\n                                        className=\"!mt-1 !text-secondary-900\"\r\n                                    >\r\n                                        {application.job_posting.title}\r\n                                    <\/Text>\r\n                                )}\r\n\r\n                                <Text size=\"sm\" color=\"muted\" className=\"!mt-1\">\r\n                                    {[\r\n                                        showCandidate ? application.candidate?.email : null,\r\n                                        application.contact_phone,\r\n                                        submitted,\r\n                                    ]\r\n                                        .filter(Boolean)\r\n                                        .join(' \u00b7 ')}\r\n                                <\/Text>\r\n                            <\/div>\r\n\r\n                            <div className=\"flex items-center gap-2\">\r\n                                {onStatusChange && (\r\n                                    <Select\r\n                                        list={[...APPLICATION_STATUS_OPTIONS]}\r\n                                        value={application.status}\r\n                                        disabled={busy}\r\n                                        onValueChange={(status) =>\r\n                                            onStatusChange(application, status as ApplicationStatus)\r\n                                        }\r\n                                        className=\"!min-w-40\"\r\n                                    \/>\r\n                                )}\r\n\r\n                                {onWithdraw && !application.is_terminal && (\r\n                                    <Button\r\n                                        type=\"button\"\r\n                                        variant=\"ghost\"\r\n                                        loading={busy}\r\n                                        disabled={busy}\r\n                                        onClick={() => onWithdraw(application)}\r\n                                        className=\"!text-secondary-700 hover:!text-brand\"\r\n                                    >\r\n                                        Withdraw\r\n                                    <\/Button>\r\n                                )}\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        {application.cover_letter && (\r\n                            <Text\r\n                                size=\"sm\"\r\n                                className=\"!mt-4 !text-secondary-700 whitespace-pre-line border-t border-secondary-200 pt-4\"\r\n                            >\r\n                                {application.cover_letter}\r\n                            <\/Text>\r\n                        )}\r\n\r\n                        {application.employer_notes && (\r\n                            <Text size=\"xs\" color=\"muted\" className=\"!mt-3\">\r\n                                Notes: {application.employer_notes}\r\n                            <\/Text>\r\n                        )}\r\n                    <\/Card>\r\n                );\r\n            })}\r\n        <\/div>\r\n    );\r\n}\r\n","type":"registry:ui","target":"components\/fancy\/application-list\/ApplicationList.tsx"}]}