Files
Investidor-V2/resources/js/pages/portfolio/index.tsx
T
2026-11-02 20:31:15 +02:00

224 lines
9.4 KiB
TypeScript

import { Link, useForm, usePage } from '@inertiajs/react';
import { searchTerritoryHref } from '@/lib/search-territory';
import { catalogueDetailHref } from '@/lib/catalogue-context';
import { Management } from '../management/shared';
import type { Paginated } from '../management/shared';
import { Layout, Field, control, button, Pagination, kind } from './shared';
import type { Dossier } from './shared';
import {
TerritoryFields,
type TerritoryOption,
} from '@/components/investment-territory';
export default function Index({
records,
management,
filters,
territorySelection = [],
sectors = [],
}: {
records: Paginated<Dossier>;
management: boolean;
sectors?: string[];
filters: {
q?: string;
kind?: string;
sector?: string;
province_id?: string;
district_id?: string;
admin_post_id?: string;
locality_id?: string;
};
territorySelection?: TerritoryOption[];
}) {
const form = useForm({
q: filters.q ?? '',
kind: filters.kind ?? '',
sector: filters.sector ?? '',
province_id: filters.province_id ?? '',
district_id: filters.district_id ?? '',
admin_post_id: filters.admin_post_id ?? '',
locality_id: filters.locality_id ?? '',
});
const base = management ? '/management/portfolio' : '/carteira';
const { url } = usePage();
const Frame = management ? Management : Layout;
return (
<Frame title="Corredores, clusters e projectos âncora">
<p>
Explore o território, identifique os elos da cadeia de valor e
acompanhe o seu interesse numa única conta.
</p>
{management && (
<p className="rounded border border-amber-500 p-3">
Matriz documental para revisão. Rascunhos não aparecem no
portal público. Pontuações não são recomendações
personalizadas.
</p>
)}
<form
className="flex flex-wrap items-end gap-3"
onSubmit={(e) => {
e.preventDefault();
form.get(base);
}}
>
<Field label="Pesquisar">
<input
className={control}
value={form.data.q}
onChange={(e) => form.setData('q', e.target.value)}
/>
</Field>
<Field label="Explorar por">
<select
className={control}
value={form.data.kind}
onChange={(e) => form.setData('kind', e.target.value)}
>
<option value="">Toda a carteira</option>
<option value="corridor">Corredores</option>
<option value="cluster">Matriz de clusters</option>
<option value="project">Projectos âncora</option>
</select>
</Field>
<Field label="Sector documentado">
<select
className={control}
value={form.data.sector}
onChange={(e) => form.setData('sector', e.target.value)}
>
<option value="">Todos os sectores</option>
{sectors.map((sector) => (
<option key={sector} value={sector}>
{sector}
</option>
))}
</select>
</Field>
<TerritoryFields
initial={territorySelection}
onChange={(path) =>
form.setData((current) => ({
...current,
province_id: path[0]?.id ?? '',
district_id: path[1]?.id ?? '',
admin_post_id: path[2]?.id ?? '',
locality_id: path[3]?.id ?? '',
}))
}
/>
<button className={button}>Pesquisar</button>
</form>
<div className="grid gap-4 md:grid-cols-2">
{records.data.map((r) => (
<article
key={r.id}
className="space-y-3 rounded-xl border p-5"
>
<p className="text-sm">
{kind(r)} · {r.content.code}
{management &&
` · ${r.publication_status === 'published' ? 'Publicado' : 'Rascunho'}`}
</p>
<h2 className="text-xl font-semibold">
<Link
className="underline"
href={catalogueDetailHref(
`${base}/${r.id}`,
new URLSearchParams(
Object.entries(filters).filter(
([, value]) => Boolean(value),
) as [string, string][],
).toString(),
url,
)}
>
{r.title}
</Link>
</h2>
<p>
{r.content.description ??
r.content.focus ??
r.content.anchors}
</p>
{(r.content.location || r.content.region) && (
<p>
Localização:{' '}
{r.content.location ?? r.content.region}
</p>
)}
{r.content.priority && (
<p>
{r.content.priority}{' '}
<span className="text-sm">
(proposta documental)
</span>
</p>
)}
<Link
className="underline"
href={catalogueDetailHref(
`${base}/${r.id}`,
new URLSearchParams(
Object.entries(filters).filter(
([, value]) => Boolean(value),
) as [string, string][],
).toString(),
url,
)}
>
Consultar ficha
</Link>
{!management &&
(r.corridor_id || r.cluster_id || r.project_id) && (
<Link
className="ml-4 underline"
href={searchTerritoryHref(
`/oportunidades?${new URLSearchParams(r.corridor_id ? { corridor_id: r.corridor_id } : r.cluster_id ? { cluster_id: r.cluster_id } : { project_id: r.project_id! })}`,
url,
)}
>
Ver oportunidades associadas
</Link>
)}
</article>
))}
</div>
{!records.data.length && (
<div className="space-y-3 rounded-xl border p-5" role="status">
<p>
Ainda não existem fichas publicadas para esta pesquisa.
Os filtros activos foram mantidos.
</p>
<p>
Esta lista contém dossiers territoriais documentados;
não representa concursos abertos nem confirma
disponibilidade de infra-estruturas ou incentivos.
</p>
<nav
aria-label="Alternativas de exploração"
className="flex flex-wrap gap-4"
>
<Link
className="underline"
href={searchTerritoryHref('/oportunidades', url)}
>
Ver oportunidades publicadas
</Link>
<Link
className="underline"
href={searchTerritoryHref('/zonas', url)}
>
Consultar ZEE, ZFI e parques
</Link>
<Link className="underline" href="/Simulador">
Transformar uma ideia num projecto
</Link>
</nav>
</div>
)}
<Pagination page={records} />
</Frame>
);
}