diff --git a/api.saladeaula.digital/app/routes/courses/__init__.py b/api.saladeaula.digital/app/routes/courses/__init__.py index a626c0c..eb53708 100644 --- a/api.saladeaula.digital/app/routes/courses/__init__.py +++ b/api.saladeaula.digital/app/routes/courses/__init__.py @@ -64,7 +64,8 @@ def put_course(course_id: str): event = router.current_event if not event.decoded_body: - raise BadRequestError('Invalid request body') now_ = now() + raise BadRequestError('Invalid request body') + body = parse( event.headers, BytesIO(event.decoded_body.encode()), @@ -98,7 +99,7 @@ def put_course(course_id: str): ':cert': course.cert.model_dump(), ':access_period': course.access_period, ':draft': course.draft, - ':updated_at': now_, + ':updated_at': now(), }, cond_expr='attribute_exists(sk)', exc_cls=BadRequestError, diff --git a/api.saladeaula.digital/app/routes/enrollments/__init__.py b/api.saladeaula.digital/app/routes/enrollments/__init__.py index bf00236..da20000 100644 --- a/api.saladeaula.digital/app/routes/enrollments/__init__.py +++ b/api.saladeaula.digital/app/routes/enrollments/__init__.py @@ -21,5 +21,9 @@ dyn = DynamoDBPersistenceLayer(ENROLLMENT_TABLE, dynamodb_client) @router.get('/') def get_enrollment(enrollment_id: str): return dyn.collection.get_items( - TransactKey(enrollment_id) + SortKey('0') + SortKey('ORG') + SortKey('LOCK') + TransactKey(enrollment_id) + + SortKey('0') + + SortKey('ORG', rename_key='org') + + SortKey('CANCEL_POLICY', rename_key='cancel_policy') + + SortKey('LOCK', rename_key='lock') ) diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments._index/columns.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments._index/columns.tsx index 0770ad9..2175fca 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments._index/columns.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments._index/columns.tsx @@ -1,7 +1,7 @@ 'use client' import type { CellContext, ColumnDef } from '@tanstack/react-table' -import { useToggle } from 'ahooks' +import { useRequest, useToggle } from 'ahooks' import { CircleXIcon, EllipsisVerticalIcon, @@ -9,7 +9,7 @@ import { HelpCircleIcon, LockOpenIcon } from 'lucide-react' -import { NavLink, useParams } from 'react-router' +import type { ComponentProps, MouseEvent } from 'react' import { toast } from 'sonner' import { @@ -40,7 +40,6 @@ import { cn, initials } from '@repo/ui/lib/utils' import { Abbr } from '@/components/abbr' import { DataTableColumnHeader } from '@/components/data-table/column-header' -import { useDataTable } from '@/components/data-table/data-table' import { labels, statuses } from './data' // This type is used to define the shape of our data. @@ -218,13 +217,40 @@ function cellDate({ return <> } +async function getEnrollment(id: string) { + const r = await fetch(`/~/api/enrollments/${id}`, { + method: 'GET' + }) + + return (await r.json()) as { + cancel_policy?: any + lock?: { hash: string } + } +} + function ActionMenu({ row }: { row: any }) { + const [open, { set: setOpen }] = useToggle(false) const cert = row.original?.cert - const progress = row.getValue('progress') + const { data, loading, run, refresh } = useRequest(getEnrollment, { + manual: true + }) + + const onSuccess = () => { + refresh() + setOpen(false) + } return (
- + { + setOpen(open) + if (data) return + run(row.id) + }} + >
) } -function DownloadItem({ id, ...props }: { id: string }) { +type ItemProps = ComponentProps & { + id: string + onSuccess?: () => void +} + +function DownloadItem({ id, onSuccess, ...props }: ItemProps) { const [loading, { set }] = useToggle(false) - const download = async (e) => { + const download = async (e: Event) => { e.preventDefault() set(true) const r = await fetch(`/~/api/enrollments/${id}/download`, { @@ -259,8 +310,10 @@ function DownloadItem({ id, ...props }: { id: string }) { if (r.ok) { const { presigned_url } = (await r.json()) as { presigned_url: string } window.open(presigned_url, '_blank') + + set(false) + onSuccess?.() } - set(false) } return ( @@ -270,32 +323,43 @@ function DownloadItem({ id, ...props }: { id: string }) { ) } -function RemoveDedupItem({ id, ...props }: { id: string }) { +function RemoveDedupItem({ + id, + lock, + onSuccess, + ...props +}: ItemProps & { + lock?: { hash: string } +}) { const [loading, { set }] = useToggle(false) - const { orgid } = useParams() - const { table } = useDataTable() + const [open, { set: setOpen }] = useToggle(false) - const cancel = async (e) => { + const cancel = async (e: MouseEvent) => { e.preventDefault() set(true) - const r = await fetch(`/~/api/enrollments/${orgid}/dedupwindow`, { - method: 'DELETE' + if (!lock) return + + const r = await fetch(`/~/api/enrollments/${id}/dedupwindow`, { + method: 'DELETE', + body: JSON.stringify({ lock_hash: lock.hash }), + headers: new Headers({ 'Content-Type': 'application/json' }) }) if (r.ok) { toast.info('A proteção contra duplicação foi removida') - // @ts-ignore - table.options.meta?.removeRow?.(id) + setOpen(false) + onSuccess?.() } } return ( - + e.preventDefault()} + disabled={!lock} {...props} > Remover proteção @@ -322,32 +386,43 @@ function RemoveDedupItem({ id, ...props }: { id: string }) { ) } -function CancelItem({ id, ...props }: { id: string }) { +function CancelItem({ + id, + cancelPolicy, + lock, + ...props +}: ItemProps & { + cancelPolicy?: object + lock?: { + hash: string + } +}) { const [loading, { set }] = useToggle(false) - const { orgid } = useParams() - const { table } = useDataTable() + const [open, { set: setOpen }] = useToggle(false) - const cancel = async (e) => { + const cancel = async (e: MouseEvent) => { e.preventDefault() set(true) - const r = await fetch(`/~/api/enrollments/${orgid}/cancel`, { - method: 'PATCH' + const r = await fetch(`/~/api/enrollments/${id}/cancel`, { + method: 'POST', + headers: new Headers({ 'Content-Type': 'application/json' }) }) if (r.ok) { toast.info('A matrícula foi cancelada') - // @ts-ignore - table.options.meta?.removeRow?.(id) } + + setOpen(false) } return ( - + e.preventDefault()} + disabled={!cancelPolicy} {...props} > Cancelar