Files
Investidor-V2/resources/js/pages/partnerships/editor.tsx
T

525 lines
26 KiB
TypeScript

import { Link } from '@inertiajs/react';
import { useState } from 'react';
import {
usePartnershipEdit,
type PartnershipEditProps,
} from '@/lib/partnership-edit';
import PartnershipEditComparison from '@/components/partnership-edit-comparison';
import {
Frame,
Field,
Errors,
control,
button,
type Catalogues,
} from './shared';
type Props = Catalogues & PartnershipEditProps;
function Editor(p: Props) {
const edit = usePartnershipEdit(p);
const form = edit.form;
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();
edit.save();
}}
>
<Errors errors={form.errors} />
<PartnershipEditComparison editor={edit} catalogues={p} />
<fieldset
className="space-y-5"
disabled={!edit.editable || edit.invalid}
>
<legend className="sr-only">Conteúdo da parceria</legend>
<Field label="Título">
<input
className={control}
value={form.data.title}
onChange={(e) =>
edit.setData('title', e.target.value)
}
/>
</Field>
<div className="grid gap-5 md:grid-cols-2">
<Field label="Organização que representa (opcional)">
<select
className={control}
value={form.data.organization_id}
onChange={(e) =>
edit.setData(
'organization_id',
e.target.value,
)
}
>
<option value="">
Sem organização indicada
</option>
{form.data.organization_id &&
!p.organizations.some(
(organization) =>
String(organization.id) ===
form.data.organization_id,
) && (
<option
value={form.data.organization_id}
>
Organização anteriormente indicada —
fora da lista actual
</option>
)}
{p.organizations.map((x) => (
<option key={x.id} value={x.id}>
{x.name}
</option>
))}
</select>
<p className="text-sm">
A autoria mantém-se. Sem organização ligada, só
o proponente originalmente registado pode editar
esta proposta.
</p>
</Field>
<Field label="Oferta ou procura">
<select
className={control}
value={form.data.kind}
onChange={(e) =>
edit.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) =>
edit.setData(
'partnership_type',
e.target.value,
)
}
>
<option value="">Por definir</option>
{form.data.partnership_type &&
!Object.hasOwn(
p.types,
form.data.partnership_type,
) && (
<option
value={form.data.partnership_type}
>
Tipo anteriormente indicado — fora
da configuração actual
</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) =>
edit.setData('sector_id', e.target.value)
}
>
<option value="">Por definir</option>
{form.data.sector_id &&
!p.sectors.some(
(sector) =>
String(sector.id) ===
form.data.sector_id,
) && (
<option value={form.data.sector_id}>
Sector anteriormente indicado — fora
da lista actual
</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) =>
edit.setData('payload', (previous) => ({
...previous,
summary: e.target.value,
}))
}
/>
</Field>
<Field label="O que oferece">
<textarea
className={control}
value={form.data.offer}
onChange={(e) =>
edit.setData('offer', e.target.value)
}
/>
</Field>
<Field label="O que procura">
<textarea
className={control}
value={form.data.seeking}
onChange={(e) =>
edit.setData('seeking', e.target.value)
}
/>
</Field>
<Field label="Requisitos e critérios desejáveis">
<textarea
className={control}
value={form.data.payload.requirements}
onChange={(e) =>
edit.setData('payload', (previous) => ({
...previous,
requirements: e.target.value,
}))
}
/>
</Field>
<Field label="Disponibilidade dos recursos ou da colaboração (opcional)">
<textarea
className={control}
maxLength={1000}
value={form.data.payload.availability}
onChange={(event) =>
edit.setData('payload', (previous) => ({
...previous,
availability: event.target.value,
}))
}
/>
<p className="text-sm">
Indique quando e em que condições os recursos ou a
colaboração estarão disponíveis. O prazo de recepção
de interesses é indicado separadamente.
</p>
</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) =>
edit.setData('payload', (previous) => ({
...previous,
mandatory: e.target.checked
? [...previous.mandatory, k]
: previous.mandatory.filter(
(x) => x !== k,
),
}))
}
/>{' '}
{v}
</label>
))}
</fieldset>
{form.data.payload.mandatory
.filter((code) => !Object.hasOwn(p.criteria, code))
.map((code) => (
<p className="rounded border p-3" key={code}>
Critério anteriormente indicado — fora da
configuração actual: {code}. Retirá-lo da
preparação altera apenas a próxima gravação; o
histórico mantém-se.{' '}
<button
type="button"
className="underline"
disabled={!edit.editable || edit.invalid}
onClick={() =>
edit.setData('payload', (previous) => ({
...previous,
mandatory:
previous.mandatory.filter(
(value) => value !== code,
),
}))
}
>
Retirar da preparação
</button>
</p>
))}
<div className="grid gap-4 md:grid-cols-2">
<Field label="Prazo para receber manifestações de interesse (opcional)">
<input
type="date"
className={control}
value={form.data.deadline}
onChange={(e) =>
edit.setData('deadline', e.target.value)
}
/>
</Field>
<Field label="Visibilidade do conteúdo detalhado">
<select
className={control}
value={form.data.payload.confidentiality}
onChange={(e) =>
edit.setData('payload', (previous) => ({
...previous,
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={() => {
edit.setData('locations', (previous) =>
previous.some(
(x) =>
x.geo_location_id === place &&
x.role === role,
)
? previous
: [
...previous,
{
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={() =>
edit.setData('locations', (previous) =>
previous.filter(
(item) =>
item.geo_location_id !==
l.geo_location_id ||
item.role !== l.role,
),
)
}
>
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(':');
edit.setData('origins', (previous) =>
previous.some(
(item) =>
item.type === type &&
item.target_id === target_id,
)
? previous
: [...previous, { 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={() =>
edit.setData('origins', (previous) =>
previous.filter(
(item) =>
item.type !== o.type ||
item.target_id !==
o.target_id,
),
)
}
>
Remover
</button>
</p>
))}
</section>
<label className="block">
<input
type="checkbox"
checked={form.data.payload.consent}
onChange={(e) =>
edit.setData('payload', (previous) => ({
...previous,
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 || edit.blocked}
>
{edit.retryingCreation
? 'Recuperar gravação anterior'
: edit.current?.status === 'published'
? 'Guardar revisão em rascunho'
: 'Guardar rascunho'}
</button>
{p.record && (
<Link
className="underline"
href={'/parcerias/' + p.record.id}
>
Pré-visualizar e submeter
</Link>
)}
</div>
</fieldset>
</form>
</Frame>
);
}
export default function Page(p: Props) {
return <Editor key={p.editorActorId ?? 'legacy-editor'} {...p} />;
}