239 lines
9.1 KiB
TypeScript
239 lines
9.1 KiB
TypeScript
import { Link, usePage } from '@inertiajs/react';
|
|
import { complementPreparationHref } from '@/lib/search-territory';
|
|
import { useState } from 'react';
|
|
import { createOperationKey } from '@/lib/operation-key';
|
|
|
|
export function ComplementEntryActions({ target }: { target: string }) {
|
|
const { url } = usePage();
|
|
const href = complementPreparationHref(url, target);
|
|
return href ? (
|
|
<Link className="block rounded border p-3 underline" href={href}>
|
|
Seleccionar este complemento e continuar no dossier de origem
|
|
</Link>
|
|
) : null;
|
|
}
|
|
|
|
type Offer = { key: string; title: string; href: string; location?: string };
|
|
export type Complement = {
|
|
key: string;
|
|
code: string;
|
|
title: string;
|
|
kind: 'opportunity' | 'service' | 'need';
|
|
origin: Offer;
|
|
target: Offer | null;
|
|
territory_id: string | null;
|
|
territory?: { title: string } | null;
|
|
source: string;
|
|
excerpt: string;
|
|
source_sha256: string;
|
|
availability?: 'review_required';
|
|
};
|
|
export type ComplementOption = Omit<Complement, 'key' | 'kind' | 'target'> & {
|
|
opportunities: Offer[];
|
|
services: Offer[];
|
|
};
|
|
export const complementInput = (item: Complement) => ({
|
|
origin: item.origin.key,
|
|
code: item.code,
|
|
target: item.target?.key ?? null,
|
|
territory_id: item.territory_id,
|
|
});
|
|
|
|
export function SelectedComplements({
|
|
items,
|
|
onRemove,
|
|
}: {
|
|
items: Complement[];
|
|
onRemove?: (key: string) => void;
|
|
}) {
|
|
if (!items.length) return null;
|
|
return (
|
|
<section
|
|
className="space-y-3 rounded-xl border p-4"
|
|
aria-label="Complementos escolhidos"
|
|
>
|
|
<h2 className="text-xl font-bold">
|
|
Complementos escolhidos no dossier
|
|
</h2>
|
|
{items.map((item) => (
|
|
<article key={item.key} className="space-y-1">
|
|
<h3 className="font-semibold">
|
|
{item.title} ·{' '}
|
|
{item.kind === 'need'
|
|
? 'Necessidade complementar — ideia em preparação'
|
|
: item.kind === 'service'
|
|
? 'Serviço ou instrumento'
|
|
: 'Oportunidade cadastrada'}
|
|
</h3>
|
|
<p>
|
|
Investimento de origem:{' '}
|
|
<Link className="underline" href={item.origin.href}>
|
|
{item.origin.title}
|
|
</Link>
|
|
</p>
|
|
<p>
|
|
Localização da preparação:{' '}
|
|
{item.territory?.title ?? 'Por definir'}
|
|
</p>
|
|
{item.target && (
|
|
<Link
|
|
className="block underline"
|
|
href={item.target.href}
|
|
>
|
|
Abrir ficha: {item.target.title}
|
|
</Link>
|
|
)}
|
|
{item.availability === 'review_required' && (
|
|
<p role="status">
|
|
A oferta ou a referência mudou. Relação anterior
|
|
conservada para revisão; consulte as opções actuais
|
|
antes de a utilizar.
|
|
</p>
|
|
)}
|
|
<p className="text-sm">
|
|
Fonte: {item.source} · Trecho: {item.excerpt}
|
|
</p>
|
|
{onRemove && (
|
|
<button
|
|
type="button"
|
|
className="underline"
|
|
onClick={() => onRemove(item.key)}
|
|
>
|
|
Retirar esta relação da preparação
|
|
</button>
|
|
)}
|
|
</article>
|
|
))}
|
|
</section>
|
|
);
|
|
}
|
|
|
|
export default function InvestmentComplements({
|
|
items,
|
|
dossierId,
|
|
onNavigate,
|
|
}: {
|
|
items: ComplementOption[];
|
|
dossierId?: string;
|
|
onNavigate?: (href: string) => void;
|
|
}) {
|
|
const [selectionKey] = useState(createOperationKey);
|
|
if (!items.length) return null;
|
|
const prepare = (item: ComplementOption, target?: Offer) => {
|
|
const query = new URLSearchParams({
|
|
complement_origin: item.origin.key,
|
|
complement_code: item.code,
|
|
});
|
|
if (target) query.set('complement_target', target.key);
|
|
if (item.territory_id)
|
|
query.set('complement_territory', item.territory_id);
|
|
if (dossierId) query.set('selection_key', selectionKey);
|
|
return (
|
|
(dossierId
|
|
? `/investimentos/${dossierId}/contexto/editar`
|
|
: '/investimentos/iniciar') +
|
|
'?' +
|
|
query
|
|
);
|
|
};
|
|
const consult = (item: ComplementOption, offer: Offer) => {
|
|
const query = prepare(item, offer).split('?')[1];
|
|
return (
|
|
offer.href +
|
|
'?' +
|
|
query +
|
|
(dossierId
|
|
? '&investment_dossier=' + encodeURIComponent(dossierId)
|
|
: '')
|
|
);
|
|
};
|
|
return (
|
|
<section
|
|
className="space-y-4 rounded-xl border p-4"
|
|
aria-label="Investimentos e serviços complementares"
|
|
>
|
|
<h2 className="text-xl font-bold">
|
|
Investimentos e serviços complementares
|
|
</h2>
|
|
<p>
|
|
Escolha os complementos relevantes para o seu investimento. A
|
|
localização e as condições de cada ficha conservam-se.
|
|
</p>
|
|
{items.map((item) => (
|
|
<article
|
|
className="space-y-2 border-t pt-3"
|
|
key={
|
|
item.origin.key + item.code + (item.territory_id ?? '')
|
|
}
|
|
>
|
|
<h3 className="font-semibold">{item.title}</h3>
|
|
<p>
|
|
Investimento de origem: {item.origin.title} ·{' '}
|
|
{item.territory?.title ?? 'Localização por definir'}
|
|
</p>
|
|
<p className="text-sm">
|
|
Referência: {item.source} · Trecho: {item.excerpt}
|
|
</p>
|
|
{[...item.opportunities, ...item.services].map((offer) => (
|
|
<div key={offer.key} className="space-y-1">
|
|
<p>
|
|
<Link
|
|
className="underline"
|
|
href={consult(item, offer)}
|
|
onClick={(event) => {
|
|
if (onNavigate) {
|
|
event.preventDefault();
|
|
onNavigate(consult(item, offer));
|
|
}
|
|
}}
|
|
>
|
|
{offer.title}
|
|
</Link>{' '}
|
|
· {offer.location}
|
|
</p>
|
|
<Link
|
|
className="block underline"
|
|
href={prepare(item, offer)}
|
|
onClick={(event) => {
|
|
if (onNavigate) {
|
|
event.preventDefault();
|
|
onNavigate(prepare(item, offer));
|
|
}
|
|
}}
|
|
>
|
|
Seleccionar esta{' '}
|
|
{offer.key.startsWith('opportunity:')
|
|
? 'oportunidade'
|
|
: 'oferta'}{' '}
|
|
para o dossier
|
|
</Link>
|
|
</div>
|
|
))}
|
|
{!item.opportunities.length && !item.services.length && (
|
|
<>
|
|
<p>
|
|
Necessidade complementar referida; sem oferta
|
|
cadastrada identificada nas relações deste
|
|
contexto.
|
|
</p>
|
|
<Link
|
|
className="underline"
|
|
href={prepare(item)}
|
|
onClick={(event) => {
|
|
if (onNavigate) {
|
|
event.preventDefault();
|
|
onNavigate(prepare(item));
|
|
}
|
|
}}
|
|
>
|
|
Iniciar preparação de uma ideia de{' '}
|
|
{item.title.toLocaleLowerCase('pt-MZ')}
|
|
</Link>
|
|
</>
|
|
)}
|
|
</article>
|
|
))}
|
|
</section>
|
|
);
|
|
}
|