Files
Investidor-V2/resources/js/pages/portal/services.tsx
T

1305 lines
61 KiB
TypeScript

import { PublicSectorCards } from '@/components/public-sector-cards';
import { PORTAL_MORE_LINK } from '@/lib/portal-presentation';
import {
PublicDocumentContent,
type PublicDocument,
} from '@/components/public-document-content';
import { Head, Link, useForm, usePage } from '@inertiajs/react';
import { useState } from 'react';
import { PortalMenu } from '@/layouts/portal-navigation';
import { newSubmissionKey } from '@/lib/portal-api';
import { ProvinceSelect } from '@/components/investment-territory';
import { SimulationForm } from '@/components/simulation-form';
import { InvestmentRoadmap } from '@/components/investment-roadmap';
import { JourneyDocumentResources } from '@/components/journey-document-resources';
import SectorEntry, { type SectorEntryData } from '@/components/sector-entry';
import CountrySelect, { type Country } from '@/components/country-select';
const input =
'box-border w-full min-w-0 max-w-full rounded-lg border border-slate-300 bg-white p-3 text-slate-900';
const button =
'inline-block rounded-lg bg-emerald-800 px-5 py-3 font-semibold text-white disabled:opacity-50';
const titles: Record<string, string> = {
sectors: 'Sectores de investimento',
sector: 'Sector prioritário',
procedures: 'Como Investir em Moçambique',
legislation: 'Legislação e Incentivos',
documents: 'Documentos e Formulários',
support: 'Apoio ao Investidor',
partnerships: 'Bolsa de Parcerias',
simulator: 'Simulador Fiscal',
investor: 'Formulário de investidor',
company: 'Formulário de empresa',
desk: 'Balcão Virtual',
};
const documentTypeLabels: Record<string, string> = {
legislation: 'Legislação',
procedure: 'Procedimento oficial',
official_form: 'Formulário oficial',
digital_adaptation: 'Adaptação digital de formulário',
guidance_template: 'Modelo orientativo',
guide: 'Guia',
study: 'Estudo',
reference_data: 'Dados de referência',
unknown: 'Tipo por classificar',
};
const legalStatusLabels: Record<string, string> = {
not_applicable: 'Não aplicável',
in_force: 'Vigência indicada; confirme o período',
historical: 'Histórico',
proposed: 'Proposta; não é norma vigente',
repealed: 'Revogada',
amended: 'Alterada; consulte alterações posteriores',
unknown: 'Por confirmar',
};
type Resource = PublicDocument & { document_type: string };
type Partnership = {
id: number;
title: string;
sector: string;
province: string;
offer: string;
seeking: string;
};
type Page<T> = {
data: T[];
prev_page_url: string | null;
next_page_url: string | null;
total: number;
};
type Props = {
sectorEntry?: SectorEntryData;
countries?: Country[];
section: string;
filters?: Record<string, string | string[]>;
sectors?: {
name: string;
slug: string;
short_description: string | null;
opportunities_count: number;
url?: string;
specializations?: { id: number; name: string; url: string }[];
}[];
sector?: {
id: number;
name: string;
slug: string;
description: string;
short_description: string;
};
opportunities?: {
title: string;
slug: string;
province: string;
summary: string;
}[];
resources?: Page<Resource>;
categories?: string[];
topics?: string[];
documentTypes?: string[];
languages?: string[];
issuers?: string[];
legalStatuses?: string[];
territories?: {
id: string;
name: string;
location_type: string;
parent_id: string | null;
}[];
activities?: string[];
stages?: string[];
modules?: string[];
partnerships?: Page<Partnership>;
contacts?: {
email?: string;
phone?: string;
address?: string;
hours?: string;
};
requests?: {
reference: string;
status: string;
created_at: string;
metadata: { kind: string };
}[];
myPartnerships?: { id: number; title: string; status: string }[];
};
const states: Record<string, string> = {
received: 'Recebido',
in_review: 'Em análise',
contacted: 'Contactado',
closed: 'Encerrado',
pending: 'A aguardar publicação',
published: 'Publicado',
withdrawn: 'Retirado',
};
function Pagination({ page }: { page: Page<unknown> }) {
return (
<nav aria-label="Paginação" className="flex gap-6 py-5">
{page.prev_page_url && (
<Link href={page.prev_page_url}>Anterior</Link>
)}
<span>{page.total} registos</span>
{page.next_page_url && (
<Link href={page.next_page_url}>Seguinte</Link>
)}
</nav>
);
}
function RequestForm({
kind,
partnershipId,
}: {
kind: string;
partnershipId?: number;
}) {
const { auth } = usePage<{
auth: { user: { name: string; email: string } | null };
}>().props;
const countries =
usePage<{ countries?: Country[] }>().props.countries ?? [];
const form = useForm({
kind,
fullName: auth.user?.name ?? '',
email: auth.user?.email ?? '',
phone: '',
country: '',
declared_origin_country_code: '',
company: '',
sectorInterest: '',
message: '',
privacyConsent: false,
submission_key: '',
partnership_id: partnershipId ?? null,
});
const [sent, setSent] = useState(false);
return (
<form
className="space-y-4 rounded-xl border bg-white p-6"
onSubmit={(e) => {
e.preventDefault();
const key = form.data.submission_key || newSubmissionKey();
form.setData('submission_key', key);
form.transform((data) => ({ ...data, submission_key: key }));
form.post('/apoio', {
preserveScroll: true,
onSuccess: () => setSent(true),
});
}}
>
<h2 className="text-xl font-bold">
{kind === 'company'
? 'Dados da empresa e do representante'
: 'Enviar pedido'}
</h2>
{!auth.user && (
<p className="text-sm">
Pode enviar sem conta. Para acompanhar no Balcão Virtual,{' '}
<Link href="/login" className="underline">
entre antes de enviar
</Link>
.
</p>
)}
{(
[
'fullName',
'email',
'phone',
'country',
'sectorInterest',
...(kind === 'company' ? ['company'] : []),
] as const
).map((key) => {
const field = key as
| 'fullName'
| 'email'
| 'phone'
| 'country'
| 'sectorInterest'
| 'company';
const labels = {
fullName: 'Nome do representante',
email: 'Email',
phone: 'Telefone (opcional)',
country: 'País',
sectorInterest: 'Sector ou assunto',
company: 'Nome da empresa',
};
return (
<label key={field} className="block space-y-1">
<span>{labels[field]}</span>
{field === 'country' ? (
<CountrySelect
countries={countries}
label="País do requerente"
value={form.data.country}
onChange={(value) =>
form.setData('country', value)
}
required
allowLegacyValue
/>
) : (
<input
className={input}
type={field === 'email' ? 'email' : 'text'}
required={field !== 'phone'}
maxLength={field === 'phone' ? 30 : 150}
value={form.data[field]}
onChange={(e) =>
form.setData(field, e.target.value)
}
/>
)}
</label>
);
})}
<label className="block">
País de origem declarado desta solicitação
<CountrySelect
countries={countries}
label="País de origem declarado desta solicitação"
value={form.data.declared_origin_country_code}
onChange={(value) =>
form.setData('declared_origin_country_code', value)
}
/>
</label>
<p className="text-sm">
Origem opcional e independente do país acima. Sem declaração,
permanece não informado nas estatísticas.
</p>
<label className="block">
Descreva o pedido
<textarea
className={input}
required
minLength={20}
maxLength={2000}
rows={5}
value={form.data.message}
onChange={(e) => form.setData('message', e.target.value)}
/>
</label>
<label className="flex items-start gap-3">
<input
type="checkbox"
required
checked={form.data.privacyConsent}
onChange={(e) =>
form.setData('privacyConsent', e.target.checked)
}
/>
<span>
Autorizo o tratamento destes dados para responder e
acompanhar este pedido. Os dados ficam acessíveis à equipa
de atendimento; não serão publicados na bolsa.
</span>
</label>
{Object.entries(form.errors).map(([key, error]) => (
<p key={key} role="alert" className="text-red-700">
{error}
</p>
))}
{sent ? (
<p role="status">
Pedido enviado. Guarde o protocolo apresentado acima.{' '}
<button
type="button"
className="underline"
onClick={() => {
setSent(false);
form.setData('submission_key', '');
form.setData('message', '');
}}
>
Enviar novo pedido
</button>
</p>
) : (
<button className={button} disabled={form.processing}>
Enviar e obter protocolo
</button>
)}
</form>
);
}
function PartnershipForm() {
const form = useForm({
title: '',
sector: '',
province: '',
offer: '',
seeking: '',
publication_consent: false,
});
return (
<form
className="space-y-4 rounded-xl border p-6"
onSubmit={(e) => {
e.preventDefault();
form.post('/parcerias', {
preserveScroll: true,
onSuccess: () => form.reset(),
});
}}
>
<h2 className="text-xl font-bold">Propor parceria</h2>
<p>
Descreva apenas informação destinada a publicação. Não inclua
contactos pessoais ou documentos confidenciais.
</p>
{(['title', 'sector', 'province', 'offer', 'seeking'] as const).map(
(key) => (
<label className="block" key={key}>
{
{
title: 'Título',
sector: 'Sector',
province: 'Província',
offer: 'O que oferece',
seeking: 'Parceiro ou capacidade que procura',
}[key]
}
{key === 'province' ? (
<ProvinceSelect
value={form.data.province}
onChange={(province) =>
form.setData('province', province)
}
className={input}
required
placeholder="Seleccione uma província"
label="Província"
/>
) : (
<textarea
className={input}
required
minLength={
key === 'offer' || key === 'seeking'
? 20
: 1
}
maxLength={
key === 'offer' || key === 'seeking'
? 2000
: 100
}
value={form.data[key]}
onChange={(e) =>
form.setData(key, e.target.value)
}
/>
)}
</label>
),
)}
<label className="flex gap-2">
<input
type="checkbox"
required
checked={form.data.publication_consent}
onChange={(e) =>
form.setData('publication_consent', e.target.checked)
}
/>
Autorizo a publicação dos dados desta proposta após revisão.
</label>
{Object.entries(form.errors).map(([key, error]) => (
<p role="alert" key={key}>
{error}
</p>
))}
<button disabled={form.processing} className={button}>
Submeter proposta
</button>
</form>
);
}
function Simulator() {
const page = usePage<{ auth: { user: { id: number } | null } }>();
const query = new URL(page.url, 'https://investmoz.invalid').searchParams;
const scope = `${page.props.auth.user?.id ?? 'anonymous'}:${query.get('context') ?? ''}:${query.get('draft') ?? ''}`;
return (
<div className="space-y-5">
<p>
<Link href="/como-investir" className="underline">
Voltar ao roteiro de investimento
</Link>{' '}
para rever mercado, localização, recursos, formalização e
execução.
</p>
<SimulationForm key={scope} />
</div>
);
}
export default function Services(p: Props) {
const { flash, auth } = usePage<{
flash?: { success?: string };
auth: { user: unknown };
}>().props;
const currentUrl = usePage<{ url: string }>().url;
const filter = useForm({
q: typeof p.filters?.q === 'string' ? p.filters.q : '',
category:
typeof p.filters?.category === 'string' ? p.filters.category : '',
topic: typeof p.filters?.topic === 'string' ? p.filters.topic : '',
sector: typeof p.filters?.sector === 'string' ? p.filters.sector : '',
province:
typeof p.filters?.province === 'string' ? p.filters.province : '',
document_type:
typeof p.filters?.document_type === 'string'
? p.filters.document_type
: '',
language:
typeof p.filters?.language === 'string' ? p.filters.language : '',
issuer: typeof p.filters?.issuer === 'string' ? p.filters.issuer : '',
legal_status:
typeof p.filters?.legal_status === 'string'
? p.filters.legal_status
: '',
source_confirmation:
typeof p.filters?.source_confirmation === 'string'
? p.filters.source_confirmation
: '',
territory_id:
typeof p.filters?.territory_id === 'string'
? p.filters.territory_id
: '',
activity: Array.isArray(p.filters?.activity) ? p.filters.activity : [],
stage: Array.isArray(p.filters?.stage) ? p.filters.stage : [],
module: Array.isArray(p.filters?.module) ? p.filters.module : [],
});
const [selectedPartner, setSelectedPartner] = useState<number>();
const title = p.sector?.name ?? titles[p.section];
const formTypes = [
'official_form',
'digital_adaptation',
'guidance_template',
];
const firstFormIndex =
p.resources?.data.findIndex((resource) =>
formTypes.includes(resource.document_type),
) ?? -1;
return (
<div className="min-h-screen bg-slate-50 text-slate-900">
<Head title={title} />
<PortalMenu />
<main className="mx-auto max-w-6xl min-w-0 space-y-8 overflow-x-clip px-5 py-10">
<h1
className={
p.section === 'sectors'
? 'text-3xl font-bold text-[#ed1c24]'
: 'text-3xl font-bold'
}
>
{title}
</h1>
{flash?.success && (
<p
role="status"
className="rounded border border-emerald-700 bg-emerald-50 p-4"
>
{flash.success}
</p>
)}
{p.section === 'sectors' && (
<section
aria-label="Todos os sectores de investimento"
className="space-y-6"
>
<p className="text-slate-600">
Explore o potencial estratégico de cada sector em
Moçambique.
</p>
<PublicSectorCards
sectors={(p.sectors ?? []).map((sector) => ({
slug: sector.slug,
name: sector.name,
description: sector.short_description,
opportunitiesCount: sector.opportunities_count,
specializations: sector.specializations,
url: sector.url,
}))}
/>
{!p.sectors?.length && (
<p>Não existem sectores publicados.</p>
)}
<div className="flex justify-center">
<a href="/" className={PORTAL_MORE_LINK}>
Voltar ao início
</a>
</div>
</section>
)}
{p.section === 'sector' && p.sector && (
<>
<p className="whitespace-pre-line">
{p.sector.description ||
p.sector.short_description ||
'Descrição sectorial em preparação.'}
</p>
{!p.sectorEntry && (
<Link
className={button}
href={
'/investimentos/iniciar?source=sector:' +
p.sector.id
}
>
Iniciar ou continuar uma ideia neste sector
</Link>
)}
{p.sectorEntry && (
<SectorEntry
data={{
...p.sectorEntry,
start_href: p.sectorEntry.start_href
? '/investimentos/iniciar?source=sector:' +
p.sector.id
: null,
}}
/>
)}
<h2 className="text-xl font-bold">
Oportunidades no sector
</h2>
{p.opportunities?.map((o) => (
<article
key={o.slug}
className="rounded border bg-white p-5"
>
<Link
className="text-lg font-bold underline"
href={`/oportunidades/${o.slug}`}
>
{o.title}
</Link>
<p>{o.province}</p>
<p>{o.summary}</p>
<ul aria-label="Localizações associadas">
{p.sectorEntry?.locations
.filter(
(l) =>
l.href ===
`/oportunidades/${o.slug}`,
)
.filter(
(l, i, rows) =>
rows.findIndex(
(other) =>
JSON.stringify(
other.location,
) ===
JSON.stringify(
l.location,
),
) === i,
)
.map((l, i) => (
<li key={i}>
{typeof l.location.name ===
'string'
? l.location.name
: 'Consultar localização e fonte na oportunidade'}
</li>
))}
</ul>
</article>
))}
{!p.opportunities?.length && (
<p>Sem oportunidades publicadas neste sector.</p>
)}
<Link
className={button}
href={`/oportunidades?sector=${p.sector.slug}`}
>
Pesquisar todas as oportunidades do sector
</Link>
<p>
<Link href="/como-investir" className="underline">
Preparar o investimento
</Link>{' '}
·{' '}
<Link href="/apoio" className="underline">
Pedir apoio sectorial
</Link>
</p>
</>
)}
{['procedures', 'documents', 'legislation'].includes(
p.section,
) && <JourneyDocumentResources stages={p.stages} />}
{p.section === 'procedures' && <InvestmentRoadmap />}
{(p.section === 'documents' || p.section === 'legislation') && (
<>
<form
className="grid min-w-0 gap-3 sm:grid-cols-2 lg:grid-cols-4"
onSubmit={(e) => {
e.preventDefault();
filter.get(
p.section === 'documents'
? '/documentos'
: '/legislacao',
);
}}
>
<label className="block min-w-0">
Pesquisar
<input
className={input}
value={filter.data.q}
onChange={(e) =>
filter.setData('q', e.target.value)
}
/>
</label>
{!!(p.topics?.length || filter.data.topic) && (
<label className="block min-w-0">
Tema
<select
className={input}
value={filter.data.topic}
onChange={(e) =>
filter.setData(
'topic',
e.target.value,
)
}
>
<option value="">Todos</option>
{p.topics?.map((topic) => (
<option key={topic}>{topic}</option>
))}
</select>
</label>
)}
{!!(
p.categories?.length || filter.data.category
) && (
<label className="block min-w-0">
Categoria
<select
className={input}
value={filter.data.category}
onChange={(e) =>
filter.setData(
'category',
e.target.value,
)
}
>
<option value="">Todas</option>
{p.categories?.map((category) => (
<option
key={category}
value={category}
>
{documentTypeLabels[category] ??
category}
</option>
))}
</select>
</label>
)}
{!!(
p.documentTypes?.length ||
filter.data.document_type
) && (
<label className="block min-w-0">
Tipo documental
<select
className={input}
value={filter.data.document_type}
onChange={(e) =>
filter.setData(
'document_type',
e.target.value,
)
}
>
<option value="">Todos</option>
{p.documentTypes?.map((type) => (
<option key={type} value={type}>
{documentTypeLabels[type] ??
'Documento'}
</option>
))}
</select>
</label>
)}
{!!p.activities?.length && (
<fieldset className="min-w-0 rounded border p-3">
<legend className="px-1">
Actividade ou CAE
</legend>
<div className="max-h-36 space-y-2 overflow-y-auto">
{p.activities.map((activity) => (
<label
key={activity}
className="flex items-start gap-2 break-words"
>
<input
type="checkbox"
checked={filter.data.activity.includes(
activity,
)}
onChange={(e) =>
filter.setData(
'activity',
e.target.checked
? [
...filter
.data
.activity,
activity,
]
: filter.data.activity.filter(
(value) =>
value !==
activity,
),
)
}
/>
<span>{activity}</span>
</label>
))}
</div>
</fieldset>
)}
{!!p.stages?.length && (
<fieldset className="min-w-0 rounded border p-3">
<legend className="px-1">
Etapa do projecto
</legend>
<div className="max-h-36 space-y-2 overflow-y-auto">
{p.stages.map((stage) => (
<label
key={stage}
className="flex items-start gap-2 break-words"
>
<input
type="checkbox"
checked={filter.data.stage.includes(
stage,
)}
onChange={(e) =>
filter.setData(
'stage',
e.target.checked
? [
...filter
.data
.stage,
stage,
]
: filter.data.stage.filter(
(value) =>
value !==
stage,
),
)
}
/>
<span>{stage}</span>
</label>
))}
</div>
</fieldset>
)}
{!!p.modules?.length && (
<fieldset className="min-w-0 rounded border p-3">
<legend className="px-1">Módulo</legend>
<div className="max-h-36 space-y-2 overflow-y-auto">
{p.modules.map((module) => (
<label
key={module}
className="flex items-start gap-2 break-words"
>
<input
type="checkbox"
checked={filter.data.module.includes(
module,
)}
onChange={(e) =>
filter.setData(
'module',
e.target.checked
? [
...filter
.data
.module,
module,
]
: filter.data.module.filter(
(value) =>
value !==
module,
),
)
}
/>
<span>
{{
simulator:
'Simulador fiscal',
procedures:
'Como investir',
investment:
'Oportunidades',
zones: 'Zonas e parques',
education: 'Educação',
business: 'Negócios',
land: 'Terra e DUAT',
}[module] ??
'Documentos de apoio'}
</span>
</label>
))}
</div>
</fieldset>
)}
{!!(p.issuers?.length || filter.data.issuer) && (
<label className="block min-w-0">
Entidade emitente
<select
className={input}
value={filter.data.issuer}
onChange={(e) =>
filter.setData(
'issuer',
e.target.value,
)
}
>
<option value="">Todas</option>
{p.issuers?.map((issuer) => (
<option key={issuer}>
{issuer}
</option>
))}
</select>
</label>
)}
{!!(
p.languages?.length || filter.data.language
) && (
<label className="block min-w-0">
Idioma
<select
className={input}
value={filter.data.language}
onChange={(e) =>
filter.setData(
'language',
e.target.value,
)
}
>
<option value="">Todos</option>
{p.languages?.map((language) => (
<option key={language}>
{language}
</option>
))}
</select>
</label>
)}
{!!(
p.legalStatuses?.length ||
filter.data.legal_status
) && (
<label className="block min-w-0">
Situação jurídica indicada
<select
className={input}
value={filter.data.legal_status}
onChange={(e) =>
filter.setData(
'legal_status',
e.target.value,
)
}
>
<option value="">Todas</option>
{p.legalStatuses?.map((status) => (
<option key={status} value={status}>
{legalStatusLabels[status] ??
'Por confirmar'}
</option>
))}
</select>
</label>
)}
<label className="block min-w-0">
Confirmação da fonte
<select
className={input}
value={filter.data.source_confirmation}
onChange={(e) =>
filter.setData(
'source_confirmation',
e.target.value,
)
}
>
<option value="">Todas</option>
<option value="official_confirmed">
Fonte oficial confirmada
</option>
<option value="identified">
Origem identificada
</option>
<option value="unconfirmed">
Por confirmar
</option>
</select>
</label>
<label>
Âmbito territorial
<select
className={input}
value={filter.data.territory_id}
onChange={(e) =>
filter.setData(
'territory_id',
e.target.value,
)
}
>
<option value="">Todos</option>
{p.territories?.map((territory) => (
<option
key={territory.id}
value={territory.id}
>
{territory.name}
</option>
))}
</select>
<span className="text-xs text-slate-600">
Documentos nacionais aplicáveis permanecem
incluídos.
</span>
</label>
<button className={button}>Pesquisar</button>
<Link
className="self-center underline"
href={
p.section === 'documents'
? '/documentos'
: '/legislacao'
}
>
Limpar filtros
</Link>
</form>
{!!p.topics?.length && (
<nav
aria-label="Navegar por assunto"
className="flex flex-wrap gap-2"
>
<span className="font-semibold">Assuntos:</span>
{p.topics.map((topic) => (
<Link
key={topic}
className="rounded-full border bg-white px-3 py-1 underline"
href={`${p.section === 'documents' ? '/documentos' : '/legislacao'}?${new URLSearchParams({ topic }).toString()}`}
>
{topic}
</Link>
))}
</nav>
)}
<p className="text-sm">
Os documentos nacionais aplicáveis continuam
incluídos numa pesquisa territorial e são
identificados como nacionais. A localização da
entidade emitente não define o âmbito de aplicação.
</p>
{p.resources?.data.map((r, index) => (
<div key={r.id} className="contents">
{index === 0 && (
<h2 className="pt-4 text-xl font-bold">
{firstFormIndex === 0
? 'Formulários e modelos'
: 'Documentos de consulta'}
</h2>
)}
{index === firstFormIndex &&
firstFormIndex > 0 && (
<h2 className="pt-4 text-xl font-bold">
Formulários e modelos
</h2>
)}
<article
key={r.id}
className="min-w-0 space-y-2 overflow-hidden rounded-xl border bg-white p-5"
>
<PublicDocumentContent
resource={r}
back={currentUrl}
/>
</article>
</div>
))}
{!p.resources?.data.length && (
<p>
Não existem documentos publicados para esta
pesquisa.{' '}
<Link href="/apoio" className="underline">
Solicitar documento ao apoio
</Link>
.
</p>
)}
{p.resources && <Pagination page={p.resources} />}
<div className="flex flex-wrap gap-4">
<Link
className={button}
href="/formularios/investidor"
>
Formulário de investidor
</Link>
<Link
className={button}
href="/formularios/empresa"
>
Formulário de empresa
</Link>
</div>
</>
)}
{['support', 'investor', 'company'].includes(p.section) && (
<RequestForm
key={p.section}
kind={p.section === 'support' ? 'support' : p.section}
/>
)}
{p.section === 'support' && (
<>
<section className="space-y-3">
<h2 className="text-xl font-bold">Contactos</h2>
{p.contacts?.email && (
<p>
Email:{' '}
<a
className="underline"
href={`mailto:${p.contacts.email}`}
>
{p.contacts.email}
</a>
</p>
)}
{p.contacts?.phone && (
<p>Telefone: {p.contacts.phone}</p>
)}
{p.contacts?.address && <p>{p.contacts.address}</p>}
{p.contacts?.hours && (
<p>Horário: {p.contacts.hours}</p>
)}
{!p.contacts?.email && !p.contacts?.phone && (
<p>
Utilize o formulário para contactar a
equipa. Os contactos directos ainda não
estão publicados.
</p>
)}
</section>
<section className="space-y-3">
<h2 className="text-xl font-bold">
Perguntas frequentes
</h2>
{[
[
'Como acompanho um pedido?',
'Entre antes de enviar o pedido. O protocolo e o estado ficam disponíveis no Balcão Virtual. Para pedidos enviados sem conta, conserve o protocolo e indique-o nos contactos seguintes.',
],
[
'Manifestar interesse garante financiamento ou aprovação?',
'O envio regista o interesse para análise e contacto da equipa. Não representa aprovação do investimento nem compromisso de financiamento.',
],
[
'Como publico uma parceria?',
'Entre com email verificado, preencha a proposta na Bolsa de Parcerias e autorize a publicação. A gestão revê a proposta antes de a tornar pública.',
],
[
'Onde obtenho documentos e formulários?',
'Consulte Documentos e Formulários. Se não encontrar o documento, indique o título ou assunto num pedido de apoio.',
],
].map(([q, a]) => (
<details
key={q}
className="rounded border bg-white p-4"
>
<summary className="cursor-pointer font-semibold">
{q}
</summary>
<p className="mt-3">{a}</p>
</details>
))}
</section>
</>
)}
{p.section === 'simulator' && <Simulator />}
{p.section === 'partnerships' && (
<>
<p>
Encontre capacidades e parceiros. O contacto é
intermediado pelo apoio; os dados privados dos
proponentes não são publicados.
</p>
<form
className="grid gap-3 md:grid-cols-4"
onSubmit={(e) => {
e.preventDefault();
filter.get('/parcerias');
}}
>
{(['q', 'sector', 'province'] as const).map(
(key) => (
<label key={key}>
{
{
q: 'Pesquisar',
sector: 'Sector',
province: 'Província',
}[key]
}
{key === 'province' ? (
<ProvinceSelect
value={filter.data.province}
onChange={(province) =>
filter.setData(
'province',
province,
)
}
className={input}
placeholder="Todas as províncias"
label="Filtrar por província"
/>
) : (
<input
className={input}
value={filter.data[key]}
onChange={(e) =>
filter.setData(
key,
e.target.value,
)
}
/>
)}
</label>
),
)}
<button className={button}>Filtrar</button>
</form>
{p.partnerships?.data.map((item) => (
<article
key={item.id}
className="space-y-3 rounded-xl border bg-white p-6"
>
<h2 className="text-xl font-bold">
{item.title}
</h2>
<p>
{item.sector} · {item.province}
</p>
<h3 className="font-bold">Oferece</h3>
<p className="whitespace-pre-line">
{item.offer}
</p>
<h3 className="font-bold">Procura</h3>
<p className="whitespace-pre-line">
{item.seeking}
</p>
<button
className={button}
onClick={() => setSelectedPartner(item.id)}
>
Pedir contacto sobre esta parceria
</button>
</article>
))}
{!p.partnerships?.data.length && (
<p>
Ainda não existem propostas publicadas para esta
pesquisa.
</p>
)}
{p.partnerships && <Pagination page={p.partnerships} />}
{selectedPartner && (
<RequestForm
key={selectedPartner}
kind="partnership"
partnershipId={selectedPartner}
/>
)}{' '}
{auth.user ? (
<PartnershipForm />
) : (
<Link className={button} href="/login">
Entrar para propor parceria
</Link>
)}
</>
)}
{p.section === 'desk' && (
<>
<p>
Acompanhe os pedidos submetidos com esta conta. Os
contactos da equipa continuam pelos dados fornecidos
no pedido.
</p>
<Link className={button} href="/apoio">
Novo pedido
</Link>
<h2 className="text-xl font-bold">Os meus pedidos</h2>
{p.requests?.map((r) => (
<article
key={r.reference}
className="rounded border bg-white p-5"
>
<strong>{r.reference}</strong>
<p>{states[r.status] ?? r.status}</p>
<p>
{new Date(r.created_at).toLocaleDateString(
'pt-MZ',
)}
</p>
</article>
))}
{!p.requests?.length && (
<p>
Ainda não existem pedidos de apoio enviados com
esta conta.
</p>
)}
<h2 className="text-xl font-bold">
As minhas propostas de parceria
</h2>
{p.myPartnerships?.map((r) => (
<p key={r.id}>
{r.title} — {states[r.status] ?? r.status}
</p>
))}
<Link href="/investidor" className="underline">
Abrir fichas e manifestações de interesse da
carteira
</Link>
</>
)}
</main>
</div>
);
}