Files
Investidor-V2/resources/js/pages/partnerships/editor.tsx
T
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

449 lines
19 KiB
TypeScript

import { createOperationKey } from '@/lib/operation-key';
import { Link, useForm } from '@inertiajs/react';
import { useState } from 'react';
import {
Frame,
Field,
Errors,
control,
button,
type Partnership,
type Catalogues,
} from './shared';
type Location = {
geo_location_id: string;
role: string;
source_reference: string;
};
type Origin = { type: string; target_id: string };
type Props = Catalogues & {
record: Partnership | null;
locations: Location[];
origins: Origin[];
};
function Editor(p: Props) {
const form = useForm({
client_key: createOperationKey(),
version: p.record?.version ?? null,
title: p.record?.title ?? '',
offer: p.record?.offer ?? '',
seeking: p.record?.seeking ?? '',
organization_id: p.record?.organization_id ?? '',
sector_id: p.record?.sector_id?.toString() ?? '',
kind: p.record?.kind ?? 'seeking',
partnership_type: p.record?.partnership_type ?? '',
deadline: p.record?.deadline ?? '',
payload: {
summary: p.record?.payload.summary ?? '',
requirements: p.record?.payload.requirements ?? '',
mandatory: p.record?.payload.mandatory ?? ([] as string[]),
confidentiality: p.record?.payload.confidentiality ?? 'public',
consent: p.record?.payload.consent ?? false,
},
locations: p.locations.map((x) => ({
geo_location_id: x.geo_location_id,
role: x.role,
source_reference: x.source_reference,
})),
origins: p.origins.map((x) => ({
type: x.type,
target_id: x.target_id,
})),
});
const [place, setPlace] = useState('');
const [role, setRole] = useState('primary');
const [source, setSource] = useState('Declaração do proponente');
const [origin, setOrigin] = useState('');
return (
<Frame title={p.record ? 'Editar parceria' : 'Preparar parceria'}>
<p className="mb-5">
Guarde uma ideia e retome-a no seu painel. Empresa constituída,
NUIT, documentos e verificação não são exigidos para preparar o
rascunho. As informações desconhecidas ficam pendentes.
</p>
<form
className="space-y-5"
onSubmit={(e) => {
e.preventDefault();
if (p.record) form.put('/parcerias/' + p.record.id);
else form.post('/parcerias/rascunhos');
}}
>
<Errors errors={form.errors} />
<Field label="Título">
<input
className={control}
value={form.data.title}
onChange={(e) => form.setData('title', e.target.value)}
/>
</Field>
<div className="grid gap-5 md:grid-cols-2">
<Field label="Participação">
<select
className={control}
value={form.data.organization_id}
onChange={(e) =>
form.setData('organization_id', e.target.value)
}
>
<option value="">
Ideia pessoal — sem organização indicada
</option>
{p.organizations.map((x) => (
<option key={x.id} value={x.id}>
{x.name}
</option>
))}
</select>
</Field>
<Field label="Oferta ou procura">
<select
className={control}
value={form.data.kind}
onChange={(e) =>
form.setData('kind', e.target.value)
}
>
<option value="seeking">Procura</option>
<option value="offering">Oferta</option>
</select>
</Field>
<Field label="Tipo de parceria">
<select
className={control}
value={form.data.partnership_type}
onChange={(e) =>
form.setData('partnership_type', e.target.value)
}
>
<option value="">Por definir</option>
{Object.entries(p.types).map(([k, v]) => (
<option key={k} value={k}>
{v}
</option>
))}
</select>
</Field>
<Field label="Setor">
<select
className={control}
value={form.data.sector_id}
onChange={(e) =>
form.setData('sector_id', e.target.value)
}
>
<option value="">Por definir</option>
{p.sectors.map((x) => (
<option key={x.id} value={x.id}>
{x.name}
</option>
))}
</select>
</Field>
</div>
<Field label="Resumo público">
<textarea
className={control}
value={form.data.payload.summary}
onChange={(e) =>
form.setData('payload', {
...form.data.payload,
summary: e.target.value,
})
}
/>
</Field>
<Field label="O que oferece">
<textarea
className={control}
value={form.data.offer}
onChange={(e) => form.setData('offer', e.target.value)}
/>
</Field>
<Field label="O que procura">
<textarea
className={control}
value={form.data.seeking}
onChange={(e) =>
form.setData('seeking', e.target.value)
}
/>
</Field>
<Field label="Requisitos e critérios desejáveis">
<textarea
className={control}
value={form.data.payload.requirements}
onChange={(e) =>
form.setData('payload', {
...form.data.payload,
requirements: e.target.value,
})
}
/>
</Field>
<fieldset className="space-y-2 rounded border p-4">
<legend>
Critérios obrigatórios expressos da proposta
</legend>
<p>
Marque apenas condições necessárias. Critérios
desejáveis servem a pontuação. Informação desconhecida
exige esclarecimento e não constitui incumprimento
comprovado.
</p>
{Object.entries(p.criteria).map(([k, v]) => (
<label className="block" key={k}>
<input
type="checkbox"
checked={form.data.payload.mandatory.includes(
k,
)}
onChange={(e) =>
form.setData('payload', {
...form.data.payload,
mandatory: e.target.checked
? [
...form.data.payload
.mandatory,
k,
]
: form.data.payload.mandatory.filter(
(x) => x !== k,
),
})
}
/>{' '}
{v}
</label>
))}
</fieldset>
<div className="grid gap-4 md:grid-cols-2">
<Field label="Prazo para interesses">
<input
type="date"
className={control}
value={form.data.deadline}
onChange={(e) =>
form.setData('deadline', e.target.value)
}
/>
</Field>
<Field label="Visibilidade do conteúdo detalhado">
<select
className={control}
value={form.data.payload.confidentiality}
onChange={(e) =>
form.setData('payload', {
...form.data.payload,
confidentiality: e.target.value,
})
}
>
<option value="public">
Público após moderação
</option>
<option value="private">
Resumo público; detalhes privados
</option>
</select>
</Field>
</div>
<section className="space-y-4 rounded border p-5">
<h2 className="font-bold">Localizações e abrangência</h2>
<p>
A localização de uma origem fica como contexto. Só
acrescente uma localização própria com fonte; não se
deduz por proximidade ou pertença a cluster.
</p>
<div className="grid gap-3 md:grid-cols-3">
<Field label="Localização do cadastro">
<select
className={control}
value={place}
onChange={(e) => setPlace(e.target.value)}
>
<option value="">Escolher território</option>
{p.territories.map((x) => (
<option key={x.id} value={x.id}>
{x.name} · {x.location_type}
</option>
))}
</select>
</Field>
<Field label="Papel">
<select
className={control}
value={role}
onChange={(e) => setRole(e.target.value)}
>
<option value="primary">Principal</option>
<option value="operating">
Área de atuação
</option>
<option value="coverage">Abrangência</option>
</select>
</Field>
<Field label="Fonte da localização">
<input
className={control}
value={source}
onChange={(e) => setSource(e.target.value)}
/>
</Field>
</div>
<button
type="button"
className={button}
disabled={!place || !source}
onClick={() => {
if (
!form.data.locations.some(
(x) =>
x.geo_location_id === place &&
x.role === role,
)
)
form.setData('locations', [
...form.data.locations,
{
geo_location_id: place,
role,
source_reference: source,
},
]);
}}
>
Acrescentar localização
</button>
{form.data.locations.map((l, i) => (
<p key={i}>
{
p.territories.find(
(t) => t.id === l.geo_location_id,
)?.name
}{' '}
· {l.role} · {l.source_reference}{' '}
<button
type="button"
className="underline"
onClick={() =>
form.setData(
'locations',
form.data.locations.filter(
(_, j) => j !== i,
),
)
}
>
Remover
</button>
</p>
))}
</section>
<section className="space-y-3 rounded border p-5">
<h2 className="font-bold">Registos de origem</h2>
<Field label="Adicionar origem existente">
<select
className={control}
value={origin}
onChange={(e) => setOrigin(e.target.value)}
>
<option value="">
Escolher empresa, investimento, projeto,
cluster, zona ou ficha
</option>
{p.sources?.map((x) => (
<option
key={x.type + ':' + x.target_id}
value={x.type + ':' + x.target_id}
>
{x.title}
</option>
))}
</select>
</Field>
<button
type="button"
className={button}
disabled={!origin}
onClick={() => {
const [type, target_id] = origin.split(':');
if (
!form.data.origins.some(
(x) =>
x.type === type &&
x.target_id === target_id,
)
)
form.setData('origins', [
...form.data.origins,
{ type, target_id },
]);
}}
>
Ligar origem
</button>
{form.data.origins.map((o, i) => (
<p key={i}>
{p.sources?.find(
(x) =>
x.type === o.type &&
x.target_id === o.target_id,
)?.title ?? 'Origem documental ligada'}{' '}
<button
type="button"
className="underline"
onClick={() =>
form.setData(
'origins',
form.data.origins.filter(
(_, j) => j !== i,
),
)
}
>
Remover
</button>
</p>
))}
</section>
<label className="block">
<input
type="checkbox"
checked={form.data.payload.consent}
onChange={(e) =>
form.setData('payload', {
...form.data.payload,
consent: e.target.checked,
})
}
/>{' '}
Autorizo submeter o conteúdo indicado para divulgação no
âmbito escolhido. Não confirma verificação ou certificação.
</label>
<div className="flex flex-wrap gap-4">
<button className={button} disabled={form.processing}>
Guardar rascunho
</button>
{p.record && (
<Link
className="underline"
href={'/parcerias/' + p.record.id}
>
Pré-visualizar e submeter
</Link>
)}
</div>
</form>
</Frame>
);
}
export default function Page(p: Props) {
return (
<Editor
key={`${p.record?.id ?? 'new'}:${p.record?.version ?? 0}`}
{...p}
/>
);
}