124 lines
5.1 KiB
TypeScript
124 lines
5.1 KiB
TypeScript
import { Head, Link, router } from '@inertiajs/react';
|
|
import { Bell, CheckCheck } from 'lucide-react';
|
|
import { PageHeading } from '@/components/page-heading';
|
|
import { Button } from '@/components/ui/button';
|
|
import { cn } from '@/lib/utils';
|
|
import type { Paginated } from '@/types';
|
|
|
|
type PortalNotification = {
|
|
id: string;
|
|
data: {
|
|
message: string;
|
|
url?: string;
|
|
reference?: string;
|
|
};
|
|
read_at: string | null;
|
|
created_at: string;
|
|
};
|
|
|
|
export default function NotificationsIndex({
|
|
notifications,
|
|
}: {
|
|
notifications: Paginated<PortalNotification>;
|
|
}) {
|
|
function openNotification(notification: PortalNotification) {
|
|
router.post(`/notificacoes/${notification.id}/ler`);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Head title="Notificações" />
|
|
<main className="mx-auto flex w-full max-w-4xl flex-1 flex-col gap-7 p-5 sm:p-8">
|
|
<PageHeading
|
|
eyebrow="Caixa interna"
|
|
title="Notificações"
|
|
description="Atualizações sobre requisições, decisões e pedidos de esclarecimento."
|
|
action={
|
|
<Button
|
|
variant="outline"
|
|
onClick={() =>
|
|
router.post('/notificacoes/ler-todas')
|
|
}
|
|
>
|
|
<CheckCheck /> Marcar todas como lidas
|
|
</Button>
|
|
}
|
|
/>
|
|
|
|
<section className="overflow-hidden rounded-xl border bg-card">
|
|
{notifications.data.map((notification) => (
|
|
<button
|
|
type="button"
|
|
key={notification.id}
|
|
onClick={() => openNotification(notification)}
|
|
className={cn(
|
|
'flex w-full gap-4 border-b p-5 text-left last:border-0 hover:bg-slate-50 dark:hover:bg-slate-900',
|
|
notification.read_at === null && 'bg-primary/5',
|
|
)}
|
|
>
|
|
<span className="mt-0.5 rounded-full bg-primary/10 p-2 text-primary">
|
|
<Bell className="size-4" />
|
|
</span>
|
|
<div className="min-w-0 flex-1">
|
|
<p className="text-sm font-medium">
|
|
{notification.data.message}
|
|
</p>
|
|
<p className="mt-1 text-xs text-muted-foreground">
|
|
{new Intl.DateTimeFormat('pt-PT', {
|
|
dateStyle: 'medium',
|
|
timeStyle: 'short',
|
|
}).format(
|
|
new Date(notification.created_at),
|
|
)}
|
|
</p>
|
|
</div>
|
|
{notification.read_at === null && (
|
|
<span className="mt-2 size-2 rounded-full bg-primary" />
|
|
)}
|
|
</button>
|
|
))}
|
|
{notifications.data.length === 0 && (
|
|
<div className="py-16 text-center text-sm text-muted-foreground">
|
|
Não existem notificações.
|
|
</div>
|
|
)}
|
|
</section>
|
|
|
|
{notifications.last_page > 1 && (
|
|
<nav className="flex justify-center gap-2">
|
|
{notifications.links.map((link, index) => (
|
|
<Button
|
|
key={index}
|
|
asChild={Boolean(link.url)}
|
|
disabled={!link.url}
|
|
variant={link.active ? 'default' : 'outline'}
|
|
size="sm"
|
|
>
|
|
{link.url ? (
|
|
<Link
|
|
href={link.url}
|
|
preserveScroll
|
|
dangerouslySetInnerHTML={{
|
|
__html: link.label,
|
|
}}
|
|
/>
|
|
) : (
|
|
<span
|
|
dangerouslySetInnerHTML={{
|
|
__html: link.label,
|
|
}}
|
|
/>
|
|
)}
|
|
</Button>
|
|
))}
|
|
</nav>
|
|
)}
|
|
</main>
|
|
</>
|
|
);
|
|
}
|
|
|
|
NotificationsIndex.layout = {
|
|
breadcrumbs: [{ title: 'Notificações', href: '/notificacoes' }],
|
|
};
|