227 lines
10 KiB
TypeScript
227 lines
10 KiB
TypeScript
import { Link, useForm } from "@inertiajs/react";
|
|
import InvestmentCoordinates from "@/components/investment-coordinates";
|
|
import { TerritoryLevel, type TerritoryOption } from "@/components/investment-territory";
|
|
import { useState } from "react";
|
|
import { Management, Field, button, control, states } from "./shared";
|
|
|
|
type Values = Record<string, string | boolean>;
|
|
type Entry = Record<string, string | number | boolean | null> & { id: number };
|
|
const opportunityFields = {
|
|
reference: "Referência",
|
|
title: "Título",
|
|
slug: "Slug (endereço)",
|
|
summary: "Resumo",
|
|
description: "Descrição",
|
|
promoter_name: "Promotor",
|
|
promoter_type: "Tipo de promotor",
|
|
estimated_investment_amount: "Investimento estimado",
|
|
estimated_investment_currency: "Moeda (código de três letras)",
|
|
financing_modality: "Modalidade de financiamento",
|
|
expected_impact: "Impacto esperado",
|
|
};
|
|
const sectorFields = {
|
|
name: "Nome",
|
|
slug: "Slug (endereço)",
|
|
short_description: "Descrição curta",
|
|
description: "Descrição",
|
|
sort_order: "Ordem de apresentação",
|
|
};
|
|
const maturities = [
|
|
"Ideia identificada",
|
|
"Pré-estudo disponível",
|
|
"Estudo de viabilidade em curso",
|
|
"Projecto estruturado",
|
|
"Pronto para investimento",
|
|
"Em negociação ou implementação",
|
|
];
|
|
|
|
export default function Editor({
|
|
resource,
|
|
record,
|
|
sectors,
|
|
territoryPath = [],
|
|
}: {
|
|
resource: "sectors" | "opportunities";
|
|
record: Entry | null;
|
|
sectors: { id: number; name: string }[];
|
|
territoryPath?: TerritoryOption[];
|
|
}) {
|
|
const isSector = resource === "sectors";
|
|
const fields = isSector ? sectorFields : opportunityFields;
|
|
const initial: Values = Object.fromEntries(
|
|
Object.keys(fields).map((key) => [
|
|
key,
|
|
String(
|
|
record?.[key] ??
|
|
(key === "estimated_investment_currency"
|
|
? "USD"
|
|
: key === "sort_order"
|
|
? "0"
|
|
: ""),
|
|
),
|
|
]),
|
|
);
|
|
if (isSector) initial.is_active = Boolean(record?.is_active ?? true);
|
|
else {
|
|
initial.sector_id = String(record?.sector_id ?? "");
|
|
initial.maturity_level = String(record?.maturity_level ?? "");
|
|
initial.status = String(record?.status ?? "draft");
|
|
initial.is_featured = Boolean(record?.is_featured ?? false);
|
|
initial.territory_version = String(record?.territory_version ?? 0);
|
|
["province_id", "district_id", "admin_post_id", "locality_id"].forEach((field, index) => {
|
|
initial[field] = territoryPath[index]?.id ?? "";
|
|
});
|
|
}
|
|
const form = useForm<Values>(initial);
|
|
const [creationPath, setCreationPath] = useState<TerritoryOption[]>(territoryPath);
|
|
return (
|
|
<Management
|
|
title={`${record ? "Editar" : "Criar"} ${isSector ? "sector" : "oportunidade"}`}
|
|
>
|
|
{!isSector && record && (
|
|
<Link className="underline" href={`/management/opportunities/${record.id}/context`}>
|
|
Gerir clusters, corredores, projectos, educação e necessidades
|
|
</Link>
|
|
)}
|
|
<form
|
|
className="max-w-3xl space-y-5"
|
|
onSubmit={(event) => {
|
|
event.preventDefault();
|
|
if (record) form.put(`/management/${resource}/${record.id}`);
|
|
else form.post(`/management/${resource}`);
|
|
}}
|
|
>
|
|
<fieldset disabled={form.processing} className="space-y-5">
|
|
{!isSector && (
|
|
<div className="grid gap-4 md:grid-cols-2">
|
|
{(
|
|
[
|
|
"province_id",
|
|
"district_id",
|
|
"admin_post_id",
|
|
"locality_id",
|
|
] as const
|
|
).map((key, index) => (
|
|
<TerritoryLevel
|
|
key={`${key}-${creationPath[index - 1]?.id ?? ""}`}
|
|
index={index}
|
|
parent={creationPath[index - 1]?.id ?? ""}
|
|
selected={creationPath[index]}
|
|
endpoint="/management/territory-options"
|
|
fieldError={form.errors[key]}
|
|
onChange={(value) => {
|
|
const path = [
|
|
...creationPath.slice(0, index),
|
|
...(value ? [value] : []),
|
|
];
|
|
setCreationPath(path);
|
|
form.setData((data) => ({
|
|
...data,
|
|
province_id: path[0]?.id ?? "",
|
|
district_id: path[1]?.id ?? "",
|
|
admin_post_id: path[2]?.id ?? "",
|
|
locality_id: path[3]?.id ?? "",
|
|
}));
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
{Object.entries(fields).map(([key, label]) => (
|
|
<Field key={key} label={label} error={form.errors[key]}>
|
|
{[
|
|
"description",
|
|
"summary",
|
|
"short_description",
|
|
"expected_impact",
|
|
].includes(key) ? (
|
|
<textarea
|
|
className={control}
|
|
rows={key === "description" ? 6 : 3}
|
|
value={String(form.data[key])}
|
|
onChange={(e) => form.setData(key, e.target.value)}
|
|
/>
|
|
) : (
|
|
<input
|
|
className={control}
|
|
value={String(form.data[key])}
|
|
onChange={(e) => form.setData(key, e.target.value)}
|
|
/>
|
|
)}
|
|
</Field>
|
|
))}
|
|
{!isSector && (
|
|
<>
|
|
<Field label="Sector" error={form.errors.sector_id}>
|
|
<select
|
|
className={control}
|
|
value={String(form.data.sector_id)}
|
|
onChange={(e) => form.setData("sector_id", e.target.value)}
|
|
>
|
|
<option value="">Seleccione</option>
|
|
{sectors.map((sector) => (
|
|
<option key={sector.id} value={sector.id}>
|
|
{sector.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</Field>
|
|
<Field label="Maturidade" error={form.errors.maturity_level}>
|
|
<select
|
|
className={control}
|
|
value={String(form.data.maturity_level)}
|
|
onChange={(e) => form.setData("maturity_level", e.target.value)}
|
|
>
|
|
<option value="">Por indicar</option>
|
|
{maturities.map((label, index) => (
|
|
<option key={label} value={index + 1}>
|
|
{label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</Field>
|
|
<Field label="Estado" error={form.errors.status}>
|
|
<select
|
|
className={control}
|
|
value={String(form.data.status)}
|
|
onChange={(e) => form.setData("status", e.target.value)}
|
|
>
|
|
{["draft", "published", "archived"].map((key) => (
|
|
<option key={key} value={key}>
|
|
{states[key]}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</Field>
|
|
</>
|
|
)}
|
|
<Field
|
|
label={isSector ? "Activo" : "Em destaque"}
|
|
error={form.errors[isSector ? "is_active" : "is_featured"]}
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
checked={Boolean(form.data[isSector ? "is_active" : "is_featured"])}
|
|
onChange={(e) =>
|
|
form.setData(
|
|
isSector ? "is_active" : "is_featured",
|
|
e.target.checked,
|
|
)
|
|
}
|
|
/>
|
|
</Field>
|
|
<div className="flex gap-4">
|
|
<button className={button} disabled={form.processing}>
|
|
{form.processing ? "A guardar…" : "Guardar"}
|
|
</button>
|
|
<Link className="p-2 underline" href={`/management/${resource}`}>
|
|
Cancelar
|
|
</Link>
|
|
</div>
|
|
</fieldset>
|
|
</form>
|
|
{!isSector && record && <InvestmentCoordinates id={record.id} />}
|
|
</Management>
|
|
);
|
|
}
|