Files
Investidor-V2/resources/js/pages/management/catalog.tsx
T
nala 1106509116
tests / ci (push) Canceled after 0s
vrsao 2
2026-10-08 14:02:01 +02:00

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