{"$schema":"https:\/\/ui.particle.academy\/schema\/registry-item.json","name":"job-posting-form","type":"registry:ui","title":"JobPostingForm","description":"Create or edit a posting, employer side.","package":"job-board","dependencies":["@particle-academy\/react-fancy","\r\n                        value={values.pay_min == null ? "],"registryDependencies":["format","types"],"files":[{"path":"components\/fancy\/job-posting-form\/JobPostingForm.tsx","content":"import {\r\n    Button,\r\n    Callout,\r\n    Card,\r\n    Heading,\r\n    Input,\r\n    Select,\r\n    Switch,\r\n    Text,\r\n    Textarea,\r\n} from '@particle-academy\/react-fancy';\r\nimport { useState } from 'react';\r\nimport { EMPLOYMENT_TYPE_OPTIONS, PAY_UNIT_OPTIONS } from '..\/format';\r\nimport type { JobPosting, JobPostingInput } from '..\/types';\r\n\r\nexport interface JobPostingFormProps {\r\n    \/** Omit to create; pass a posting to edit it. *\/\r\n    posting?: JobPosting;\r\n    onSubmit: (input: JobPostingInput) => void | Promise<void>;\r\n    onCancel?: () => void;\r\n    submitting?: boolean;\r\n    errors?: Partial<Record<keyof JobPostingInput, string>>;\r\n    \/**\r\n     * When false, the form explains that publishing is unavailable \u2014 the\r\n     * employer can still save drafts.\r\n     *\/\r\n    canPublish?: boolean;\r\n    \/** Message explaining why publishing is unavailable. *\/\r\n    publishBlockedReason?: string;\r\n    className?: string;\r\n}\r\n\r\nconst emptyPosting: JobPostingInput = {\r\n    title: '',\r\n    description: '',\r\n    requirements: '',\r\n    employment_type: null,\r\n    location: '',\r\n    is_remote: false,\r\n    pay_min: null,\r\n    pay_max: null,\r\n    pay_unit: 'hour',\r\n    contact_email: '',\r\n    contact_phone: '',\r\n    apply_url: '',\r\n    openings: 1,\r\n};\r\n\r\n\/** Create or edit a posting. Status is never set here \u2014 publishing is its own action. *\/\r\nexport function JobPostingForm({\r\n    posting,\r\n    onSubmit,\r\n    onCancel,\r\n    submitting = false,\r\n    errors = {},\r\n    canPublish = true,\r\n    publishBlockedReason,\r\n    className,\r\n}: JobPostingFormProps) {\r\n    const [values, setValues] = useState<JobPostingInput>(() =>\r\n        posting\r\n            ? {\r\n                  title: posting.title,\r\n                  description: posting.description ?? '',\r\n                  requirements: posting.requirements ?? '',\r\n                  employment_type: posting.employment_type ?? null,\r\n                  location: posting.location ?? '',\r\n                  is_remote: posting.is_remote,\r\n                  pay_min: posting.pay_min ?? null,\r\n                  pay_max: posting.pay_max ?? null,\r\n                  pay_unit: posting.pay_unit ?? 'hour',\r\n                  contact_email: posting.contact_email ?? '',\r\n                  contact_phone: posting.contact_phone ?? '',\r\n                  apply_url: posting.apply_url ?? '',\r\n                  openings: posting.openings,\r\n              }\r\n            : { ...emptyPosting },\r\n    );\r\n\r\n    const patch = (next: Partial<JobPostingInput>) =>\r\n        setValues((current) => ({ ...current, ...next }));\r\n\r\n    \/\/ Empty string is not a number; keep it null so the API sees \"unset\".\r\n    const num = (v: string): number | null => (v.trim() === '' ? null : Number(v));\r\n\r\n    return (\r\n        <Card\r\n            variant=\"outlined\"\r\n            padding=\"lg\"\r\n            className={`!rounded-xl !border-secondary-200 !bg-white !shadow-sm ${className ?? ''}`}\r\n        >\r\n            <Heading as=\"h2\" size=\"lg\" weight=\"bold\" className=\"!text-secondary-900\">\r\n                {posting ? 'Edit posting' : 'New posting'}\r\n            <\/Heading>\r\n\r\n            {!canPublish && (\r\n                <Callout color=\"amber\" className=\"!mt-4\">\r\n                    <Text size=\"sm\">\r\n                        {publishBlockedReason ??\r\n                            'You can save drafts now. Publishing unlocks once your account is approved.'}\r\n                    <\/Text>\r\n                <\/Callout>\r\n            )}\r\n\r\n            <form\r\n                className=\"mt-5 grid gap-4\"\r\n                onSubmit={(e) => {\r\n                    e.preventDefault();\r\n                    void onSubmit(values);\r\n                }}\r\n            >\r\n                <Input\r\n                    label=\"Job title\"\r\n                    required\r\n                    placeholder=\"Overnight patrol guard\"\r\n                    value={values.title}\r\n                    onValueChange={(title) => patch({ title })}\r\n                    error={errors.title}\r\n                \/>\r\n\r\n                <Textarea\r\n                    label=\"Description\"\r\n                    description=\"What the role involves day to day.\"\r\n                    minRows={5}\r\n                    autoResize\r\n                    value={values.description ?? ''}\r\n                    onValueChange={(description) => patch({ description })}\r\n                    error={errors.description}\r\n                \/>\r\n\r\n                <Textarea\r\n                    label=\"Requirements\"\r\n                    description=\"Licences, experience, availability.\"\r\n                    minRows={3}\r\n                    autoResize\r\n                    value={values.requirements ?? ''}\r\n                    onValueChange={(requirements) => patch({ requirements })}\r\n                    error={errors.requirements}\r\n                \/>\r\n\r\n                <div className=\"grid gap-4 sm:grid-cols-2\">\r\n                    <Select\r\n                        label=\"Employment type\"\r\n                        placeholder=\"Choose a type\"\r\n                        list={[...EMPLOYMENT_TYPE_OPTIONS]}\r\n                        value={values.employment_type ?? ''}\r\n                        onValueChange={(v) =>\r\n                            patch({ employment_type: (v || null) as JobPostingInput['employment_type'] })\r\n                        }\r\n                        error={errors.employment_type}\r\n                    \/>\r\n                    <Input\r\n                        type=\"number\"\r\n                        label=\"Openings\"\r\n                        value={String(values.openings ?? 1)}\r\n                        onValueChange={(v) => patch({ openings: num(v) ?? 1 })}\r\n                        error={errors.openings}\r\n                    \/>\r\n                <\/div>\r\n\r\n                <div className=\"grid gap-4 sm:grid-cols-2 sm:items-end\">\r\n                    <Input\r\n                        label=\"Location\"\r\n                        placeholder=\"Torrance, CA\"\r\n                        value={values.location ?? ''}\r\n                        onValueChange={(location) => patch({ location })}\r\n                        error={errors.location}\r\n                    \/>\r\n                    <div className=\"sm:pb-2\">\r\n                        <Switch\r\n                            label=\"This role is remote\"\r\n                            checked={Boolean(values.is_remote)}\r\n                            onCheckedChange={(is_remote) => patch({ is_remote })}\r\n                        \/>\r\n                    <\/div>\r\n                <\/div>\r\n\r\n                <div className=\"grid gap-4 sm:grid-cols-3\">\r\n                    <Input\r\n                        type=\"number\"\r\n                        label=\"Pay from\"\r\n                        value={values.pay_min == null ? '' : String(values.pay_min)}\r\n                        onValueChange={(v) => patch({ pay_min: num(v) })}\r\n                        error={errors.pay_min}\r\n                    \/>\r\n                    <Input\r\n                        type=\"number\"\r\n                        label=\"Pay to\"\r\n                        value={values.pay_max == null ? '' : String(values.pay_max)}\r\n                        onValueChange={(v) => patch({ pay_max: num(v) })}\r\n                        error={errors.pay_max}\r\n                    \/>\r\n                    <Select\r\n                        label=\"Per\"\r\n                        list={[...PAY_UNIT_OPTIONS]}\r\n                        value={values.pay_unit ?? 'hour'}\r\n                        onValueChange={(v) => patch({ pay_unit: v as JobPostingInput['pay_unit'] })}\r\n                        error={errors.pay_unit}\r\n                    \/>\r\n                <\/div>\r\n\r\n                <div className=\"grid gap-4 sm:grid-cols-2\">\r\n                    <Input\r\n                        type=\"email\"\r\n                        label=\"Contact email\"\r\n                        value={values.contact_email ?? ''}\r\n                        onValueChange={(contact_email) => patch({ contact_email })}\r\n                        error={errors.contact_email}\r\n                    \/>\r\n                    <Input\r\n                        type=\"tel\"\r\n                        label=\"Contact phone\"\r\n                        value={values.contact_phone ?? ''}\r\n                        onValueChange={(contact_phone) => patch({ contact_phone })}\r\n                        error={errors.contact_phone}\r\n                    \/>\r\n                <\/div>\r\n\r\n                <Input\r\n                    type=\"url\"\r\n                    label=\"External application link\"\r\n                    description=\"Optional. Candidates can apply here instead.\"\r\n                    value={values.apply_url ?? ''}\r\n                    onValueChange={(apply_url) => patch({ apply_url })}\r\n                    error={errors.apply_url}\r\n                \/>\r\n\r\n                <div className=\"flex items-center justify-end gap-3 pt-1\">\r\n                    {onCancel && (\r\n                        <Button\r\n                            type=\"button\"\r\n                            variant=\"ghost\"\r\n                            onClick={onCancel}\r\n                            className=\"!text-secondary-700 hover:!text-brand !px-4 !py-2\"\r\n                        >\r\n                            Cancel\r\n                        <\/Button>\r\n                    )}\r\n                    <Button\r\n                        type=\"submit\"\r\n                        loading={submitting}\r\n                        disabled={submitting}\r\n                        className=\"!bg-brand hover:!bg-primary-600 disabled:!bg-secondary-300 !text-white !font-semibold !px-6 !py-2.5 !rounded-md !shadow-sm\"\r\n                    >\r\n                        {posting ? 'Save changes' : 'Create draft'}\r\n                    <\/Button>\r\n                <\/div>\r\n            <\/form>\r\n        <\/Card>\r\n    );\r\n}\r\n","type":"registry:ui","target":"components\/fancy\/job-posting-form\/JobPostingForm.tsx"}]}