add zustard

This commit is contained in:
2025-12-30 14:37:53 -03:00
parent 58068cd463
commit bad7e15f46
15 changed files with 349 additions and 280 deletions

View File

@@ -1,4 +1,4 @@
import { Fragment } from 'react'
import { Fragment, useEffect } from 'react'
import {
ArrowRightIcon,
MinusIcon,
@@ -36,9 +36,11 @@ import { MAX_ITEMS, type Course } from '../_.$orgid.enrollments.add/data'
import { Discount, applyDiscount, type Coupon } from './discount'
import { currency } from './utils'
import { useWizard } from '@/components/wizard'
import { useWizardStore } from './store'
const emptyRow = {
course: undefined
course: undefined as any,
quantity: 1
}
const item = z.object({
@@ -57,39 +59,24 @@ const item = z.object({
})
const formSchema = z.object({
items: z.array(item).min(1).max(MAX_ITEMS),
coupon: z
.object({
code: z.string(),
type: z.enum(['FIXED', 'PERCENT']),
amount: z.number().positive()
})
.optional()
items: z.array(item).min(1).max(MAX_ITEMS)
})
type Schema = z.infer<typeof formSchema>
type Schema = z.input<typeof formSchema>
export type Item = z.infer<typeof item>
type BulkProps = {
onSubmit: (value: any) => void | Promise<void>
courses: Promise<{ hits: Course[] }>
items: Item[]
coupon?: Coupon
}
export function Bulk({
courses,
onSubmit,
items: itemsInit,
coupon: couponInit
}: BulkProps) {
export function Bulk({ courses }: BulkProps) {
const wizard = useWizard()
const { update, ...state } = useWizardStore()
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: {
items: itemsInit?.length ? itemsInit : [emptyRow],
coupon: couponInit
items: state.items.length ? state.items : [emptyRow]
}
})
const {
@@ -109,35 +96,20 @@ export function Bulk({
control,
name: 'items'
})
const coupon = useWatch({ control, name: 'coupon' })
const subtotal = items.reduce(
(acc, { course, quantity }) =>
acc +
(course?.unit_price || 0) *
(Number.isFinite(quantity) && quantity > 0 ? quantity : 1),
0
)
const onSubmit_ = async ({ coupon, ...data }: Schema) => {
const items = Object.values(
data.items.reduce<Record<string, Item>>((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: [] })
const onSubmit_ = async ({ items }: Schema) => {
update({ items })
wizard('payment')
}
useEffect(() => {
const parsed = formSchema.safeParse({ items })
if (parsed.success) {
update(parsed.data)
}
}, [items])
return (
<Form {...form}>
<form onSubmit={handleSubmit(onSubmit_)} className="space-y-4">
@@ -316,7 +288,7 @@ export function Bulk({
</Button>
</div>
<Summary {...{ subtotal, coupon, setValue }} />
<Summary />
</div>
<Separator />
@@ -337,17 +309,9 @@ export function Bulk({
)
}
type SummaryProps = {
subtotal: number
coupon?: Coupon
setValue: UseFormSetValue<any>
}
export function Summary({ subtotal, coupon, setValue }: SummaryProps) {
const discount = coupon
? applyDiscount(subtotal, coupon.amount, coupon.type) * -1
: 0
const total = subtotal > 0 ? subtotal + discount : 0
export function Summary() {
const { summary, coupon, update } = useWizardStore()
const { total, discount, subtotal } = summary()
return (
<>
@@ -407,7 +371,7 @@ export function Summary({ subtotal, coupon, setValue }: SummaryProps) {
tabIndex={-1}
variant="ghost"
onClick={() => {
setValue('coupon', null)
update({ coupon: undefined })
}}
>
<XIcon />
@@ -416,7 +380,7 @@ export function Summary({ subtotal, coupon, setValue }: SummaryProps) {
<Discount
disabled={subtotal === 0}
onChange={(coupon) => {
setValue('coupon', coupon)
update({ coupon })
}}
/>
)}