Files
Investidor-V2/resources/js/components/dashboard-work-queue.tsx
T

168 lines
7.5 KiB
TypeScript

import { Link } from '@inertiajs/react';
import { states as interestStates } from '@/pages/management/shared';
import { taskStates } from '@/pages/crm/types';
type WorkItem = {
id: string;
title: string;
reference: string | null;
status: string;
due_at: string | null;
overdue: boolean;
href: string;
};
type WorkGroup = {
total: number;
overdue: number;
items: WorkItem[];
page: number;
last_page: number;
previous: string | null;
next: string | null;
};
export type DashboardQueue = {
as_of: string;
desk_states: Record<string, string>;
groups: Partial<Record<'interests' | 'desk' | 'crm_tasks', WorkGroup>>;
};
const groups = {
interests: 'Manifestações atribuídas a mim',
desk: 'Pedidos do Balcão atribuídos a mim',
crm_tasks: 'Tarefas do CRM atribuídas a mim',
} as const;
const date = (value: string) =>
new Intl.DateTimeFormat('pt-MZ', {
dateStyle: 'medium',
timeStyle: 'short',
timeZone: 'Africa/Maputo',
}).format(new Date(value));
export default function DashboardWorkQueue({
queue,
}: {
queue: DashboardQueue | null | undefined;
}) {
if (!queue) return null;
return (
<section aria-labelledby="my-work-title" className="space-y-4">
<h2 id="my-work-title" className="text-xl font-semibold">
O meu acompanhamento
</h2>
<p className="text-muted-foreground text-sm">
Pedidos e tarefas atribuídos a si, por prazo. Actualizado em{' '}
<time dateTime={queue.as_of}>{date(queue.as_of)} (Maputo)</time>
.
</p>
<div className="grid gap-4 lg:grid-cols-3">
{Object.entries(groups).map(([kind, title]) => {
const group = queue.groups[kind as keyof typeof groups];
if (!group) return null;
const states =
kind === 'interests'
? interestStates
: kind === 'desk'
? queue.desk_states
: taskStates;
return (
<section
key={kind}
aria-labelledby={`work-${kind}`}
className="space-y-3 rounded-xl border p-4"
>
<h3 id={`work-${kind}`} className="font-semibold">
{title}
</h3>
<p>
{group.total} por acompanhar · {group.overdue}{' '}
com prazo ultrapassado
</p>
{group.items.length ? (
<ul className="space-y-3">
{group.items.map((item) => (
<li
key={item.id}
className="space-y-1 border-t pt-3"
>
<Link
className="focus-visible:outline-primary font-medium underline focus-visible:outline-2"
href={item.href}
>
{item.title}
</Link>
{item.reference && (
<p className="text-sm">
{item.reference}
</p>
)}
<p className="text-sm">
{states[item.status] ??
item.status}
</p>
<p className="text-sm">
{item.due_at ? (
<>
<time
dateTime={
item.due_at
}
>
{date(item.due_at)}{' '}
(Maputo)
</time>
{item.overdue && (
<strong>
{' '}
· Prazo
ultrapassado
</strong>
)}
</>
) : (
'Sem prazo definido'
)}
</p>
</li>
))}
</ul>
) : (
<p>
Não tem registos activos atribuídos neste
acompanhamento.
</p>
)}
{group.last_page > 1 && (
<nav
aria-label={`Páginas: ${title}`}
className="flex flex-wrap items-center gap-3"
>
{group.previous && (
<Link
href={group.previous}
preserveScroll
className="underline"
>
Anterior
</Link>
)}
<span>
Página {group.page} de {group.last_page}
</span>
{group.next && (
<Link
href={group.next}
preserveScroll
className="underline"
>
Seguinte
</Link>
)}
</nav>
)}
</section>
);
})}
</div>
</section>
);
}