Files
compras/resources/js/pages/candidate/entry.tsx
T

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>
</>
);
}