283 lines
12 KiB
TypeScript
283 lines
12 KiB
TypeScript
import { Head, Link, useForm } from '@inertiajs/react';
|
|
import { FormErrors, controlClass } from '@/components/procurement';
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
export default function Entry({
|
|
company,
|
|
portal,
|
|
opportunities = { data: [], prev_page_url: null, next_page_url: null },
|
|
}: {
|
|
company: { id: number; name: string };
|
|
portal: { version: number; registration_open: boolean; notice: string };
|
|
opportunities?: {
|
|
data: {
|
|
id: number;
|
|
title: string;
|
|
kind: string;
|
|
closes_on: string;
|
|
description: string;
|
|
}[];
|
|
prev_page_url: string | null;
|
|
next_page_url: string | null;
|
|
};
|
|
}) {
|
|
const login = useForm({
|
|
email: '',
|
|
password: '',
|
|
opportunity_id: null as number | null,
|
|
});
|
|
const register = useForm({
|
|
email: '',
|
|
password: '',
|
|
password_confirmation: '',
|
|
portal_version: portal.version,
|
|
notice_acknowledged: false,
|
|
opportunity_id: null as number | null,
|
|
});
|
|
const credentials = (form: typeof login, prefix: string) => (
|
|
<>
|
|
<label className="grid gap-2">
|
|
Email
|
|
<input
|
|
className={controlClass}
|
|
type="email"
|
|
required
|
|
autoComplete="email"
|
|
value={form.data.email}
|
|
onChange={(e) => form.setData('email', e.target.value)}
|
|
/>
|
|
</label>
|
|
<label className="grid gap-2">
|
|
Palavra-passe
|
|
<input
|
|
className={controlClass}
|
|
type="password"
|
|
required
|
|
autoComplete={
|
|
prefix === 'login' ? 'current-password' : 'new-password'
|
|
}
|
|
value={form.data.password}
|
|
onChange={(e) => form.setData('password', e.target.value)}
|
|
/>
|
|
</label>
|
|
</>
|
|
);
|
|
return (
|
|
<>
|
|
<Head title="Área do candidato" />
|
|
<Link
|
|
href={`/candidaturas/${company.id}/recuperar`}
|
|
className="m-5 inline-block underline"
|
|
>
|
|
Recuperar acesso
|
|
</Link>
|
|
<main className="mx-auto max-w-3xl space-y-6 p-6">
|
|
<p>SGC · Uma solução TEMPO</p>
|
|
<h1 className="text-3xl font-semibold">
|
|
Área do candidato · {company.name}
|
|
</h1>
|
|
<p>
|
|
Prepare o seu perfil e candidate-se às oportunidades
|
|
publicadas. Os documentos exigidos são indicados em cada
|
|
formulário. Criar um perfil não constitui candidatura ou
|
|
contratação.
|
|
</p>
|
|
<section
|
|
className="space-y-3"
|
|
aria-label="Oportunidades publicadas"
|
|
>
|
|
<h2 className="text-xl font-semibold">
|
|
Oportunidades publicadas
|
|
</h2>
|
|
{opportunities.data.length === 0 && (
|
|
<p>Não existem oportunidades nesta página.</p>
|
|
)}
|
|
{opportunities.data.map((item) => (
|
|
<article
|
|
key={item.id}
|
|
className="space-y-2 rounded-lg border p-4"
|
|
>
|
|
<h3 className="font-semibold">{item.title}</h3>
|
|
<p>
|
|
{item.kind === 'service'
|
|
? 'Prestação de serviços'
|
|
: 'Recrutamento'}{' '}
|
|
· Prazo: {item.closes_on}
|
|
</p>
|
|
<details>
|
|
<summary className="cursor-pointer underline">
|
|
Consultar descrição publicada
|
|
</summary>
|
|
<p className="whitespace-pre-wrap">
|
|
{item.description}
|
|
</p>
|
|
</details>
|
|
<a
|
|
className="inline-block underline"
|
|
href="#candidate-login"
|
|
onClick={() => {
|
|
login.setData('opportunity_id', item.id);
|
|
register.setData('opportunity_id', item.id);
|
|
}}
|
|
>
|
|
Entrar para preparar a candidatura
|
|
</a>
|
|
</article>
|
|
))}
|
|
<nav
|
|
aria-label="Páginas de oportunidades"
|
|
className="flex gap-4"
|
|
>
|
|
{opportunities.prev_page_url && (
|
|
<Link
|
|
href={opportunities.prev_page_url}
|
|
preserveScroll
|
|
>
|
|
Anterior
|
|
</Link>
|
|
)}
|
|
{opportunities.next_page_url && (
|
|
<Link
|
|
href={opportunities.next_page_url}
|
|
preserveScroll
|
|
>
|
|
Seguinte
|
|
</Link>
|
|
)}
|
|
</nav>
|
|
</section>
|
|
<section className="space-y-3" id="candidate-login">
|
|
{login.data.opportunity_id !== null && (
|
|
<p>
|
|
Oportunidade escolhida:{' '}
|
|
{
|
|
opportunities.data.find(
|
|
(item) =>
|
|
item.id === login.data.opportunity_id,
|
|
)?.title
|
|
}
|
|
</p>
|
|
)}
|
|
<h2 className="text-xl font-semibold">Entrar</h2>
|
|
<form
|
|
className="space-y-3"
|
|
onSubmit={(e) => {
|
|
e.preventDefault();
|
|
login.post(`/candidaturas/${company.id}/entrar`, {
|
|
onFinish: () => login.reset('password'),
|
|
});
|
|
}}
|
|
>
|
|
<FormErrors errors={login.errors} />
|
|
{credentials(login, 'login')}
|
|
<Button disabled={login.processing}>
|
|
Entrar na minha área
|
|
</Button>
|
|
</form>
|
|
</section>
|
|
<section className="space-y-3">
|
|
<h2 className="text-xl font-semibold">Criar conta</h2>
|
|
<p className="whitespace-pre-wrap">{portal.notice}</p>
|
|
{portal.registration_open ? (
|
|
<form
|
|
className="space-y-3"
|
|
onSubmit={(e) => {
|
|
e.preventDefault();
|
|
register.post(
|
|
`/candidaturas/${company.id}/registar`,
|
|
{
|
|
onFinish: () =>
|
|
register.reset(
|
|
'password',
|
|
'password_confirmation',
|
|
),
|
|
},
|
|
);
|
|
}}
|
|
>
|
|
<FormErrors errors={register.errors} />
|
|
<label className="grid gap-2">
|
|
Email
|
|
<input
|
|
className={controlClass}
|
|
type="email"
|
|
autoComplete="email"
|
|
required
|
|
value={register.data.email}
|
|
onChange={(e) =>
|
|
register.setData(
|
|
'email',
|
|
e.target.value,
|
|
)
|
|
}
|
|
/>
|
|
</label>
|
|
<label className="grid gap-2">
|
|
Palavra-passe (mínimo 12 caracteres)
|
|
<input
|
|
className={controlClass}
|
|
type="password"
|
|
autoComplete="new-password"
|
|
minLength={12}
|
|
maxLength={128}
|
|
required
|
|
value={register.data.password}
|
|
onChange={(e) =>
|
|
register.setData(
|
|
'password',
|
|
e.target.value,
|
|
)
|
|
}
|
|
/>
|
|
</label>
|
|
<label className="grid gap-2">
|
|
Confirmar palavra-passe
|
|
<input
|
|
className={controlClass}
|
|
type="password"
|
|
autoComplete="new-password"
|
|
required
|
|
value={register.data.password_confirmation}
|
|
onChange={(e) =>
|
|
register.setData(
|
|
'password_confirmation',
|
|
e.target.value,
|
|
)
|
|
}
|
|
/>
|
|
</label>
|
|
<label className="flex gap-2">
|
|
<input
|
|
type="checkbox"
|
|
required
|
|
checked={register.data.notice_acknowledged}
|
|
onChange={(e) =>
|
|
register.setData(
|
|
'notice_acknowledged',
|
|
e.target.checked,
|
|
)
|
|
}
|
|
/>
|
|
Tomei conhecimento do aviso apresentado, versão{' '}
|
|
{portal.version}.
|
|
</label>
|
|
<p className="text-sm">
|
|
Este registo não autoriza a partilha do perfil
|
|
para outras finalidades ou empresas.
|
|
</p>
|
|
<Button disabled={register.processing}>
|
|
Criar a minha conta
|
|
</Button>
|
|
</form>
|
|
) : (
|
|
<p>
|
|
Novos registos estão encerrados. As contas
|
|
existentes podem continuar a entrar.
|
|
</p>
|
|
)}
|
|
</section>
|
|
</main>
|
|
</>
|
|
);
|
|
}
|