add feedback
This commit is contained in:
@@ -3,9 +3,13 @@ import type { Route } from './+types/route'
|
|||||||
import type { MouseEvent, ReactNode } from 'react'
|
import type { MouseEvent, ReactNode } from 'react'
|
||||||
import { useRequest, useToggle } from 'ahooks'
|
import { useRequest, useToggle } from 'ahooks'
|
||||||
import {
|
import {
|
||||||
|
AlertTriangleIcon,
|
||||||
BanIcon,
|
BanIcon,
|
||||||
|
CalendarClockIcon,
|
||||||
CalendarIcon,
|
CalendarIcon,
|
||||||
CircleXIcon,
|
CircleXIcon,
|
||||||
|
ClockAlertIcon,
|
||||||
|
ClockCheckIcon,
|
||||||
EllipsisIcon,
|
EllipsisIcon,
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
RocketIcon,
|
RocketIcon,
|
||||||
@@ -151,19 +155,37 @@ export default function Route({
|
|||||||
<TabsContent value="pending" className="space-y-5">
|
<TabsContent value="pending" className="space-y-5">
|
||||||
<Timeline events={scheduled}>
|
<Timeline events={scheduled}>
|
||||||
{({ items }) => (
|
{({ items }) => (
|
||||||
<Scheduled items={items} className="col-span-4" />
|
<Card className="col-span-4">
|
||||||
|
<CardContent>
|
||||||
|
<Scheduled items={items} />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
)}
|
)}
|
||||||
</Timeline>
|
</Timeline>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="executed" className="space-y-5">
|
<TabsContent value="executed" className="space-y-5">
|
||||||
<Timeline events={executed}>
|
<Timeline events={executed}>
|
||||||
{({ items }) => <>...</>}
|
{({ items }) => (
|
||||||
|
<Card className="col-span-4">
|
||||||
|
<CardContent>
|
||||||
|
<Executed items={items} />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
</Timeline>
|
</Timeline>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="failed" className="space-y-5">
|
<TabsContent value="failed" className="space-y-5">
|
||||||
<Timeline events={failed}>{({ items }) => <>...</>}</Timeline>
|
<Timeline events={failed}>
|
||||||
|
{({ items }) => (
|
||||||
|
<Card className="col-span-4">
|
||||||
|
<CardContent>
|
||||||
|
<Failed items={items} />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</Timeline>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
@@ -198,13 +220,10 @@ function Timeline({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Scheduled({ items, className }) {
|
function Scheduled({ items = [] }) {
|
||||||
return (
|
return (
|
||||||
<Card className={className}>
|
|
||||||
<CardContent>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
{items.map(
|
{items.map(({ sk, user, course, created_by, scheduled_at }, index) => (
|
||||||
({ sk, user, course, created_by, scheduled_at }, index) => (
|
|
||||||
<Fragment key={index}>
|
<Fragment key={index}>
|
||||||
<Item className="max-lg:px-0 max-lg:first:pt-0 max-lg:last:pb-0">
|
<Item className="max-lg:px-0 max-lg:first:pt-0 max-lg:last:pb-0">
|
||||||
<ItemMedia className="hidden lg:block">
|
<ItemMedia className="hidden lg:block">
|
||||||
@@ -225,11 +244,12 @@ function Scheduled({ items, className }) {
|
|||||||
<div className="mt-1">
|
<div className="mt-1">
|
||||||
<ul className="lg:flex gap-2.5 text-muted-foreground text-sm *:flex *:gap-1 *:items-center">
|
<ul className="lg:flex gap-2.5 text-muted-foreground text-sm *:flex *:gap-1 *:items-center">
|
||||||
<li>
|
<li>
|
||||||
<CalendarIcon className="size-3.5" />{' '}
|
<CalendarIcon className="size-3.5" />
|
||||||
{datetime.format(new Date(scheduled_at))}
|
<span>{datetime.format(new Date(scheduled_at))}</span>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<UserIcon className="size-3.5" /> {created_by.name}
|
<UserIcon className="size-3.5" />
|
||||||
|
<span>{created_by.name}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -242,11 +262,92 @@ function Scheduled({ items, className }) {
|
|||||||
|
|
||||||
{index !== items.length - 1 && <ItemSeparator />}
|
{index !== items.length - 1 && <ItemSeparator />}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
))}
|
||||||
)}
|
</ItemGroup>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Executed({ items = [] }) {
|
||||||
|
return (
|
||||||
|
<ItemGroup>
|
||||||
|
{items.map(({ course, user, created_at }, index) => (
|
||||||
|
<Fragment key={index}>
|
||||||
|
<Item className="max-lg:px-0 max-lg:first:pt-0 max-lg:last:pb-0">
|
||||||
|
<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>
|
||||||
|
<ClockCheckIcon className="size-3.5" />
|
||||||
|
<span>{datetime.format(new Date(created_at))}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</ItemContent>
|
||||||
|
</Item>
|
||||||
|
|
||||||
|
{index !== items.length - 1 && <ItemSeparator />}
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</ItemGroup>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Failed({ items = [] }) {
|
||||||
|
return (
|
||||||
|
<ItemGroup>
|
||||||
|
{items.map(({ snapshot: { course, user }, cause, created_at }, index) => (
|
||||||
|
<Fragment key={index}>
|
||||||
|
<Item className="max-lg:px-0 max-lg:first:pt-0 max-lg:last:pb-0">
|
||||||
|
<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>
|
||||||
|
<ClockAlertIcon className="size-3.5" />
|
||||||
|
<span>{datetime.format(new Date(created_at))}</span>
|
||||||
|
</li>
|
||||||
|
{cause?.type === 'DeduplicationConflictError' ? (
|
||||||
|
<li className="text-red-400">
|
||||||
|
<AlertTriangleIcon className="size-3.5" />
|
||||||
|
<span>Protegido contra duplicação</span>
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</ItemContent>
|
||||||
|
</Item>
|
||||||
|
|
||||||
|
{index !== items.length - 1 && <ItemSeparator />}
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,9 +367,7 @@ function ActionMenu({ sk }: { sk: string }) {
|
|||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
|
|
||||||
<DropdownMenuContent align="end" className="*:cursor-pointer w-42">
|
<DropdownMenuContent align="end" className="*:cursor-pointer w-42">
|
||||||
<DropdownMenuItem disabled>
|
<Proceedtem sk={sk} onSuccess={onSuccess} />
|
||||||
<RocketIcon /> Matricular agora
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<CancelItem sk={sk} onSuccess={onSuccess} />
|
<CancelItem sk={sk} onSuccess={onSuccess} />
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
@@ -276,6 +375,26 @@ function ActionMenu({ sk }: { sk: string }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Proceedtem({ sk, onSuccess }: { sk: string; onSuccess?: () => void }) {
|
||||||
|
const { runAsync, loading } = useRequest(
|
||||||
|
async () => {
|
||||||
|
await new Promise((r) => setTimeout(r, 1000))
|
||||||
|
},
|
||||||
|
{ manual: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const proceed = async (e: MouseEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
await runAsync()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenuItem onClick={proceed}>
|
||||||
|
{loading ? <Spinner /> : <RocketIcon />} Matricular agora
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function CancelItem({ sk, onSuccess }: { sk: string; onSuccess?: () => void }) {
|
function CancelItem({ sk, onSuccess }: { sk: string; onSuccess?: () => void }) {
|
||||||
const { orgid } = useParams()
|
const { orgid } = useParams()
|
||||||
const [open, { set: setOpen }] = useToggle(false)
|
const [open, { set: setOpen }] = useToggle(false)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from aws_lambda_powertools.utilities.data_classes import (
|
|||||||
from aws_lambda_powertools.utilities.typing import LambdaContext
|
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||||
from layercake.dateutils import now
|
from layercake.dateutils import now
|
||||||
from layercake.dynamodb import DynamoDBPersistenceLayer
|
from layercake.dynamodb import DynamoDBPersistenceLayer
|
||||||
from layercake.funcs import pick
|
|
||||||
|
|
||||||
from boto3clients import dynamodb_client
|
from boto3clients import dynamodb_client
|
||||||
from config import ENROLLMENT_TABLE
|
from config import ENROLLMENT_TABLE
|
||||||
@@ -58,14 +57,13 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
|||||||
persistence_layer=dyn,
|
persistence_layer=dyn,
|
||||||
)
|
)
|
||||||
|
|
||||||
keys = ('id', 'name')
|
|
||||||
dyn.put_item(
|
dyn.put_item(
|
||||||
item={
|
item={
|
||||||
'id': old_image['id'],
|
'id': old_image['id'],
|
||||||
'sk': f'{sk}#EXECUTED',
|
'sk': f'{sk}#EXECUTED',
|
||||||
'enrollment_id': enrollment.id,
|
'enrollment_id': enrollment.id,
|
||||||
'user': pick(keys, old_image['user']),
|
'user': old_image['user'],
|
||||||
'course': pick(keys, old_image['course']),
|
'course': old_image['course'],
|
||||||
'created_by': created_by,
|
'created_by': created_by,
|
||||||
'created_at': now_,
|
'created_at': now_,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user