208 lines
8.5 KiB
TypeScript
208 lines
8.5 KiB
TypeScript
import { Link, useForm } from '@inertiajs/react';
|
|
import { Management, Pagination, button, control, states } from './shared';
|
|
import type { Paginated } from './shared';
|
|
|
|
type Record = {
|
|
id: number;
|
|
name?: string;
|
|
title?: string;
|
|
reference?: string;
|
|
slug: string;
|
|
is_active?: boolean;
|
|
status?: string;
|
|
geography_scope?: string;
|
|
primary_territory?: {
|
|
name: string;
|
|
location_type: string;
|
|
source_system?: string;
|
|
source_code?: string | null;
|
|
} | null;
|
|
};
|
|
export default function Catalog({
|
|
resource,
|
|
records,
|
|
filters,
|
|
sectors,
|
|
geographyReviewCount = 0,
|
|
}: {
|
|
resource: 'sectors' | 'opportunities';
|
|
records: Paginated<Record>;
|
|
filters: {
|
|
q?: string;
|
|
status?: string;
|
|
sector_id?: number | string;
|
|
geography_scope?: string;
|
|
};
|
|
sectors: { id: number; name: string }[];
|
|
geographyReviewCount?: number;
|
|
}) {
|
|
const form = useForm({
|
|
q: filters.q ?? '',
|
|
status: filters.status ?? '',
|
|
sector_id: String(filters.sector_id ?? ''),
|
|
geography_scope: filters.geography_scope ?? '',
|
|
});
|
|
return (
|
|
<Management
|
|
title={resource === 'sectors' ? 'Sectores' : 'Oportunidades'}
|
|
>
|
|
<Link className={button} href={`/management/${resource}/create`}>
|
|
Criar {resource === 'sectors' ? 'sector' : 'oportunidade'}
|
|
</Link>
|
|
{resource === 'opportunities' && (
|
|
<p role="status" className="my-3">
|
|
{geographyReviewCount} oportunidade(s) aguardam confirmação
|
|
do âmbito geográfico. A lista não altera localizações;
|
|
confirme apenas com fontes existentes.
|
|
</p>
|
|
)}
|
|
<form
|
|
className="flex flex-wrap gap-3"
|
|
onSubmit={(event) => {
|
|
event.preventDefault();
|
|
form.get(`/management/${resource}`, {
|
|
preserveState: false,
|
|
});
|
|
}}
|
|
>
|
|
<input
|
|
aria-label="Pesquisar catálogo"
|
|
placeholder="Nome, título, referência ou slug"
|
|
className={`${control} max-w-sm`}
|
|
value={form.data.q}
|
|
onChange={(event) => form.setData('q', event.target.value)}
|
|
maxLength={150}
|
|
/>
|
|
<select
|
|
aria-label="Estado"
|
|
className={`${control} max-w-xs`}
|
|
value={form.data.status}
|
|
onChange={(event) =>
|
|
form.setData('status', event.target.value)
|
|
}
|
|
>
|
|
<option value="">Todos os estados</option>
|
|
{(resource === 'sectors'
|
|
? ['active', 'inactive']
|
|
: ['draft', 'published', 'archived']
|
|
).map((status) => (
|
|
<option key={status} value={status}>
|
|
{status === 'active'
|
|
? 'Activo'
|
|
: status === 'inactive'
|
|
? 'Inactivo'
|
|
: states[status]}
|
|
</option>
|
|
))}
|
|
</select>
|
|
{resource === 'opportunities' && (
|
|
<>
|
|
<select
|
|
aria-label="Sector"
|
|
className={`${control} max-w-xs`}
|
|
value={form.data.sector_id}
|
|
onChange={(event) =>
|
|
form.setData('sector_id', event.target.value)
|
|
}
|
|
>
|
|
<option value="">Todos os sectores</option>
|
|
{sectors.map((sector) => (
|
|
<option key={sector.id} value={sector.id}>
|
|
{sector.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
<select
|
|
aria-label="Âmbito geográfico"
|
|
className={`${control} max-w-xs`}
|
|
value={form.data.geography_scope}
|
|
onChange={(event) =>
|
|
form.setData(
|
|
'geography_scope',
|
|
event.target.value,
|
|
)
|
|
}
|
|
>
|
|
<option value="">Todos os âmbitos</option>
|
|
<option value="unspecified">
|
|
Pendente de confirmação
|
|
</option>
|
|
<option value="specific">
|
|
Localização específica
|
|
</option>
|
|
<option value="multiple">
|
|
Várias localizações
|
|
</option>
|
|
<option value="national">Âmbito nacional</option>
|
|
<option value="digital">
|
|
Digital/sem instalação definida
|
|
</option>
|
|
</select>
|
|
</>
|
|
)}
|
|
<button className={button} disabled={form.processing}>
|
|
Pesquisar
|
|
</button>
|
|
<Link
|
|
className="p-2 underline"
|
|
href={`/management/${resource}`}
|
|
>
|
|
Limpar filtros
|
|
</Link>
|
|
{Object.values(form.errors).map((error) => (
|
|
<p key={error} role="alert" className="w-full text-red-600">
|
|
{error}
|
|
</p>
|
|
))}
|
|
</form>
|
|
<div className="space-y-3">
|
|
{records.data.map((record) => (
|
|
<article
|
|
key={record.id}
|
|
className="flex flex-wrap items-center justify-between gap-3 rounded border p-4"
|
|
>
|
|
<div>
|
|
<h2 className="font-semibold">
|
|
{record.name ?? record.title}
|
|
</h2>
|
|
<p className="text-muted-foreground text-sm">
|
|
{record.reference ?? record.slug} ·{' '}
|
|
{resource === 'sectors'
|
|
? record.is_active
|
|
? 'Activo'
|
|
: 'Inactivo'
|
|
: states[record.status ?? 'draft']}
|
|
</p>
|
|
{resource === 'opportunities' &&
|
|
record.geography_scope === 'unspecified' && (
|
|
<p className="mt-1 text-sm">
|
|
Pendente de confirmação; território já
|
|
registado:{' '}
|
|
{record.primary_territory?.name ??
|
|
'nenhum'}
|
|
. Fonte do território:{' '}
|
|
{record.primary_territory
|
|
?.source_system ?? 'não indicada'}
|
|
{record.primary_territory?.source_code
|
|
? `, código ${record.primary_territory.source_code}`
|
|
: ''}
|
|
. A classificação não é alterada
|
|
automaticamente.
|
|
</p>
|
|
)}
|
|
</div>
|
|
<Link
|
|
className="underline"
|
|
href={`/management/${resource}/${record.id}/edit`}
|
|
>
|
|
Editar
|
|
</Link>
|
|
</article>
|
|
))}
|
|
</div>
|
|
{!records.data.length && <p>Nenhum registo encontrado.</p>}
|
|
<Pagination page={records} />
|
|
</Management>
|
|
);
|
|
}
|