import type { Route } from './+types/route' import { ErrorMessage } from '@hookform/error-message' import { zodResolver } from '@hookform/resolvers/zod' import { CircleQuestionMarkIcon, CopyIcon, PlusIcon, Trash2Icon } from 'lucide-react' import { Fragment, useMemo } from 'react' import { Controller, useFieldArray, useForm, useWatch } from 'react-hook-form' import { Link, redirect, useFetcher, useParams } from 'react-router' import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from '@repo/ui/components/ui/breadcrumb' import { Button } from '@repo/ui/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@repo/ui/components/ui/card' import { HoverCard, HoverCardContent, HoverCardTrigger } from '@repo/ui/components/ui/hover-card' import { Kbd } from '@repo/ui/components/ui/kbd' import { Separator } from '@repo/ui/components/ui/separator' import { Spinner } from '@repo/ui/components/ui/spinner' import { HttpMethod, request as req } from '@repo/util/request' import { workspaceContext } from '@/middleware/workspace' import { CoursePicker } from '../_.$orgid.enrollments.add/course-picker' import { formSchema, MAX_ITEMS, type Course, type Schema, type User } from '../_.$orgid.enrollments.add/data' import { Cell, DuplicateRowMultipleTimes, emptyRow } from '../_.$orgid.enrollments.add/route' import { ScheduledForInput } from '../_.$orgid.enrollments.add/scheduled-for' import { UserPicker } from '../_.$orgid.enrollments.add/user-picker' export function meta({}: Route.MetaArgs) { return [{ title: 'Adicionar matrícula' }] } type Seat = { order_id: string enrollment_id: string } export async function loader({ request, params, context }: Route.LoaderArgs) { const { subscription } = context.get(workspaceContext) // If there's subscription for the org, redirect it if (subscription) { throw redirect('../enrollments/add') } const seats = await req({ url: `/orgs/${params.orgid}/seats`, request, context }) .then((r) => r.json() as any) .then(({ items }) => items as { sk: string; course: Course }[]) return { seats } } export async function action({ params, request, context }: Route.ActionArgs) { const { orgid: org_id } = params const body = (await request.json()) as object const r = await req({ url: `enrollments`, headers: new Headers({ 'Content-Type': 'application/json' }), method: HttpMethod.POST, body: JSON.stringify({ org_id, ...body }), request, context }) const data = (await r.json()) as { sk: string } return redirect(`/${org_id}/enrollments/${data.sk}/submitted`) } export default function Route({ loaderData: { seats } }: Route.ComponentProps) { const { orgid } = useParams() const fetcher = useFetcher() const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { enrollments: [emptyRow] } }) const { formState, control, handleSubmit, getValues, setValue } = form const { fields, insert, remove, append } = useFieldArray({ control, name: 'enrollments' }) const enrollments = useWatch({ control, name: 'enrollments' }) const usedSeatIds = useMemo(() => { return new Set(enrollments?.map((e) => e.id).filter(Boolean)) }, [enrollments]) const seatsByCourse = useMemo(() => { return seats.reduce>((acc, seat) => { const courseId = seat.course.id const [, order_id, , enrollment_id] = seat.sk.split('#') if (!acc[courseId]) { acc[courseId] = [] } acc[courseId].push({ order_id, enrollment_id }) return acc }, {}) }, [seats]) const usedSeatsByCourse = useMemo(() => { const acc = new Map() seats.forEach((seat) => { const [, , , enrollment_id] = seat.sk.split('#') if (!usedSeatIds.has(enrollment_id)) { return } const courseId = seat.course.id acc.set(courseId, (acc.get(courseId) ?? 0) + 1) }) return acc }, [seats, usedSeatIds]) const courses = useMemo(() => { return { hits: Array.from( seats .reduce((acc, { course }) => { const existing = acc.get(course.id) if (existing) { existing.quantity += 1 } else { acc.set(course.id, { ...course, metadata__unit_price: 1, quantity: 1, disabled: false }) } return acc }, new Map()) .values() ).map((course) => { const used = usedSeatsByCourse.get(course.id) ?? 0 return { ...course, disabled: used >= course.quantity } }) } }, [seats, usedSeatsByCourse]) const onSubmit = async (data: Schema) => { await fetcher.submit(JSON.stringify(data), { method: 'post', encType: 'application/json' }) } const onSearch = async (search: string) => { const params = new URLSearchParams({ q: search }) const r = await fetch(`/${orgid}/users.json?${params.toString()}`) const { hits } = (await r.json()) as { hits: User[] } return hits } const pickSeat = (courseId: string): Seat | null => { const pool = seatsByCourse[courseId] if (!pool) { return null } return ( pool.find((seat) => { return !usedSeatIds.has(seat.enrollment_id) }) ?? null ) } const duplicateRow = (index: number, times: number = 1) => { if (fields.length + times > MAX_ITEMS) { return null } const { course, ...rest } = getValues(`enrollments.${index}`) if (!course?.id) { Array.from({ length: times }, (_, i) => { // @ts-ignore insert(index + 1 + i, { course: null }) }) return null } const reservedSeatIds = new Set(usedSeatIds) Array.from({ length: times }, (_, i) => { const pool = seatsByCourse[course.id] const seat = pool?.find((seat) => !reservedSeatIds.has(seat.enrollment_id)) ?? null if (seat) { reservedSeatIds.add(seat.enrollment_id) // @ts-ignore insert(index + 1 + i, { id: seat.enrollment_id, seat: { order_id: seat.order_id }, course, ...rest }) } else { // @ts-ignore insert(index + 1 + i, { course: null }) } }) } return (
Matrículas Adicionar matrículas
Adicionar matrículas Siga os passos abaixo para adicionar colaboradores às matrículas abertas.
{/* Header */} <> Colaborador Curso Matricular em

Escolha a data em que o colaborador será matriculado no curso.

Você poderá acompanhar as matrículas em{' '} Agendamentos

{/**/} {/* Rows */} <> {fields.map((field, index) => ( {/* Separator only for mobile */} {index >= 1 &&
} (
(

{message}

)} />
)} /> (
{ const seat = pickSeat(course.id) if (!seat) { return } setValue( `enrollments.${index}.id`, seat.enrollment_id ) setValue( `enrollments.${index}.seat.order_id`, seat.order_id ) onChange(course) }} options={courses.hits} error={fieldState.error} readOnly /> (

{message}

)} />
)} /> ( )} /> {/* Action */}
))}
) }