add restore

This commit is contained in:
2025-12-19 18:42:27 -03:00
parent 01c7de34fa
commit 13f8126269
10 changed files with 175 additions and 23 deletions

View File

@@ -8,9 +8,10 @@ import {
UserIcon,
BanIcon,
PlusIcon,
XIcon
EllipsisIcon,
RotateCcw
} from 'lucide-react'
import { Link } from 'react-router'
import { Link, useParams } from 'react-router'
import { Suspense } from 'react'
import {
@@ -47,7 +48,12 @@ 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'
import { Badge } from '@repo/ui/components/ui/badge'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from '@repo/ui/components/ui/dropdown-menu'
export function meta({}: Route.MetaArgs) {
return [{ title: 'Relatório de matrículas' }]
@@ -62,11 +68,15 @@ export async function loader({ context, request, params }: Route.LoaderArgs) {
}).then((r) => r.json())
return {
data: submission
submission
}
}
export default function Route({ loaderData: { data } }: Route.ComponentProps) {
export default function Route({
loaderData: { submission }
}: Route.ComponentProps) {
const { id } = useParams()
return (
<Suspense fallback={<Skeleton />}>
<div className="space-y-2.5">
@@ -90,7 +100,7 @@ export default function Route({ loaderData: { data } }: Route.ComponentProps) {
</BreadcrumbList>
</Breadcrumb>
<Await resolve={data} errorElement={<NotFound />}>
<Await resolve={submission} errorElement={<NotFound />}>
{({ enrolled, scheduled, sk, created_by }) => {
const succeed = enrolled?.filter(
({ status }) => status === 'success'
@@ -146,6 +156,30 @@ export default function Route({ loaderData: { data } }: Route.ComponentProps) {
))}
</ul>
</AlertDescription>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
className="absolute right-2.5 top-2.5 text-accent-foreground cursor-pointer"
variant="ghost"
size="icon-sm"
>
<EllipsisIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-42">
<DropdownMenuItem
className="cursor-pointer"
asChild
>
<Link
to={`../enrollments/add?submission=${id}`}
>
<RotateCcw /> Restaurar dados
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</Alert>
)}

View File

@@ -20,8 +20,7 @@ export const enrollment = z.object({
{
id: z.string(),
name: z.string(),
access_period: z.number(),
unit_price: z.number()
access_period: z.number()
},
{ error: 'Escolha um curso' }
)

View File

@@ -19,7 +19,7 @@ import {
} from 'lucide-react'
import { redirect, Link, useParams, useFetcher } from 'react-router'
import { Controller, useFieldArray, useForm } from 'react-hook-form'
import { Fragment, use, useMemo, useState } from 'react'
import { Fragment, use, useEffect, useMemo, useState } from 'react'
import { format } from 'date-fns'
import { ptBR } from 'react-day-picker/locale'
import { zodResolver } from '@hookform/resolvers/zod'
@@ -75,12 +75,15 @@ import { cloudflareContext } from '@repo/auth/context'
import { SearchFilter } from '@repo/ui/components/search-filter'
import { formSchema, type Schema, MAX_ITEMS } from './data'
import { pick } from 'ramda'
export function meta({}: Route.MetaArgs) {
return [{ title: 'Adicionar matrícula' }]
}
export async function loader({ context }: Route.LoaderArgs) {
export async function loader({ params, context, request }: Route.LoaderArgs) {
const url = new URL(request.url)
const submissionId = url.searchParams.get('submission')
const cloudflare = context.get(cloudflareContext)
const courses = createSearch({
index: 'saladeaula_courses',
@@ -90,7 +93,15 @@ export async function loader({ context }: Route.LoaderArgs) {
env: cloudflare.env
})
return { courses }
const submission = submissionId
? req({
url: `/orgs/${params.orgid}/enrollments/${submissionId}/submitted`,
context,
request
}).then((r) => r.json())
: Promise.resolve({ enrolled: [] })
return { courses, submission }
}
export async function action({ params, request, context }: Route.ActionArgs) {
@@ -116,9 +127,10 @@ const emptyRow = {
}
export default function Route({
loaderData: { courses }
loaderData: { courses, submission }
}: Route.ComponentProps) {
const { orgid } = useParams()
const { enrolled } = use(submission)
const fetcher = useFetcher()
const form = useForm({
resolver: zodResolver(formSchema),
@@ -156,6 +168,18 @@ export default function Route({
})
}
useEffect(() => {
if (enrolled.length === 0) {
return
}
reset({
enrollments: enrolled.map(({ input_record }) =>
pick(['course', 'user'], input_record)
)
})
}, [enrolled, reset])
return (
<div className="space-y-2.5">
<Breadcrumb>

View File

@@ -25,6 +25,7 @@
"luxon": "^3.7.2",
"meilisearch": "^0.54.0",
"meilisearch-helper": "github:sergiors/meilisearch-helper",
"ramda": "^0.32.0",
"react": "^19.2.1",
"react-dom": "^19.2.1",
"react-router": "^7.10.1",

View File

@@ -62,10 +62,10 @@ export default function Component({
<Container className="space-y-5 max-w-2xl">
<Suspense fallback={<Skeleton />}>
<div className="space-y-1.5">
<h1 className="text-4xl font-semibold tracking-tight sm:text-3xl xl:text-4xl">
<h1 className="text-2xl lg:text-4xl font-semibold tracking-tight sm:text-3xl xl:text-4xl">
Expore nossos cursos
</h1>
<p className="text-muted-foreground text-[1.05rem] text-balance sm:text-base">
<p className="text-muted-foreground lg:text-[1.05rem] lg:text-balance text-base">
Conheça todos os cursos que oferecemos, desenvolvidos para
fortalecer suas competências profissionais e apoiar seu crescimento
contínuo na carreira.