{"$schema":"https:\/\/ui.particle.academy\/schema\/registry-item.json","name":"employer-job-list","type":"registry:ui","title":"EmployerJobList","description":"An employer's own postings and their draft \/ published \/ closed status.","package":"job-board","dependencies":["@particle-academy\/react-fancy"],"registryDependencies":["format","types"],"files":[{"path":"components\/fancy\/employer-job-list\/EmployerJobList.tsx","content":"import { Badge, Button, Card, Heading, Text } from '@particle-academy\/react-fancy';\r\nimport { formatLocation, formatPay, postingStatusColor } from '..\/format';\r\nimport type { JobPosting } from '..\/types';\r\n\r\nexport interface EmployerJobListProps {\r\n    postings: JobPosting[];\r\n    onEdit?: (posting: JobPosting) => void;\r\n    onPublish?: (posting: JobPosting) => void;\r\n    onUnpublish?: (posting: JobPosting) => void;\r\n    onClose?: (posting: JobPosting) => void;\r\n    onViewApplicants?: (posting: JobPosting) => void;\r\n    onCreate?: () => void;\r\n    \/** Publishing is gated by the host; false renders the reason instead. *\/\r\n    canPublish?: boolean;\r\n    publishBlockedReason?: string;\r\n    \/**\r\n     * Label for the publish action, per posting. Hosts that charge per listing\r\n     * or meter it against a plan use this to say so up front \u2014 \"Publish \u2014 $49\"\r\n     * beats a bare \"Publish\" that silently opens a checkout.\r\n     *\/\r\n    publishLabel?: (posting: JobPosting) => string;\r\n    \/** Slug of the posting currently mid-action, to show a spinner on its row. *\/\r\n    busyId?: number | null;\r\n    className?: string;\r\n}\r\n\r\n\/**\r\n * The employer's own postings, in every status, with the actions that move\r\n * them between statuses.\r\n *\/\r\nexport function EmployerJobList({\r\n    postings,\r\n    onEdit,\r\n    onPublish,\r\n    onUnpublish,\r\n    onClose,\r\n    onViewApplicants,\r\n    onCreate,\r\n    canPublish = true,\r\n    publishBlockedReason,\r\n    publishLabel,\r\n    busyId = null,\r\n    className,\r\n}: EmployerJobListProps) {\r\n    if (postings.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                <Heading as=\"h3\" size=\"lg\" weight=\"bold\" className=\"!text-secondary-900\">\r\n                    No postings yet\r\n                <\/Heading>\r\n                <Text color=\"muted\" className=\"!mt-2\">\r\n                    Create your first job posting to start receiving applications.\r\n                <\/Text>\r\n                {onCreate && (\r\n                    <div className=\"mt-5 flex justify-center\">\r\n                        <Button\r\n                            type=\"button\"\r\n                            onClick={onCreate}\r\n                            className=\"!bg-brand hover:!bg-primary-600 !text-white !font-semibold !px-6 !py-2.5 !rounded-md !shadow-sm\"\r\n                        >\r\n                            New posting\r\n                        <\/Button>\r\n                    <\/div>\r\n                )}\r\n            <\/Card>\r\n        );\r\n    }\r\n\r\n    return (\r\n        <div className={`grid gap-3 ${className ?? ''}`}>\r\n            {postings.map((posting) => {\r\n                const busy = busyId === posting.id;\r\n                const pay = formatPay(posting);\r\n                const location = formatLocation(posting);\r\n\r\n                return (\r\n                    <Card\r\n                        key={posting.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                                <div className=\"flex items-center gap-2\">\r\n                                    <Badge\r\n                                        color={postingStatusColor(posting.status)}\r\n                                        variant=\"soft\"\r\n                                        size=\"sm\"\r\n                                    >\r\n                                        {posting.status_label}\r\n                                    <\/Badge>\r\n                                    {posting.employment_type_label && (\r\n                                        <Text size=\"xs\" color=\"muted\">\r\n                                            {posting.employment_type_label}\r\n                                        <\/Text>\r\n                                    )}\r\n                                <\/div>\r\n\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                                    {posting.title}\r\n                                <\/Heading>\r\n\r\n                                <Text size=\"sm\" color=\"muted\" className=\"!mt-1\">\r\n                                    {[location, pay].filter(Boolean).join(' \u00b7 ') || 'No location or pay set'}\r\n                                <\/Text>\r\n                            <\/div>\r\n\r\n                            <div className=\"text-right\">\r\n                                <Text size=\"lg\" weight=\"bold\" className=\"!text-secondary-900 !text-2xl\">\r\n                                    {posting.applications_count}\r\n                                <\/Text>\r\n                                <Text size=\"xs\" color=\"muted\">\r\n                                    {posting.applications_count === 1 ? 'applicant' : 'applicants'}\r\n                                <\/Text>\r\n                            <\/div>\r\n                        <\/div>\r\n\r\n                        <div className=\"mt-4 flex flex-wrap items-center gap-2 border-t border-secondary-200 pt-4\">\r\n                            {onViewApplicants && posting.applications_count > 0 && (\r\n                                <Button\r\n                                    type=\"button\"\r\n                                    onClick={() => onViewApplicants(posting)}\r\n                                    className=\"!bg-brand hover:!bg-primary-600 !text-white !font-semibold !px-4 !py-2 !rounded-md\"\r\n                                >\r\n                                    View applicants\r\n                                <\/Button>\r\n                            )}\r\n\r\n                            {onEdit && (\r\n                                <Button\r\n                                    type=\"button\"\r\n                                    variant=\"ghost\"\r\n                                    onClick={() => onEdit(posting)}\r\n                                    className=\"!text-secondary-700 hover:!text-brand\"\r\n                                >\r\n                                    Edit\r\n                                <\/Button>\r\n                            )}\r\n\r\n                            {posting.status !== 'published' && onPublish && (\r\n                                canPublish ? (\r\n                                    <Button\r\n                                        type=\"button\"\r\n                                        variant=\"ghost\"\r\n                                        loading={busy}\r\n                                        disabled={busy}\r\n                                        onClick={() => onPublish(posting)}\r\n                                        className=\"!text-secondary-700 hover:!text-brand\"\r\n                                    >\r\n                                        {publishLabel?.(posting) ?? 'Publish'}\r\n                                    <\/Button>\r\n                                ) : (\r\n                                    <Text size=\"xs\" color=\"muted\">\r\n                                        {publishBlockedReason ?? 'Publishing locked until approved'}\r\n                                    <\/Text>\r\n                                )\r\n                            )}\r\n\r\n                            {posting.status === 'published' && onUnpublish && (\r\n                                <Button\r\n                                    type=\"button\"\r\n                                    variant=\"ghost\"\r\n                                    loading={busy}\r\n                                    disabled={busy}\r\n                                    onClick={() => onUnpublish(posting)}\r\n                                    className=\"!text-secondary-700 hover:!text-brand\"\r\n                                >\r\n                                    Unpublish\r\n                                <\/Button>\r\n                            )}\r\n\r\n                            {posting.status !== 'closed' && onClose && (\r\n                                <Button\r\n                                    type=\"button\"\r\n                                    variant=\"ghost\"\r\n                                    loading={busy}\r\n                                    disabled={busy}\r\n                                    onClick={() => onClose(posting)}\r\n                                    className=\"!text-secondary-700 hover:!text-brand\"\r\n                                >\r\n                                    Close\r\n                                <\/Button>\r\n                            )}\r\n                        <\/div>\r\n                    <\/Card>\r\n                );\r\n            })}\r\n        <\/div>\r\n    );\r\n}\r\n","type":"registry:ui","target":"components\/fancy\/employer-job-list\/EmployerJobList.tsx"}]}