Files
Investidor-V2/resources/js/components/investment-complements.tsx
T

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