Files
2025-12-11 22:47:45 -03:00

226 lines
7.1 KiB
TypeScript

import type { Route } from './+types/route'
import {
AlertCircleIcon,
CheckCircle2Icon,
ClockIcon,
CalendarIcon,
UserIcon,
BanIcon,
PlusIcon
} from 'lucide-react'
import { Link } from 'react-router'
import { Suspense } from 'react'
import {
Empty,
EmptyContent,
EmptyDescription,
EmptyHeader,
EmptyMedia,
EmptyTitle
} from '@repo/ui/components/ui/empty'
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle
} from '@repo/ui/components/ui/card'
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator
} from '@repo/ui/components/ui/breadcrumb'
import {
Alert,
AlertDescription,
AlertTitle
} from '@repo/ui/components/ui/alert'
import { request as req } from '@repo/util/request'
import { Skeleton } from '@repo/ui/components/skeleton'
import { Await } from 'react-router'
import { Abbr } from '@repo/ui/components/abbr'
import { Button } from '@repo/ui/components/ui/button'
export function meta({}: Route.MetaArgs) {
return [{ title: 'Relatório de matrículas' }]
}
export async function loader({ context, request, params }: Route.LoaderArgs) {
const { orgid, id } = params
const submission = req({
url: `/orgs/${orgid}/enrollments/${id}/submitted`,
context,
request
}).then((r) => r.json())
return {
data: submission
}
}
export default function Route({ loaderData: { data } }: Route.ComponentProps) {
return (
<Suspense fallback={<Skeleton />}>
<div className="space-y-2.5">
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink asChild>
<Link to="../enrollments">Matrículas</Link>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbLink asChild>
<Link to="../enrollments/add">Adicionar matrículas</Link>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Relatório de matrículas</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
<Await resolve={data} errorElement={<NotFound />}>
{({ enrolled, scheduled, sk, created_by }) => {
const succeed = enrolled.filter(
({ status }) => status === 'success'
)
const failed = enrolled.filter(({ status }) => status === 'fail')
return (
<div className="lg:max-w-4xl mx-auto space-y-2.5">
<Card>
<CardHeader>
<CardTitle className="text-2xl">
Relatório de matrículas
</CardTitle>
<CardDescription>
Resumo detalhado do processamento das matrículas enviadas.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4 text-base">
<>
{succeed?.length > 0 && (
<Alert className="text-green-500 *:data-[slot=alert-description]:text-green-500/90">
<CheckCircle2Icon />
<AlertTitle>
Matrículas adicionadas com sucesso.
</AlertTitle>
<AlertDescription>
<ul className="list-decimal list-inside">
{succeed.map(({ output }, index) => (
<li className="space-x-1" key={index}>
<Abbr>{output.user.name}</Abbr>
<span>&mdash;</span>
<Abbr>{output.course.name}</Abbr>
</li>
))}
</ul>
</AlertDescription>
</Alert>
)}
{failed?.length > 0 && (
<Alert variant="destructive">
<AlertCircleIcon />
<AlertTitle>Matrículas não processadas.</AlertTitle>
<AlertDescription>
<ul className="list-decimal list-inside">
{failed.map(({ input_record }, index) => (
<li className="space-x-1" key={index}>
<Abbr>{input_record.user.name}</Abbr>
<span>&mdash;</span>
<Abbr>{input_record.course.name}</Abbr>
</li>
))}
</ul>
</AlertDescription>
</Alert>
)}
{scheduled?.length && (
<Alert>
<ClockIcon />
<AlertTitle>
Matrículas agendadas. Serão processadas na data
definida.
</AlertTitle>
<AlertDescription>
<ul className="list-decimal list-inside">
<li>...</li>
<li>...</li>
</ul>
</AlertDescription>
</Alert>
)}
</>
</CardContent>
<CardFooter>
<ul
className="grid lg:grid-cols-2 gap-x-2.5 text-muted-foreground text-sm
*:flex *:gap-1 *:items-center"
>
<li>
<CalendarIcon className="size-3.5" />
{formatted.format(new Date(sk))}
</li>
{created_by && (
<li>
<UserIcon className="size-3.5" /> {created_by.name}
</li>
)}
</ul>
</CardFooter>
</Card>
</div>
)
}}
</Await>
</div>
</Suspense>
)
}
const formatted = new Intl.DateTimeFormat('pt-BR', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
function NotFound() {
return (
<>
<Empty className="border border-dashed">
<EmptyHeader>
<EmptyMedia variant="icon">
<BanIcon />
</EmptyMedia>
<EmptyTitle>Nenhum relatório aqui</EmptyTitle>
<EmptyDescription>
Matricule colaboradores de forma rápida e acompanhe seu progresso.
</EmptyDescription>
</EmptyHeader>
<EmptyContent>
<Button asChild>
<Link to="../enrollments/add">
<PlusIcon /> Matricular
</Link>
</Button>
</EmptyContent>
</Empty>
</>
)
}