From d0dcc0a9537170c5ec6f9f9a10a79f8c81c39985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Rafael=20Siqueira?= Date: Fri, 26 Dec 2025 18:26:09 -0300 Subject: [PATCH] fix --- .../routes/_.$orgid.courses._index/route.tsx | 2 +- .../course-picker.tsx | 3 +- .../routes/_.$orgid.enrollments.add/route.tsx | 2 +- .../_.$orgid.enrollments.buy/assigned.tsx | 24 +- .../routes/_.$orgid.enrollments.buy/bulk.tsx | 48 +- .../_.$orgid.enrollments.buy/discount.tsx | 24 +- .../_.$orgid.enrollments.buy/payment.tsx | 289 ++++++--- .../_.$orgid.enrollments.buy/review.tsx | 6 +- .../routes/_.$orgid.enrollments.buy/route.tsx | 41 +- .../tests/events/test_copy_course_metadata.py | 2 +- courses-events/uv.lock | 593 ++++++++---------- 11 files changed, 574 insertions(+), 460 deletions(-) diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.courses._index/route.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.courses._index/route.tsx index ef4369f..dfea85d 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.courses._index/route.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.courses._index/route.tsx @@ -28,7 +28,7 @@ export async function loader({ context, request, params }: Route.LoaderArgs) { const courses = createSearch({ index: 'saladeaula_courses', sort: ['created_at:desc'], - filter: 'unlisted NOT EXISTS', + filter: 'unlisted = false', hitsPerPage: 100, env: cloudflare.env }) diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.add/course-picker.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.add/course-picker.tsx index 4dbb9ed..e280577 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.add/course-picker.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.add/course-picker.tsx @@ -110,13 +110,14 @@ export const CoursePicker = forwardRef( variant="link" size="icon-sm" tabIndex={-1} - className="cursor-pointer text-muted-foreground" + className="cursor-pointer text-muted-foreground hover:text-accent-foreground" onClick={toggle} > {sort == 'a-z' ? : } + {/* Force rerender to reset the scroll position */} Nenhum resultado encontrado. diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.add/route.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.add/route.tsx index 52274df..1673677 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.add/route.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.add/route.tsx @@ -90,7 +90,7 @@ export async function loader({ params, context, request }: Route.LoaderArgs) { const courses = createSearch({ index: 'saladeaula_courses', sort: ['created_at:desc'], - filter: 'unlisted NOT EXISTS', + filter: 'unlisted = false', hitsPerPage: 100, env: cloudflare.env }) diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/assigned.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/assigned.tsx index e065416..7e1a68d 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/assigned.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/assigned.tsx @@ -41,6 +41,7 @@ import { UserPicker } from '../_.$orgid.enrollments.add/user-picker' import { Summary } from './bulk' import { currency } from './utils' import { useWizard } from '@/components/wizard' +import type { Item } from './bulk' const emptyRow = { user: undefined, @@ -48,11 +49,6 @@ const emptyRow = { scheduled_for: undefined } -type Item = { - course: Enrollment['course'] - quantity: number -} - const formSchemaAssigned = formSchema.extend({ coupon: z .object({ @@ -68,7 +64,7 @@ type Schema = z.infer type AssignedProps = { onSubmit: (value: any) => void | Promise courses: Promise<{ hits: Course[] }> - enrollments: object[] + enrollments: Enrollment[] coupon?: object } @@ -84,10 +80,14 @@ export function Assigned({ resolver: zodResolver(formSchemaAssigned), defaultValues: { coupon: couponInit, - enrollments: enrollments?.map((e: any) => ({ - ...e, - scheduled_for: e.scheduled_for ? new Date(e.scheduled_for) : undefined - })) || [emptyRow] + enrollments: enrollments.length + ? enrollments.map((e: any) => ({ + ...e, + scheduled_for: e.scheduled_for + ? new Date(e.scheduled_for) + : undefined + })) + : [emptyRow] } }) @@ -113,7 +113,7 @@ export function Assigned({ return hits } - const onSubmit_ = async ({ enrollments }: Schema) => { + const onSubmit_ = async ({ enrollments, coupon }: Schema) => { const items = Object.values( enrollments.reduce>((acc, e) => { const id = e.course.id @@ -124,7 +124,7 @@ export function Assigned({ return acc }, {}) ) - await onSubmit({ enrollments, items }) + await onSubmit({ enrollments, items, coupon }) wizard('payment') } diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/bulk.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/bulk.tsx index 8a0e491..f7e792c 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/bulk.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/bulk.tsx @@ -33,7 +33,7 @@ import { Abbr } from '@repo/ui/components/abbr' import { Cell } from '../_.$orgid.enrollments.add/route' import { CoursePicker } from '../_.$orgid.enrollments.add/course-picker' import { MAX_ITEMS, type Course } from '../_.$orgid.enrollments.add/data' -import { Discount, applyDiscount } from './discount' +import { Discount, applyDiscount, type Coupon } from './discount' import { currency } from './utils' import { useWizard } from '@/components/wizard' @@ -41,13 +41,6 @@ const emptyRow = { course: undefined } -type BulkProps = { - onSubmit: (value: any) => void | Promise - courses: Promise<{ hits: Course[] }> - items?: object[] - coupon?: object -} - const item = z.object({ course: z .object( @@ -76,17 +69,26 @@ const formSchema = z.object({ type Schema = z.infer +export type Item = z.infer + +type BulkProps = { + onSubmit: (value: any) => void | Promise + courses: Promise<{ hits: Course[] }> + items: Item[] + coupon?: Coupon +} + export function Bulk({ courses, onSubmit, - items: itemInit, + items: itemsInit, coupon: couponInit }: BulkProps) { const wizard = useWizard() const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { - items: itemInit || [emptyRow], + items: itemsInit?.length ? itemsInit : [emptyRow], coupon: couponInit } }) @@ -117,8 +119,22 @@ export function Bulk({ 0 ) - const onSubmit_ = async (data: Schema) => { - await onSubmit(data) + const onSubmit_ = async ({ coupon, ...data }: Schema) => { + const items = Object.values( + data.items.reduce>((acc, item) => { + const id = item.course.id + + if (!acc[id]) { + acc[id] = { ...item } + } else { + acc[id].quantity += item.quantity + } + + return acc + }, {}) + ) + + await onSubmit({ coupon, items, enrollments: [] }) wizard('payment') } @@ -403,11 +419,11 @@ export function Summary({ subtotal, coupon, setValue }: SummaryProps) { ) : ( { + onChange={(coupon) => { setValue('coupon', { - code: sk, - amount: discount_amount, - type: discount_type + // code: sk, + // amount: discount_amount, + // type: discount_type }) }} /> diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/discount.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/discount.tsx index c5ba0c7..7d99a74 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/discount.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/discount.tsx @@ -21,17 +21,23 @@ import { FormMessage } from '@repo/ui/components/ui/form' -export const formSchema = z.object({ +const formSchema = z.object({ coupon: z.string().min(3, { error: 'Digite um cupom válido' }).trim() }) -export type Schema = z.infer +type Schema = z.infer + +export type Coupon = { + code: string + amount: number + type: 'FIXED' | 'PERCENT' +} interface DiscountProps extends Omit< ButtonHTMLAttributes, 'onChange' > { - onChange?: (value: any) => void + onChange?: (value: Coupon) => void disabled?: boolean } @@ -56,7 +62,17 @@ export function Discount({ onChange, ...props }: DiscountProps) { if (!r.ok) { return setError('coupon', { message: 'Cupom inválido' }) } - onChange?.(await r.json()) + const { + sk: code, + discount_amount: amount, + discount_type: type + } = (await r.json()) as { + sk: string + discount_amount: number + discount_type: 'FIXED' | 'PERCENT' + } + + onChange?.({ code, amount, type }) reset() set(false) diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/payment.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/payment.tsx index 593db49..320fed2 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/payment.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/payment.tsx @@ -1,63 +1,87 @@ -import { useForm, Controller, useWatch } from 'react-hook-form' +import { useForm, Controller, useWatch, type Control } from 'react-hook-form' +import { PatternFormat } from 'react-number-format' import { zodResolver } from '@hookform/resolvers/zod' import { ErrorMessage } from '@hookform/error-message' import z from 'zod' -import { ArrowRightIcon } from 'lucide-react' +import { ArrowRightIcon, CircleQuestionMarkIcon } from 'lucide-react' import { Button } from '@repo/ui/components/ui/button' +import { Kbd } from '@repo/ui/components/ui/kbd' import { Label } from '@repo/ui/components/ui/label' import { RadioGroup, RadioGroupItem } from '@repo/ui/components/ui/radio-group' import { Separator } from '@repo/ui/components/ui/separator' - -import { Checkbox } from '@repo/ui/components/ui/checkbox' +import { Input } from '@repo/ui/components/ui/input' +import { Card, CardContent } from '@repo/ui/components/ui/card' +import { + HoverCard, + HoverCardContent, + HoverCardTrigger +} from '@repo/ui/components/ui/hover-card' import { Field, - FieldDescription, FieldGroup, FieldLabel, - FieldLegend, - FieldSeparator, FieldSet } from '@repo/ui/components/ui/field' -import { Input } from '@repo/ui/components/ui/input' import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue -} from '@repo/ui/components/ui/select' -import { Textarea } from '@repo/ui/components/ui/textarea' + NativeSelect, + NativeSelectOption +} from '@repo/ui/components/ui/native-select' import { useWizard } from '@/components/wizard' -import { Card, CardContent } from '@repo/ui/components/ui/card' -const formSchema = z.object({ - payment_method: z.enum(['PIX', 'BANK_SLIP', 'CREDIT_CARD'], { - error: 'Escolha uma forma de pagamento' - }) +const creditCard = z.object({ + holder_name: z.string().min(1), + number: z.string().min(13).max(19), + exp_month: z.string().min(2), + exp_year: z.string().min(4), + cvv: z.string().min(3).max(4) }) +const formSchema = z.discriminatedUnion( + 'payment_method', + [ + z.object({ + payment_method: z.literal('PIX') + }), + z.object({ + payment_method: z.literal('BANK_SLIP') + }), + z.object({ + payment_method: z.literal('CREDIT_CARD'), + credit_card: creditCard + }) + ], + { error: 'Escolha uma forma de pagamento' } +) + type Schema = z.infer +export type CreditCard = z.infer + type PaymentProps = { onSubmit: (value: any) => void | Promise - defaultValues?: object + payment_method?: 'PIX' | 'BANK_SLIP' | 'CREDIT_CARD' + credit_card?: CreditCard } -export function Payment({ onSubmit, defaultValues }: PaymentProps) { +export function Payment({ + onSubmit, + payment_method: paymentMethodInit, + credit_card: creditCardInit = undefined +}: PaymentProps) { const wizard = useWizard() const { control, handleSubmit } = useForm({ defaultValues: { - payment_method: '' as any, - ...defaultValues + payment_method: paymentMethodInit, + credit_card: creditCardInit }, resolver: zodResolver(formSchema) }) const paymentMethod = useWatch({ control, name: 'payment_method' }) const onSubmit_ = async (data: Schema) => { - await onSubmit(data) + await onSubmit({ credit_card: undefined, ...data }) wizard('review') } @@ -72,10 +96,9 @@ export function Payment({ onSubmit, defaultValues }: PaymentProps) { value={value} onValueChange={onChange} className="lg:flex gap-3 - *:p-5 *:border *:rounded-xl *:flex-1 - *:cursor-pointer *:bg-accent/25 - *:has-[button[data-state=checked]]:bg-accent - " + *:p-5 *:border *:rounded-xl *:flex-1 + *:cursor-pointer *:bg-accent/25 + *:has-[button[data-state=checked]]:bg-accent" >