216 lines
9.5 KiB
TypeScript
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>
|
|
</>
|
|
);
|
|
}
|