add certs page
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import {
|
||||
BookCopyIcon,
|
||||
CalendarClockIcon,
|
||||
FileBadgeIcon,
|
||||
// FileBadgeIcon,
|
||||
GraduationCap,
|
||||
LayoutDashboardIcon,
|
||||
@@ -65,11 +66,11 @@ const data = {
|
||||
url: '/enrollments',
|
||||
icon: GraduationCap
|
||||
},
|
||||
// {
|
||||
// title: 'Certificações',
|
||||
// url: '/certs',
|
||||
// icon: FileBadgeIcon
|
||||
// },
|
||||
{
|
||||
title: 'Certificações',
|
||||
url: '/certs',
|
||||
icon: FileBadgeIcon
|
||||
},
|
||||
{
|
||||
title: 'Agendamentos',
|
||||
url: '/scheduled',
|
||||
|
||||
@@ -76,73 +76,71 @@ export default function Route({
|
||||
const search = searchParams.get('s') as string
|
||||
|
||||
return (
|
||||
<>
|
||||
<Suspense fallback={<Skeleton />}>
|
||||
<div className="space-y-0.5 mb-8">
|
||||
<h1 className="text-2xl font-bold tracking-tight">
|
||||
Resumo de cobranças
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Acompanhe as cobranças em tempo real e garanta mais eficiência no
|
||||
controle financeiro.
|
||||
</p>
|
||||
</div>
|
||||
<Suspense fallback={<Skeleton />}>
|
||||
<div className="space-y-0.5 mb-8">
|
||||
<h1 className="text-2xl font-bold tracking-tight">
|
||||
Resumo de cobranças
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Acompanhe as cobranças em tempo real e garanta mais eficiência no
|
||||
controle financeiro.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Await resolve={billing}>
|
||||
{({ items = [], ...billing }) => {
|
||||
const {
|
||||
icon: Icon,
|
||||
label: status,
|
||||
color
|
||||
} = statuses?.[billing?.status || 'CLOSED']
|
||||
<Await resolve={billing}>
|
||||
{({ items = [], ...billing }) => {
|
||||
const {
|
||||
icon: Icon,
|
||||
label: status,
|
||||
color
|
||||
} = statuses?.[billing?.status || 'CLOSED']
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex max-lg:flex-col gap-2.5">
|
||||
<div className="w-full xl:w-1/4">
|
||||
<SearchForm
|
||||
defaultValue={search || ''}
|
||||
placeholder={
|
||||
<>
|
||||
Digite <Kbd className="border font-mono">/</Kbd>{' '}
|
||||
para pesquisar
|
||||
</>
|
||||
}
|
||||
onChange={(value) =>
|
||||
setSearchParams((searchParams) => {
|
||||
searchParams.set('s', String(value))
|
||||
return searchParams
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<RangePeriod
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
billingDay={billing_day}
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex max-lg:flex-col gap-2.5">
|
||||
<div className="w-full xl:w-1/4">
|
||||
<SearchForm
|
||||
defaultValue={search || ''}
|
||||
placeholder={
|
||||
<>
|
||||
Digite <Kbd className="border font-mono">/</Kbd> para
|
||||
pesquisar
|
||||
</>
|
||||
}
|
||||
onChange={(value) =>
|
||||
setSearchParams((searchParams) => {
|
||||
searchParams.set('s', String(value))
|
||||
return searchParams
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<Button
|
||||
className={cn('pointer-events-none lg:ml-auto', color)}
|
||||
variant="outline"
|
||||
asChild
|
||||
>
|
||||
<span>
|
||||
<Icon className="size-3.5" /> {status}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<List items={items} search={search} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}}
|
||||
</Await>
|
||||
</Suspense>
|
||||
</>
|
||||
<RangePeriod
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
billingDay={billing_day}
|
||||
/>
|
||||
|
||||
<Button
|
||||
className={cn('pointer-events-none lg:ml-auto', color)}
|
||||
variant="outline"
|
||||
asChild
|
||||
>
|
||||
<span>
|
||||
<Icon className="size-3.5" /> {status}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<List items={items} search={search} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}}
|
||||
</Await>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -217,7 +215,7 @@ function List({ items, search }) {
|
||||
{charges.length ? (
|
||||
<>
|
||||
<TableHeader>
|
||||
<TableRow className="bg-muted-foreground/10 pointer-events-none">
|
||||
<TableRow className=" pointer-events-none">
|
||||
<TableHead>Colaborador</TableHead>
|
||||
<TableHead>Curso</TableHead>
|
||||
<TableHead>Matriculado por</TableHead>
|
||||
|
||||
@@ -1,12 +1,46 @@
|
||||
import type { Route } from './+types/route'
|
||||
|
||||
import { Suspense } from 'react'
|
||||
import { Await } from 'react-router'
|
||||
|
||||
import { DateTime } from '@repo/ui/components/datetime'
|
||||
import { Skeleton } from '@repo/ui/components/skeleton'
|
||||
import { Card, CardContent } from '@repo/ui/components/ui/card'
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow
|
||||
} from '@repo/ui/components/ui/table'
|
||||
import { request as req } from '@repo/util/request'
|
||||
|
||||
export function meta({}) {
|
||||
return [{ title: 'Certificações' }]
|
||||
}
|
||||
|
||||
export default function Route({}: Route.ComponentProps) {
|
||||
export async function loader({ context, request, params }: Route.LoaderArgs) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
|
||||
const month =
|
||||
searchParams.get('month') || new Date().toISOString().slice(0, 10)
|
||||
const reporting = req({
|
||||
url: `/orgs/${params.orgid}/enrollments/certs?month=${month}`,
|
||||
context,
|
||||
request
|
||||
}).then((r) => r.json())
|
||||
|
||||
return {
|
||||
reporting
|
||||
}
|
||||
}
|
||||
|
||||
export default function Route({
|
||||
loaderData: { reporting }
|
||||
}: Route.ComponentProps) {
|
||||
return (
|
||||
<>
|
||||
<Suspense fallback={<Skeleton />}>
|
||||
<div className="space-y-0.5 mb-8">
|
||||
<h1 className="text-2xl font-bold tracking-tight">
|
||||
Gerenciar certificações
|
||||
@@ -16,6 +50,57 @@ export default function Route({}: Route.ComponentProps) {
|
||||
prazos e renovações com facilidade.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className=" pointer-events-none">
|
||||
<TableHead>Colaborador</TableHead>
|
||||
<TableHead>Curso</TableHead>
|
||||
<TableHead>Matriculado em</TableHead>
|
||||
<TableHead>Concluído em</TableHead>
|
||||
<TableHead>Cert. válido até</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<Await resolve={reporting}>
|
||||
{({ items = [] }) => {
|
||||
return (
|
||||
<>
|
||||
{items.map(
|
||||
({
|
||||
course,
|
||||
user,
|
||||
enrolled_at,
|
||||
completed_at,
|
||||
expires_at
|
||||
}) => {
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell>{user.name}</TableCell>
|
||||
<TableCell>{course.name}</TableCell>
|
||||
<TableCell>
|
||||
<DateTime>{enrolled_at}</DateTime>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<DateTime>{completed_at}</DateTime>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<DateTime>{expires_at}</DateTime>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}}
|
||||
</Await>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -190,7 +190,10 @@ export default function Route({
|
||||
<Button size="sm" variant="outline" asChild>
|
||||
<NavLink to="../enrollments/seats">
|
||||
{({ isPending }) => (
|
||||
<>{isPending ? <Spinner /> : <PlusIcon />} Matricular</>
|
||||
<>
|
||||
{isPending ? <Spinner /> : <PlusIcon />}
|
||||
<span className="max-lg:hidden">Matricular</span>
|
||||
</>
|
||||
)}
|
||||
</NavLink>
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user