197 lines
7.7 KiB
TypeScript
197 lines
7.7 KiB
TypeScript
import { Link, useForm } from '@inertiajs/react';
|
|
import { Frame, card, button } from '@/pages/investment/shared';
|
|
import { Field, control, states, type Publication } from './shared';
|
|
type Choice = { id: string; name: string };
|
|
export default function Index({
|
|
items,
|
|
filters,
|
|
options,
|
|
regimes,
|
|
management,
|
|
}: {
|
|
items: {
|
|
data: { id: string; publication: Publication; state: string }[];
|
|
current_page: number;
|
|
links: { url: string | null; label: string; active: boolean }[];
|
|
};
|
|
filters: Record<string, string>;
|
|
options: Record<string, Choice[]>;
|
|
regimes: Record<string, string>;
|
|
management: boolean;
|
|
}) {
|
|
const form = useForm({
|
|
q: filters.q ?? '',
|
|
entity: filters.entity ?? '',
|
|
sector: filters.sector ?? '',
|
|
territory: filters.territory ?? '',
|
|
modality: filters.modality ?? '',
|
|
financier: filters.financier ?? '',
|
|
state: filters.state ?? '',
|
|
deadline_before: filters.deadline_before ?? '',
|
|
regime: filters.regime ?? '',
|
|
});
|
|
const query = new URLSearchParams(
|
|
Object.entries({ ...filters, page: String(items.current_page) }).filter(
|
|
([, v]) => v != null && v !== '',
|
|
),
|
|
).toString();
|
|
return (
|
|
<Frame title="Concursos e anúncios">
|
|
<p>
|
|
Procedimentos e actos divulgados pelas entidades responsáveis.
|
|
Uma oportunidade divulgada não é, por si, um concurso aberto.
|
|
</p>
|
|
<div className="flex flex-wrap gap-3">
|
|
<Link className="underline" href="/concursos/acompanhar">
|
|
Os concursos que acompanho
|
|
</Link>
|
|
{management && (
|
|
<Link className={button} href="/management/concursos">
|
|
Gerir concursos e anúncios
|
|
</Link>
|
|
)}
|
|
</div>
|
|
<form
|
|
className={`${card} grid gap-3 md:grid-cols-3`}
|
|
onSubmit={(e) => {
|
|
e.preventDefault();
|
|
form.get('/concursos', { preserveState: true });
|
|
}}
|
|
>
|
|
<Field label="Palavras-chave">
|
|
<input
|
|
className={control}
|
|
value={form.data.q}
|
|
onChange={(e) => form.setData('q', e.target.value)}
|
|
/>
|
|
</Field>
|
|
{(
|
|
[
|
|
'entity',
|
|
'sector',
|
|
'territory',
|
|
'modality',
|
|
'financier',
|
|
] as const
|
|
).map((key) => (
|
|
<Field
|
|
key={key}
|
|
label={
|
|
{
|
|
entity: 'Entidade',
|
|
sector: 'Sector',
|
|
territory: 'Localização',
|
|
modality: 'Modalidade',
|
|
financier: 'Financiador',
|
|
}[key]
|
|
}
|
|
>
|
|
<select
|
|
className={control}
|
|
value={form.data[key]}
|
|
onChange={(e) => form.setData(key, e.target.value)}
|
|
>
|
|
<option value="">Todos</option>
|
|
{(options[key] ?? []).map((o) => (
|
|
<option key={o.id} value={o.id}>
|
|
{o.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</Field>
|
|
))}
|
|
<Field label="Regime">
|
|
<select
|
|
className={control}
|
|
value={form.data.regime}
|
|
onChange={(e) => form.setData('regime', e.target.value)}
|
|
>
|
|
<option value="">Todos</option>
|
|
{Object.entries(regimes).map(([v, l]) => (
|
|
<option key={v} value={v}>
|
|
{l}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</Field>
|
|
<Field label="Estado">
|
|
<select
|
|
className={control}
|
|
value={form.data.state}
|
|
onChange={(e) => form.setData('state', e.target.value)}
|
|
>
|
|
<option value="">Todos</option>
|
|
{Object.entries(states).map(([v, l]) => (
|
|
<option key={v} value={v}>
|
|
{l}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</Field>
|
|
<Field label="Prazo até (data local do procedimento)">
|
|
<input
|
|
className={control}
|
|
type="date"
|
|
value={form.data.deadline_before}
|
|
onChange={(e) =>
|
|
form.setData('deadline_before', e.target.value)
|
|
}
|
|
/>
|
|
</Field>
|
|
<button className={button}>Pesquisar</button>
|
|
</form>
|
|
{!items.data.length && (
|
|
<p role="status">Nenhum anúncio corresponde aos filtros.</p>
|
|
)}
|
|
<div className="grid gap-4 md:grid-cols-2">
|
|
{items.data.map((item) => (
|
|
<article key={item.id} className={card}>
|
|
<h2 className="text-xl font-semibold">
|
|
<Link
|
|
className="underline"
|
|
href={`/concursos/${item.id}${query ? '?' + query : ''}`}
|
|
>
|
|
{String(item.publication.payload.title)}
|
|
</Link>
|
|
</h2>
|
|
<p>
|
|
{String(item.publication.payload.organization_name)}{' '}
|
|
· {states[item.state]}
|
|
</p>
|
|
<p>
|
|
Entrega:{' '}
|
|
{String(item.publication.payload.deadline).replace(
|
|
'T',
|
|
' ',
|
|
)}{' '}
|
|
· {String(item.publication.payload.timezone)}
|
|
</p>
|
|
<p>{String(item.publication.payload.object)}</p>
|
|
</article>
|
|
))}
|
|
</div>
|
|
<nav aria-label="Paginação">
|
|
{items.links.map((link, i) =>
|
|
link.url ? (
|
|
<Link
|
|
key={i}
|
|
className={`mx-2 underline ${link.active ? 'font-bold' : ''}`}
|
|
href={link.url}
|
|
>
|
|
{link.label
|
|
.replace('«', '«')
|
|
.replace('»', '»')}
|
|
</Link>
|
|
) : (
|
|
<span key={i} className="mx-2">
|
|
{link.label
|
|
.replace('«', '«')
|
|
.replace('»', '»')}
|
|
</span>
|
|
),
|
|
)}
|
|
</nav>
|
|
</Frame>
|
|
);
|
|
}
|