312 lines
15 KiB
TypeScript
312 lines
15 KiB
TypeScript
import ProfileNavigation from '@/components/profile-navigation';
|
|
import { Head, Link } from '@inertiajs/react';
|
|
import { dashboard } from '@/routes';
|
|
import type { InvestmentContext } from '@/pages/investor-path/index';
|
|
import type { OpportunityManagementAccess } from '@/types/opportunity-management';
|
|
|
|
type Summary = {
|
|
canCreateOpportunity?: boolean;
|
|
requestsByStatus: Record<string, number>;
|
|
publishedOpportunities: number;
|
|
draftOpportunities: number;
|
|
activeSectors: number;
|
|
recentRequests: {
|
|
id: number;
|
|
reference: string;
|
|
sector_interest: string;
|
|
status: string;
|
|
created_at: string;
|
|
}[];
|
|
};
|
|
const states: Record<string, string> = {
|
|
received: 'Recebidos',
|
|
in_review: 'Em análise',
|
|
contacted: 'Contactados',
|
|
closed: 'Encerrados',
|
|
};
|
|
const card =
|
|
'rounded-xl border p-5 transition-colors hover:bg-muted focus-visible:outline-2 focus-visible:outline-primary';
|
|
|
|
export default function Dashboard({
|
|
summary,
|
|
investorPath,
|
|
opportunityManagement,
|
|
}: {
|
|
summary: Summary | null;
|
|
opportunityManagement?: OpportunityManagementAccess | null;
|
|
investorPath?: {
|
|
contexts: InvestmentContext[];
|
|
current: {
|
|
id: string;
|
|
next: { code: string; title: string } | null;
|
|
pending: number;
|
|
documents: number;
|
|
results: number;
|
|
tasks: number;
|
|
} | null;
|
|
} | null;
|
|
}) {
|
|
return (
|
|
<main className="space-y-8 p-4 md:p-8">
|
|
<Head title="Painel" />
|
|
<h1 className="text-2xl font-semibold">Painel do portal</h1>
|
|
<ProfileNavigation />
|
|
{summary ? (
|
|
<>
|
|
{opportunityManagement && (
|
|
<section
|
|
aria-label="Gestão de Oportunidades"
|
|
className="space-y-4 rounded-xl border p-5"
|
|
>
|
|
<h2 className="text-lg font-semibold">
|
|
Gestão de Oportunidades
|
|
</h2>
|
|
<nav
|
|
aria-label="Ações de oportunidades"
|
|
className="flex flex-wrap gap-3"
|
|
>
|
|
<Link
|
|
className="rounded-lg border px-4 py-2 underline"
|
|
href={opportunityManagement.manage.href}
|
|
>
|
|
{opportunityManagement.manage.label}
|
|
</Link>
|
|
{opportunityManagement.installed ? (
|
|
<Link
|
|
className="bg-primary text-primary-foreground rounded-lg px-4 py-2"
|
|
href={opportunityManagement.create.href}
|
|
>
|
|
{opportunityManagement.create.label}
|
|
</Link>
|
|
) : (
|
|
<Link
|
|
className="underline"
|
|
href={
|
|
opportunityManagement.installationHref
|
|
}
|
|
>
|
|
Preparar instalação da gestão de
|
|
rascunhos
|
|
</Link>
|
|
)}
|
|
</nav>
|
|
<p className="text-muted-foreground text-sm">
|
|
Os rascunhos de oportunidades não publicam o
|
|
dossier privado do investidor. Publicação exige
|
|
uma ação própria.
|
|
</p>
|
|
</section>
|
|
)}
|
|
<section className="space-y-4">
|
|
<h2 className="text-lg font-semibold">
|
|
Manifestações de interesse
|
|
</h2>
|
|
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
|
{Object.entries(states).map(([status, label]) => (
|
|
<Link
|
|
key={status}
|
|
href={`/management/interests?status=${status}`}
|
|
className={card}
|
|
>
|
|
<p className="text-muted-foreground text-sm">
|
|
{label}
|
|
</p>
|
|
<p className="mt-2 text-3xl font-semibold">
|
|
{summary.requestsByStatus[status] ?? 0}
|
|
</p>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</section>
|
|
<section className="space-y-4">
|
|
<h2 className="text-lg font-semibold">
|
|
<Link href="/management/opportunities">
|
|
{opportunityManagement
|
|
? 'Indicadores de oportunidades'
|
|
: 'Gestão de Oportunidades'}
|
|
</Link>
|
|
</h2>
|
|
<div className="grid gap-4 sm:grid-cols-3">
|
|
{[
|
|
{
|
|
label: 'Oportunidades publicadas',
|
|
value: summary.publishedOpportunities,
|
|
href: '/management/opportunities?status=published',
|
|
},
|
|
{
|
|
label: 'Oportunidades em rascunho',
|
|
value: summary.draftOpportunities,
|
|
href: '/management/opportunities?status=draft',
|
|
},
|
|
{
|
|
label: 'Sectores activos',
|
|
value: summary.activeSectors,
|
|
href: '/management/sectors?status=active',
|
|
},
|
|
].map((item) => (
|
|
<Link
|
|
key={item.label}
|
|
href={item.href}
|
|
className={card}
|
|
>
|
|
<p className="text-muted-foreground text-sm">
|
|
{item.label}
|
|
</p>
|
|
<p className="mt-2 text-3xl font-semibold">
|
|
{item.value}
|
|
</p>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</section>
|
|
<section className="space-y-4">
|
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
<h2 className="text-lg font-semibold">
|
|
Pedidos mais recentes
|
|
</h2>
|
|
<Link
|
|
href="/management/interests"
|
|
className="underline"
|
|
>
|
|
Ver todos
|
|
</Link>
|
|
</div>
|
|
{summary.recentRequests.length ? (
|
|
<ul className="divide-y rounded-xl border">
|
|
{summary.recentRequests.map((item) => (
|
|
<li
|
|
key={item.id}
|
|
className="flex flex-wrap items-center justify-between gap-3 p-4"
|
|
>
|
|
<div className="min-w-0">
|
|
<Link
|
|
href={`/management/interests/${item.id}`}
|
|
className="font-medium break-all underline"
|
|
>
|
|
{item.reference}
|
|
</Link>
|
|
<p className="text-muted-foreground text-sm">
|
|
{item.sector_interest} ·{' '}
|
|
{states[item.status] ??
|
|
item.status}
|
|
</p>
|
|
</div>
|
|
<time
|
|
dateTime={item.created_at}
|
|
className="text-muted-foreground text-sm"
|
|
>
|
|
{new Intl.DateTimeFormat('pt-MZ', {
|
|
dateStyle: 'medium',
|
|
timeZone: 'Africa/Maputo',
|
|
}).format(
|
|
new Date(item.created_at),
|
|
)}
|
|
</time>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
) : (
|
|
<p className="text-muted-foreground rounded-xl border p-6">
|
|
Ainda não existem manifestações de interesse.
|
|
</p>
|
|
)}
|
|
</section>
|
|
<nav
|
|
aria-label="Acções rápidas"
|
|
className="flex flex-wrap gap-4"
|
|
>
|
|
{summary.canCreateOpportunity !== false && <Link
|
|
className="underline"
|
|
href="/management/opportunities/create"
|
|
>
|
|
{opportunityManagement?.create.label ??
|
|
'Criar oportunidade'}
|
|
</Link>}
|
|
<Link
|
|
className="underline"
|
|
href="/management/sectors/create"
|
|
>
|
|
Criar sector
|
|
</Link>
|
|
</nav>
|
|
</>
|
|
) : investorPath ? (
|
|
<section className="space-y-4 rounded-xl border p-6">
|
|
<h2 className="text-lg font-semibold">
|
|
Os meus investimentos
|
|
</h2>
|
|
{investorPath.current && (
|
|
<div className="space-y-2">
|
|
<p>
|
|
{investorPath.current.pending} etapas pendentes
|
|
· {investorPath.current.documents} documentos ·{' '}
|
|
{investorPath.current.results} resultados ·{' '}
|
|
{investorPath.current.tasks} tarefas
|
|
</p>
|
|
{investorPath.current.next && (
|
|
<Link
|
|
className="underline"
|
|
href={`/investimentos/${investorPath.current.id}#${investorPath.current.next.code}`}
|
|
>
|
|
Próxima acção:{' '}
|
|
{investorPath.current.next.code} ·{' '}
|
|
{investorPath.current.next.title}
|
|
</Link>
|
|
)}
|
|
</div>
|
|
)}
|
|
{investorPath.contexts.map((item, index) => (
|
|
<div className="rounded border p-4" key={item.id}>
|
|
<p>
|
|
{index === 0
|
|
? 'Investimento actualizado mais recentemente'
|
|
: 'Outro investimento'}
|
|
</p>
|
|
<Link
|
|
className="font-semibold underline"
|
|
href={`/investimentos/${item.id}`}
|
|
>
|
|
{item.title}
|
|
</Link>
|
|
<p>
|
|
Percurso, pendências, documentos, resultados e
|
|
próxima acção no dossier.
|
|
</p>
|
|
</div>
|
|
))}
|
|
{!investorPath.contexts.length && (
|
|
<p>
|
|
Comece pela sua ideia e prepare as etapas ao seu
|
|
ritmo.
|
|
</p>
|
|
)}
|
|
<Link className="underline" href="/investimentos">
|
|
Preparar ou escolher um investimento
|
|
</Link>
|
|
</section>
|
|
) : (
|
|
<section className="max-w-2xl space-y-4 rounded-xl border p-6">
|
|
<h2 className="text-lg font-semibold">
|
|
Configure o acesso quando estiver pronto
|
|
</h2>
|
|
<p>
|
|
A gestão está disponível para contas autorizadas. Pode
|
|
consultar os passos de configuração ou continuar a
|
|
explorar as oportunidades públicas.
|
|
</p>
|
|
<Link
|
|
href="/acesso-gestao"
|
|
className="bg-primary text-primary-foreground inline-block rounded-lg px-4 py-2"
|
|
>
|
|
Consultar o meu acesso
|
|
</Link>
|
|
</section>
|
|
)}
|
|
<Link href="/" className="inline-block underline">
|
|
Voltar ao portal público
|
|
</Link>
|
|
</main>
|
|
);
|
|
}
|
|
Dashboard.layout = { breadcrumbs: [{ title: 'Painel', href: dashboard() }] };
|