organizacao da tela inicial
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
import { HomepageSectors } from '@/components/public-sector-cards';
|
||||
import {
|
||||
HOME_SECTION,
|
||||
HOME_SECTION_BAND,
|
||||
HOME_SECTION_HEADING,
|
||||
} from '@/lib/portal-presentation';
|
||||
import InvestmentContextCards from '@/components/investment-context-cards';
|
||||
import InvestmentSteps from '@/components/investment-steps';
|
||||
import PortalHero from '@/components/portal-hero';
|
||||
import SearchState from '@/components/search-state';
|
||||
import { useInterestDraft } from '@/hooks/use-interest-draft';
|
||||
@@ -8,7 +16,6 @@ import { ProvinceSelect } from '@/components/investment-territory';
|
||||
import { MATURITY_LEVELS } from '@/lib/investment-maturity';
|
||||
import CountrySelect, { type Country } from '@/components/country-select';
|
||||
import type { Dispatch, FormEvent, ReactNode, SetStateAction } from 'react';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
import { usePortalQuery } from '@/hooks/use-portal-query';
|
||||
import {
|
||||
PortalApiError,
|
||||
@@ -31,28 +38,20 @@ import type {
|
||||
Sector,
|
||||
} from '@/lib/portal-api';
|
||||
import {
|
||||
Leaf,
|
||||
Zap,
|
||||
Hotel,
|
||||
Truck,
|
||||
Waves,
|
||||
Banknote,
|
||||
ArrowLeft,
|
||||
MapPin,
|
||||
CheckCircle2,
|
||||
LogIn,
|
||||
FileText,
|
||||
Scale,
|
||||
Handshake,
|
||||
Calculator,
|
||||
ArrowRight,
|
||||
} from 'lucide-react';
|
||||
|
||||
type View = 'home' | 'opportunities' | 'detail' | 'interest';
|
||||
type OpportunityAction = (opportunity: Opportunity) => void;
|
||||
const SectorContext = createContext<Sector[]>([]);
|
||||
const SECTOR_ICONS: Record<string, LucideIcon> = {
|
||||
agronegocio: Leaf,
|
||||
energia: Zap,
|
||||
turismo: Hotel,
|
||||
infraestruturas: Truck,
|
||||
'economia-azul': Waves,
|
||||
'mercado-financeiro': Banknote,
|
||||
};
|
||||
|
||||
function QueryStatus({
|
||||
loading,
|
||||
@@ -67,7 +66,7 @@ function QueryStatus({
|
||||
return (
|
||||
<p
|
||||
role="status"
|
||||
className="mx-auto max-w-6xl px-5 py-8 text-slate-600"
|
||||
className="mx-auto max-w-6xl px-5 py-5 text-slate-600 sm:py-6"
|
||||
>
|
||||
A carregar dados...
|
||||
</p>
|
||||
@@ -76,7 +75,7 @@ function QueryStatus({
|
||||
return (
|
||||
<div
|
||||
role="alert"
|
||||
className="mx-auto my-6 max-w-6xl rounded-xl border border-red-200 bg-red-50 p-5 text-red-800"
|
||||
className="mx-auto my-5 max-w-6xl rounded-xl border border-red-200 bg-red-50 p-5 text-red-800"
|
||||
>
|
||||
<p>{error}</p>
|
||||
<button
|
||||
@@ -136,65 +135,6 @@ function TopBar({
|
||||
);
|
||||
}
|
||||
|
||||
function SectorGrid({
|
||||
onSelectSector,
|
||||
}: {
|
||||
onSelectSector: (slug: string) => void;
|
||||
}) {
|
||||
const sectors = useContext(SectorContext);
|
||||
return (
|
||||
<section className="mx-auto max-w-6xl px-5 py-16">
|
||||
<h2 className="text-2xl font-black text-slate-900">
|
||||
Sectores estratégicos
|
||||
</h2>
|
||||
<p className="mt-2 max-w-2xl text-slate-600">
|
||||
Explore as áreas prioritárias do programa de investimento
|
||||
nacional.
|
||||
</p>
|
||||
|
||||
<div className="mt-8 grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{sectors.map((sector) => {
|
||||
const Icon = SECTOR_ICONS[sector.slug] ?? Banknote;
|
||||
const count = sector.opportunitiesCount;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={sector.slug}
|
||||
onClick={() => onSelectSector(sector.slug)}
|
||||
className="group rounded-2xl border border-slate-200 bg-white p-6 text-left shadow-sm transition hover:-translate-y-0.5 hover:border-emerald-300 hover:shadow-md"
|
||||
>
|
||||
<div className="flex h-11 w-11 items-center justify-center rounded-xl bg-emerald-50 text-emerald-700">
|
||||
<Icon size={22} />
|
||||
</div>
|
||||
<h3 className="mt-4 font-bold text-slate-900">
|
||||
{sector.name}
|
||||
</h3>
|
||||
<p className="mt-2 text-sm leading-relaxed text-slate-600">
|
||||
{sector.shortDescription}
|
||||
</p>
|
||||
{sector.specializations?.map((child) => (
|
||||
<span
|
||||
key={child.id}
|
||||
className="mt-3 block text-sm font-semibold text-emerald-800"
|
||||
>
|
||||
Especialização:{' '}
|
||||
{child.name.replace(
|
||||
'Mercado Financeiro — ',
|
||||
'',
|
||||
)}
|
||||
</span>
|
||||
))}
|
||||
<p className="mt-4 text-sm font-semibold text-emerald-700 group-hover:underline">
|
||||
{count} oportunidade{count === 1 ? '' : 's'} →
|
||||
</p>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function OpportunityCard({
|
||||
opportunity,
|
||||
onView,
|
||||
@@ -284,7 +224,12 @@ function OpportunitiesView({
|
||||
|
||||
return (
|
||||
<section className="mx-auto max-w-6xl px-5 py-12">
|
||||
<SearchState loading={query.loading} error={query.error} total={total} retry={query.retry} />
|
||||
<SearchState
|
||||
loading={query.loading}
|
||||
error={query.error}
|
||||
total={total}
|
||||
retry={query.retry}
|
||||
/>
|
||||
<h1 className="text-3xl font-black text-slate-900">
|
||||
Oportunidades de investimento
|
||||
</h1>
|
||||
@@ -597,19 +542,22 @@ function InterestForm({
|
||||
const pending = useRef(false);
|
||||
const submission = useRef<{ body: string; key: string } | null>(null);
|
||||
const [submitError, setSubmitError] = useState<string | null>(null);
|
||||
const draft = useInterestDraft<InterestFormData>('opportunity:' + (preselected?.reference ?? 'general'), {
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
investorType: '',
|
||||
country: '',
|
||||
declared_origin_country_code: '',
|
||||
sectorInterest: preselected?.sectorName ?? '',
|
||||
opportunityReference: preselected ? preselected.reference : '',
|
||||
investmentRange: '',
|
||||
message: '',
|
||||
privacyConsent: false,
|
||||
});
|
||||
const draft = useInterestDraft<InterestFormData>(
|
||||
'opportunity:' + (preselected?.reference ?? 'general'),
|
||||
{
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
investorType: '',
|
||||
country: '',
|
||||
declared_origin_country_code: '',
|
||||
sectorInterest: preselected?.sectorName ?? '',
|
||||
opportunityReference: preselected ? preselected.reference : '',
|
||||
investmentRange: '',
|
||||
message: '',
|
||||
privacyConsent: false,
|
||||
},
|
||||
);
|
||||
const { form, setForm } = draft;
|
||||
const [errors, setErrors] = useState<FormErrors>({});
|
||||
const [result, setResult] = useState<{ reference: string } | null>(null);
|
||||
@@ -671,7 +619,10 @@ function InterestForm({
|
||||
setResult({ reference: response.data.reference });
|
||||
onSubmitted();
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof PortalApiError && error.status === 401) { draft.authenticate(); return; }
|
||||
if (error instanceof PortalApiError && error.status === 401) {
|
||||
draft.authenticate();
|
||||
return;
|
||||
}
|
||||
if (error instanceof PortalApiError) {
|
||||
setErrors(fieldErrors(error));
|
||||
setSubmitError(error.message);
|
||||
@@ -697,7 +648,13 @@ function InterestForm({
|
||||
A sua referência é <strong>{result.reference}</strong>. A
|
||||
equipa do portal irá analisar o pedido e poderá solicitar
|
||||
informação adicional.
|
||||
</p><a href="/investidor/manifestacoes" className="mt-4 inline-block underline">Acompanhar manifestações</a>
|
||||
</p>
|
||||
<a
|
||||
href="/investidor/manifestacoes"
|
||||
className="mt-4 inline-block underline"
|
||||
>
|
||||
Acompanhar manifestações
|
||||
</a>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -945,16 +902,20 @@ function Field({
|
||||
|
||||
function Footer() {
|
||||
return (
|
||||
<footer className="border-t border-slate-200 bg-slate-950 py-10 text-slate-400">
|
||||
<div className="mx-auto max-w-6xl px-5 text-sm">
|
||||
<p className="font-bold text-white">INVESTE Moçambique</p>
|
||||
<p className="mt-2 max-w-md">
|
||||
Portal do Investidor — conheça as oportunidades de
|
||||
investimento em Moçambique e manifeste o seu interesse.{' '}
|
||||
<a href="/acesso-gestao" className="underline">
|
||||
Acesso à gestão
|
||||
</a>
|
||||
</p>
|
||||
<footer
|
||||
aria-label="Portal do Investidor"
|
||||
className="border-t border-slate-200 bg-white"
|
||||
>
|
||||
<div className="mx-auto flex max-w-6xl justify-start px-5 py-2">
|
||||
<a
|
||||
href="/acesso-gestao"
|
||||
aria-label="Acesso à gestão"
|
||||
title="Acesso à gestão"
|
||||
className="inline-flex min-h-11 items-center justify-center gap-2 rounded-lg px-3 text-sm font-medium text-[#ed1c24] transition-colors hover:bg-red-50 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-[#ed1c24]"
|
||||
>
|
||||
<LogIn className="size-5" aria-hidden="true" />
|
||||
<span>Acesso à gestão</span>
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
@@ -1015,10 +976,6 @@ export default function PortalInvestidorApp({
|
||||
router.get('/oportunidades', { q: heroQuery.trim() });
|
||||
}
|
||||
|
||||
function handleSelectSector(slug: string) {
|
||||
router.get('/sectores/' + encodeURIComponent(slug));
|
||||
}
|
||||
|
||||
function handleView(opportunity: Opportunity) {
|
||||
router.get('/oportunidades/' + encodeURIComponent(opportunity.slug));
|
||||
}
|
||||
@@ -1032,7 +989,9 @@ export default function PortalInvestidorApp({
|
||||
return (
|
||||
<SectorContext.Provider value={sectors}>
|
||||
<div className="min-h-screen bg-slate-50 font-sans text-slate-900">
|
||||
{view !== 'home' && <TopBar onNavigate={navigate} requestCount={requestCount} />}
|
||||
{view !== 'home' && (
|
||||
<TopBar onNavigate={navigate} requestCount={requestCount} />
|
||||
)}
|
||||
<PortalMenu />
|
||||
<main>
|
||||
{view === 'home' && (
|
||||
@@ -1044,109 +1003,38 @@ export default function PortalInvestidorApp({
|
||||
stats={home.data?.data.stats}
|
||||
onNavigate={navigate}
|
||||
/>
|
||||
<section className="mx-auto max-w-6xl space-y-5 px-5 py-10">
|
||||
<h2 className="text-2xl font-bold">
|
||||
Encontre o contexto do seu investimento
|
||||
</h2>
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{[
|
||||
[
|
||||
'/clusters',
|
||||
'Clusters',
|
||||
'Cadeias de valor e lacunas produtivas.',
|
||||
],
|
||||
[
|
||||
'/corredores',
|
||||
'Corredores',
|
||||
'Territórios, logística e conexões.',
|
||||
],
|
||||
[
|
||||
'/projectos-ancora',
|
||||
'Projectos âncora',
|
||||
'Projectos estruturantes e relações.',
|
||||
],
|
||||
[
|
||||
'/educacao',
|
||||
'Educação',
|
||||
'Investimentos em competências e formação.',
|
||||
],
|
||||
[
|
||||
'/documentos',
|
||||
'Documentos e Formulários',
|
||||
'Legislação, procedimentos, formulários e materiais de apoio.',
|
||||
],
|
||||
].map(([href, title, text]) => (
|
||||
<a
|
||||
key={href}
|
||||
href={href}
|
||||
className="rounded-xl border bg-white p-5"
|
||||
>
|
||||
<h3 className="text-lg font-bold">
|
||||
{title}
|
||||
</h3>
|
||||
<p>{text}</p>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<InvestmentContextCards />
|
||||
<QueryStatus
|
||||
loading={home.loading}
|
||||
error={home.error}
|
||||
retry={home.retry}
|
||||
/>
|
||||
{home.data && (
|
||||
<SectorGrid
|
||||
onSelectSector={handleSelectSector}
|
||||
<HomepageSectors
|
||||
sectors={sectors.map((sector) => ({
|
||||
...sector,
|
||||
description: sector.shortDescription,
|
||||
}))}
|
||||
/>
|
||||
)}
|
||||
|
||||
<section className="mx-auto max-w-6xl space-y-5 px-5 py-12">
|
||||
<h2 className="text-2xl font-black">
|
||||
Como Investir
|
||||
</h2>
|
||||
<div className="grid gap-5 md:grid-cols-3">
|
||||
{[
|
||||
[
|
||||
'1. Explorar',
|
||||
'Identifique o sector, território e oportunidade.',
|
||||
'/oportunidades',
|
||||
],
|
||||
[
|
||||
'2. Preparar',
|
||||
'Conheça o percurso e organize os documentos.',
|
||||
'/como-investir',
|
||||
],
|
||||
[
|
||||
'3. Acompanhar',
|
||||
'Envie o pedido e acompanhe o protocolo.',
|
||||
'/balcao-virtual',
|
||||
],
|
||||
].map(([title, text, href]) => (
|
||||
<a
|
||||
key={href}
|
||||
href={href}
|
||||
className="rounded-xl border bg-white p-6"
|
||||
>
|
||||
<h3 className="font-bold">
|
||||
{title}
|
||||
</h3>
|
||||
<p>{text}</p>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
<InvestmentSteps />
|
||||
|
||||
<section className="border-t border-slate-200 bg-white py-16">
|
||||
<section className={HOME_SECTION_BAND}>
|
||||
<div className="mx-auto max-w-6xl px-5">
|
||||
<div className="mb-8 flex items-end justify-between">
|
||||
<h2 className="text-2xl font-black text-slate-900">
|
||||
<div className="mb-5 flex items-end justify-between">
|
||||
<h2 className={HOME_SECTION_HEADING}>
|
||||
Oportunidades em destaque
|
||||
</h2>
|
||||
<button
|
||||
onClick={goToOpportunities}
|
||||
className="text-sm font-bold text-emerald-700 hover:underline"
|
||||
className="inline-flex items-center gap-1 text-sm font-bold text-[#ed1c24] hover:underline"
|
||||
>
|
||||
Ver todas →
|
||||
<span>Ver todas</span>
|
||||
<ArrowRight
|
||||
className="h-4 w-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
{home.data && featured.length === 0 && (
|
||||
@@ -1167,77 +1055,154 @@ export default function PortalInvestidorApp({
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="mx-auto grid max-w-6xl gap-6 px-5 py-12 md:grid-cols-2">
|
||||
<div className="space-y-4 rounded-xl border bg-white p-6">
|
||||
<h2 className="text-2xl font-black">
|
||||
Documentos essenciais
|
||||
</h2>
|
||||
{essentialResources.length > 0 && (
|
||||
<ul className="space-y-2">
|
||||
{essentialResources.map(
|
||||
(resource) => (
|
||||
<li key={resource.id}>
|
||||
<a
|
||||
className="underline"
|
||||
href={`/documentos/${resource.id}/download`}
|
||||
>
|
||||
{resource.title}
|
||||
</a>
|
||||
</li>
|
||||
),
|
||||
)}
|
||||
</ul>
|
||||
)}
|
||||
<p>
|
||||
Consulte legislação, referências e
|
||||
formulários publicados para preparar o
|
||||
seu investimento.
|
||||
</p>
|
||||
<a
|
||||
className="font-semibold text-emerald-800 underline"
|
||||
href="/documentos"
|
||||
>
|
||||
Abrir biblioteca documental
|
||||
</a>
|
||||
<p>
|
||||
<section
|
||||
className={`${HOME_SECTION} grid gap-4 sm:gap-5 md:grid-cols-2`}
|
||||
>
|
||||
<article className="group flex h-full flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm transition-shadow hover:shadow-md">
|
||||
<div className="flex items-start gap-4 p-6">
|
||||
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-[#ed1c24]/10 text-[#ed1c24]">
|
||||
<FileText
|
||||
className="h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<h2 className="text-xl font-bold text-slate-900 sm:text-2xl">
|
||||
Documentos essenciais
|
||||
</h2>
|
||||
<p className="text-sm leading-relaxed text-slate-600 sm:text-base">
|
||||
Consulte legislação, referências
|
||||
e formulários publicados para
|
||||
preparar o seu investimento.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-auto flex flex-col gap-2 px-6 pb-6">
|
||||
<a
|
||||
className="underline"
|
||||
className="inline-flex items-center justify-between gap-2 rounded-xl border border-slate-200 px-4 py-3 text-sm font-semibold text-slate-900 transition-colors hover:border-[#ed1c24] hover:bg-red-50/40 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#ed1c24]"
|
||||
href="/documentos"
|
||||
>
|
||||
Abrir biblioteca documental
|
||||
<ArrowRight
|
||||
className="h-4 w-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
className="inline-flex items-center justify-between gap-2 rounded-xl border border-slate-200 px-4 py-3 text-sm font-semibold text-slate-900 transition-colors hover:border-[#ed1c24] hover:bg-red-50/40 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#ed1c24]"
|
||||
href="/legislacao"
|
||||
>
|
||||
Legislação e Incentivos
|
||||
<span className="flex items-center gap-2">
|
||||
<Scale
|
||||
className="h-4 w-4 text-[#ed1c24]"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Legislação e Incentivos
|
||||
</span>
|
||||
<ArrowRight
|
||||
className="h-4 w-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-4 rounded-xl bg-emerald-900 p-6 text-white">
|
||||
<h2 className="text-2xl font-black">
|
||||
Apoio ao Investidor
|
||||
</h2>
|
||||
<p>
|
||||
Peça orientação à equipa, proponha uma
|
||||
parceria ou acompanhe o seu pedido.
|
||||
</p>
|
||||
<a
|
||||
className="font-semibold underline"
|
||||
href="/apoio"
|
||||
>
|
||||
Contactar Apoio ao Investidor
|
||||
</a>
|
||||
<p>
|
||||
{essentialResources.length > 0 && (
|
||||
<div className="mt-2 rounded-xl bg-slate-50/80 p-4">
|
||||
<p className="mb-2 text-xs font-semibold tracking-wide text-slate-500 uppercase">
|
||||
Destaques
|
||||
</p>
|
||||
<ul className="space-y-1.5">
|
||||
{essentialResources
|
||||
.slice(0, 3)
|
||||
.map((resource) => (
|
||||
<li
|
||||
key={
|
||||
resource.id
|
||||
}
|
||||
>
|
||||
<a
|
||||
className="inline-flex items-center gap-2 text-sm text-slate-700 underline-offset-4 hover:text-slate-900 hover:underline"
|
||||
href={`/documentos/${resource.id}/download`}
|
||||
>
|
||||
<FileText
|
||||
className="h-4 w-4 text-slate-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{
|
||||
resource.title
|
||||
}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</article>
|
||||
<article className="group flex h-full flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm transition-shadow hover:shadow-md">
|
||||
<div className="flex items-start gap-4 p-6">
|
||||
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-[#ed1c24]/10 text-[#ed1c24]">
|
||||
<Handshake
|
||||
className="h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<h2 className="text-xl font-bold text-slate-900 sm:text-2xl">
|
||||
Apoio ao Investidor
|
||||
</h2>
|
||||
<p className="text-sm leading-relaxed text-slate-600 sm:text-base">
|
||||
Peça orientação à equipa,
|
||||
proponha uma parceria ou
|
||||
acompanhe o seu pedido.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-auto flex flex-col gap-2 px-6 pb-6">
|
||||
<a
|
||||
className="underline"
|
||||
href="/parcerias"
|
||||
className="inline-flex items-center justify-between gap-2 rounded-xl bg-[#ed1c24] px-4 py-3 text-sm font-semibold text-white transition-colors hover:bg-[#c9141b] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#ed1c24]"
|
||||
href="/apoio"
|
||||
>
|
||||
Bolsa de Parcerias
|
||||
</a>{' '}
|
||||
·{' '}
|
||||
<a
|
||||
className="underline"
|
||||
href="/simulador-fiscal"
|
||||
>
|
||||
Simulador Fiscal
|
||||
Contactar Apoio ao Investidor
|
||||
<ArrowRight
|
||||
className="h-4 w-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<a
|
||||
className="inline-flex items-center justify-between gap-2 rounded-xl border border-slate-200 px-4 py-3 text-sm font-semibold text-slate-900 transition-colors hover:border-[#ed1c24] hover:bg-red-50/40 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#ed1c24]"
|
||||
href="/parcerias"
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<Handshake
|
||||
className="h-4 w-4 text-[#ed1c24]"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Bolsa de Parcerias
|
||||
</span>
|
||||
<ArrowRight
|
||||
className="h-4 w-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
className="inline-flex items-center justify-between gap-2 rounded-xl border border-slate-200 px-4 py-3 text-sm font-semibold text-slate-900 transition-colors hover:border-[#ed1c24] hover:bg-red-50/40 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#ed1c24]"
|
||||
href="/simulador-fiscal"
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<Calculator
|
||||
className="h-4 w-4 text-[#ed1c24]"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Simulador Fiscal
|
||||
</span>
|
||||
<ArrowRight
|
||||
className="h-4 w-4"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { InvestmentContextGrid } from '@/components/investment-context-cards';
|
||||
import { PORTAL_MORE_LINK } from '@/lib/portal-presentation';
|
||||
import { Head } from '@inertiajs/react';
|
||||
|
||||
export default function InvestmentContexts() {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50 text-slate-900">
|
||||
<Head title="Contextos de investimento" />
|
||||
<main className="mx-auto max-w-6xl min-w-0 space-y-6 px-5 py-10">
|
||||
<h1 className="text-3xl font-bold text-[#ed1c24]">
|
||||
Encontre o contexto do seu investimento
|
||||
</h1>
|
||||
<p className="text-slate-600">
|
||||
Explore os contextos e recursos de apoio ao seu
|
||||
investimento.
|
||||
</p>
|
||||
<InvestmentContextGrid />
|
||||
<div className="flex justify-center">
|
||||
<a href="/" className={PORTAL_MORE_LINK}>
|
||||
Voltar ao início
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { PublicSectorCards } from '@/components/public-sector-cards';
|
||||
import { PORTAL_MORE_LINK } from '@/lib/portal-presentation';
|
||||
import {
|
||||
PublicDocumentContent,
|
||||
type PublicDocument,
|
||||
@@ -18,7 +20,7 @@ const input =
|
||||
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 Prioritários',
|
||||
sectors: 'Sectores de investimento',
|
||||
sector: 'Sector prioritário',
|
||||
procedures: 'Como Investir em Moçambique',
|
||||
legislation: 'Legislação e Incentivos',
|
||||
@@ -73,8 +75,9 @@ type Props = {
|
||||
sectors?: {
|
||||
name: string;
|
||||
slug: string;
|
||||
short_description: string;
|
||||
short_description: string | null;
|
||||
opportunities_count: number;
|
||||
url?: string;
|
||||
specializations?: { id: number; name: string; url: string }[];
|
||||
}[];
|
||||
sector?: {
|
||||
@@ -489,7 +492,15 @@ export default function Services(p: Props) {
|
||||
<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="text-3xl font-bold">{title}</h1>
|
||||
<h1
|
||||
className={
|
||||
p.section === 'sectors'
|
||||
? 'text-3xl font-bold text-[#ed1c24]'
|
||||
: 'text-3xl font-bold'
|
||||
}
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
{flash?.success && (
|
||||
<p
|
||||
role="status"
|
||||
@@ -499,38 +510,33 @@ export default function Services(p: Props) {
|
||||
</p>
|
||||
)}
|
||||
{p.section === 'sectors' && (
|
||||
<div className="grid gap-5 md:grid-cols-3">
|
||||
{p.sectors?.map((s) => (
|
||||
<article
|
||||
className="rounded-xl border bg-white p-6"
|
||||
key={s.slug}
|
||||
>
|
||||
<h2 className="text-xl font-bold">
|
||||
<Link href={`/sectores/${s.slug}`}>
|
||||
{s.name}
|
||||
</Link>
|
||||
</h2>
|
||||
<p className="my-3">{s.short_description}</p>
|
||||
{s.specializations?.map((child) => (
|
||||
<p key={child.id}>
|
||||
<Link
|
||||
className="font-semibold underline"
|
||||
href={child.url}
|
||||
>
|
||||
{child.name}
|
||||
</Link>
|
||||
</p>
|
||||
))}
|
||||
<p>
|
||||
{s.opportunities_count} oportunidades
|
||||
publicadas
|
||||
</p>
|
||||
</article>
|
||||
))}
|
||||
<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>
|
||||
<div className="flex justify-center">
|
||||
<a href="/" className={PORTAL_MORE_LINK}>
|
||||
Voltar ao início
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
{p.section === 'sector' && p.sector && (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user