Files
compras/resources/js/pages/candidate/opportunity.tsx
T
nala c6373365bf
tests / ci (push) Canceled after 0s
commit9
2026-10-30 20:02:30 +02:00

216 lines
9.5 KiB
TypeScript

import { Head, Link, useForm } from '@inertiajs/react';
import { FormErrors, controlClass } from '@/components/procurement';
import { Button } from '@/components/ui/button';
type Field = { key: string; label: string; type: string; required: boolean };
type Location = { province: string; district: string; locality: string };
const answerText = (value: string | Location) =>
typeof value === 'string' ? value : '';
export default function Opportunity({
companyId,
opportunity,
profile,
email,
emailVerified,
}: {
companyId: number;
opportunity: {
id: number;
title: string;
description: string;
closes_on: string;
publication_version: number;
form: {
version: number;
definition: {
fields: Field[];
documents: { key: string; label: string; required: boolean }[];
};
};
};
profile: {
version: number;
data: {
name: string;
phone?: string;
summary: string;
availability: Location;
};
};
email: string;
emailVerified: boolean;
}) {
const source: Record<string, string | Location> = {
name: profile.data.name,
full_name: profile.data.name,
email,
phone: profile.data.phone ?? '',
summary: profile.data.summary,
availability_location: profile.data.availability,
};
const form = useForm({
operation_key: crypto.randomUUID(),
profile_version: profile.version,
publication_version: opportunity.publication_version,
answers: Object.fromEntries(
opportunity.form.definition.fields.map((field) => [
field.key,
field.type === 'location'
? typeof source[field.key] === 'object'
? source[field.key]
: { province: '', district: '', locality: '' }
: typeof source[field.key] === 'string'
? source[field.key]
: '',
]),
) as Record<string, string | Location>,
});
return (
<>
<Head title={opportunity.title} />
<main className="mx-auto max-w-3xl space-y-5 p-6">
<Link
href={`/candidaturas/${companyId}/perfil`}
className="underline"
>
O meu perfil
</Link>
<h1 className="text-3xl font-semibold">{opportunity.title}</h1>
<p className="whitespace-pre-wrap">{opportunity.description}</p>
<p>
Fecho: {opportunity.closes_on} · formulário v
{opportunity.form.version} · perfil v{profile.version}
</p>
<p>
Dados existentes foram preenchidos a partir do perfil. As
respostas específicas não alteram o perfil. A recepção não
constitui aceitação, selecção ou contratação.
</p>
{!emailVerified && (
<p>Confirme o email na sua área antes de submeter.</p>
)}
{profile.version === 0 && <p>Guarde primeiro o seu perfil.</p>}
<form
className="space-y-4"
onSubmit={(e) => {
e.preventDefault();
form.post(
`/candidaturas/${companyId}/oportunidades/${opportunity.id}`,
);
}}
>
<FormErrors errors={form.errors} />
{opportunity.form.definition.fields.map((field) =>
field.type === 'location' ? (
<fieldset
key={field.key}
className="grid gap-3 sm:grid-cols-3"
>
<legend>
{field.label}
{field.required ? ' *' : ''}
</legend>
{(
[
'province',
'district',
'locality',
] as const
).map((part) => (
<label key={part}>
{
{
province: 'Província',
district: 'Distrito',
locality: 'Localidade',
}[part]
}
<input
className={controlClass}
required={field.required}
value={
(typeof form.data.answers[
field.key
] === 'object'
? (form.data.answers[
field.key
] as Location)
: {
province: '',
district: '',
locality: '',
})[part]
}
onChange={(e) =>
form.setData('answers', {
...form.data.answers,
[field.key]: {
...(form.data.answers[
field.key
] as Location),
[part]: e.target.value,
},
})
}
/>
</label>
))}
</fieldset>
) : (
<label className="grid gap-2" key={field.key}>
{field.label}
{field.required ? ' *' : ''}
{field.type === 'textarea' ? (
<textarea
className={controlClass}
required={field.required}
value={answerText(
form.data.answers[field.key],
)}
onChange={(e) =>
form.setData('answers', {
...form.data.answers,
[field.key]: e.target.value,
})
}
/>
) : (
<input
className={controlClass}
type={field.type}
required={field.required}
value={answerText(
form.data.answers[field.key],
)}
onChange={(e) =>
form.setData('answers', {
...form.data.answers,
[field.key]: e.target.value,
})
}
/>
)}
</label>
),
)}
{opportunity.form.definition.documents.length > 0 && (
<p>
O envio de anexos ainda está em desenvolvimento. Não
é possível entregar documentos neste formulário.
</p>
)}
<Button
disabled={
form.processing ||
!emailVerified ||
profile.version === 0
}
>
Submeter candidatura e obter comprovativo
</Button>
</form>
</main>
</>
);
}