Files
saladeaula.digital/apps/admin.saladeaula.digital/app/routes/_.$orgid.scheduled/route.tsx

199 lines
6.3 KiB
TypeScript

import type { Route } from './+types/route'
import {
BanIcon,
CalendarIcon,
EllipsisIcon,
PlusIcon,
UserIcon
} from 'lucide-react'
import { DateTime } from 'luxon'
import { Fragment, Suspense } from 'react'
import { Await } from 'react-router'
import { request as req } from '@repo/util/request'
import { Button } from '@repo/ui/components/ui/button'
import {
Empty,
EmptyContent,
EmptyDescription,
EmptyHeader,
EmptyMedia,
EmptyTitle
} from '@repo/ui/components/ui/empty'
import { Skeleton } from '@repo/ui/components/skeleton'
import { Card, CardContent } from '@repo/ui/components/ui/card'
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemGroup,
ItemMedia,
ItemSeparator,
ItemTitle
} from '@repo/ui/components/ui/item'
import { Link } from 'react-router'
import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar'
import { initials } from '@repo/ui/lib/utils'
import { Abbr } from '@repo/ui/components/abbr'
export function meta({}: Route.MetaArgs) {
return [{ title: 'Matrículas agendadas' }]
}
export async function loader({ context, request, params }: Route.LoaderArgs) {
const scheduled = req({
url: `/orgs/${params.orgid}/enrollments/scheduled`,
context,
request
}).then((r) => r.json())
return {
scheduled
}
}
export default function Route({
loaderData: { scheduled }
}: Route.ComponentProps) {
return (
<Suspense fallback={<Skeleton />}>
<div className="space-y-0.5 mb-8">
<h1 className="text-2xl font-bold tracking-tight">
Matrículas agendadas
</h1>
<p className="text-muted-foreground">
Acompanhe todas as matrículas agendadas, cancele quando quiser ou
matricule imediatamente.
</p>
</div>
<Await resolve={scheduled}>
{({ items }) => {
const scheduled = grouping(items)
if (scheduled.length === 0) {
return (
<Empty className="border border-dashed">
<EmptyHeader>
<EmptyMedia variant="icon">
<BanIcon />
</EmptyMedia>
<EmptyTitle>Nenhum agendamento ainda</EmptyTitle>
<EmptyDescription>
Agende a matrícula dos seus colaboradores de forma rápida e
organizada.
</EmptyDescription>
</EmptyHeader>
<EmptyContent>
<Button asChild>
<Link to="../enrollments/add">
<PlusIcon /> Agendar
</Link>
</Button>
</EmptyContent>
</Empty>
)
}
return (
<div className="space-y-5 lg:max-w-4xl mx-auto">
{scheduled.map(([run_at, items], index) => (
<div className="grid lg:grid-cols-5 gap-2.5" key={index}>
<div>
{DateTime.fromISO(run_at)
.setLocale('pt-BR')
.toFormat('cccc, dd LLL yyyy')}
</div>
<Card className="col-span-4">
<CardContent>
<ItemGroup>
{items.map(
(
{ user, course, created_by, scheduled_at },
index
) => (
<Fragment key={index}>
<Item>
<ItemMedia className="hidden lg:block">
<Avatar className="size-10 ">
<AvatarFallback className="border">
{initials(user.name)}
</AvatarFallback>
</Avatar>
</ItemMedia>
<ItemContent>
<ItemTitle>{course.name}</ItemTitle>
<ItemDescription className="flex flex-col">
<Abbr>{user.name}</Abbr>
<Abbr>{user.email}</Abbr>
</ItemDescription>
<div className="mt-1">
<ul
className="lg:flex gap-2.5 text-muted-foreground text-sm
*:flex *:gap-1 *:items-center"
>
<li>
<CalendarIcon className="size-3.5" />{' '}
{datetime.format(
new Date(scheduled_at)
)}
</li>
<li>
<UserIcon className="size-3.5" />{' '}
{created_by.name}
</li>
</ul>
</div>
</ItemContent>
{/*<ItemActions>
<Button variant="ghost" size="icon-sm">
<EllipsisIcon />
</Button>
</ItemActions>*/}
</Item>
{index !== items.length - 1 && <ItemSeparator />}
</Fragment>
)
)}
</ItemGroup>
</CardContent>
</Card>
</div>
))}
</div>
)
}}
</Await>
</Suspense>
)
}
function grouping(items) {
const newItems = Object.entries(
items.reduce((acc, item) => {
const [run_at] = item.sk.split('#')
if (!acc[run_at]) {
acc[run_at] = []
}
acc[run_at].push(item)
return acc
}, [])
)
return newItems.sort((x, y) => x[0].localeCompare(y[0]))
}
const datetime = new Intl.DateTimeFormat('pt-BR', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
})