144 lines
3.4 KiB
TypeScript
144 lines
3.4 KiB
TypeScript
import { create } from 'zustand'
|
|
import { persist } from 'zustand/middleware'
|
|
import { applyDiscount, type Coupon } from './discount'
|
|
import { calcInterest, type CreditCard } from './payment'
|
|
import type { PaymentMethod } from '@repo/ui/routes/orders/data'
|
|
import type { Enrollment } from '../_.$orgid.enrollments.add/data'
|
|
import type { Item } from './bulk'
|
|
import type { Address } from './review'
|
|
|
|
export type WizardState = {
|
|
index: number
|
|
kind: 'bulk' | 'assigned'
|
|
items: Item[]
|
|
enrollments: Enrollment[]
|
|
coupon?: Coupon
|
|
installments?: number
|
|
payment_method?: PaymentMethod
|
|
credit_card?: CreditCard
|
|
address?: Address
|
|
}
|
|
|
|
type Summary = {
|
|
subtotal: number
|
|
discount: number
|
|
interest_amount: number
|
|
total: number
|
|
}
|
|
|
|
export type WizardStore = WizardState & {
|
|
setIndex: (index: number) => void
|
|
setKind: (kind: 'bulk' | 'assigned') => void
|
|
update: (data: Partial<WizardState>) => void
|
|
reset: () => void
|
|
summary: () => Summary
|
|
}
|
|
|
|
const emptyWizard: WizardState = {
|
|
index: 0,
|
|
kind: 'bulk',
|
|
items: [],
|
|
enrollments: [],
|
|
address: undefined,
|
|
coupon: undefined,
|
|
installments: undefined,
|
|
payment_method: undefined,
|
|
credit_card: undefined
|
|
}
|
|
|
|
export const useWizardStore = create<WizardStore>()(
|
|
persist(
|
|
(set, get) => ({
|
|
...emptyWizard,
|
|
|
|
setIndex: (index) => set({ index }),
|
|
setKind: (kind) => set({ kind }),
|
|
|
|
summary: () => {
|
|
const { items, coupon, credit_card, installments } = get()
|
|
const subtotal = items.reduce(
|
|
(acc, { course, quantity }) =>
|
|
acc +
|
|
(course.unit_price || 0) *
|
|
(Number.isFinite(quantity) && quantity > 0 ? quantity : 1),
|
|
0
|
|
)
|
|
const discount = coupon
|
|
? applyDiscount(subtotal, coupon.amount, coupon.type) * -1
|
|
: 0
|
|
|
|
const total = subtotal > 0 ? subtotal + discount : 0
|
|
const interest_amount =
|
|
credit_card && typeof installments === 'number' && installments > 1
|
|
? calcInterest(total, installments) - total
|
|
: 0
|
|
|
|
return {
|
|
subtotal,
|
|
discount,
|
|
interest_amount,
|
|
total: total + interest_amount
|
|
}
|
|
},
|
|
|
|
update: (data) =>
|
|
set((state) => {
|
|
if (data.enrollments) {
|
|
const items = Object.values(
|
|
data.enrollments.reduce<Record<string, Item>>(
|
|
(acc, { course }) => {
|
|
const { id } = course
|
|
|
|
acc[id] ??= { course, quantity: 0 }
|
|
acc[id].quantity++
|
|
|
|
return acc
|
|
},
|
|
{}
|
|
)
|
|
)
|
|
|
|
return {
|
|
...state,
|
|
...data,
|
|
items
|
|
}
|
|
}
|
|
|
|
if (data.items) {
|
|
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
|
|
}, {})
|
|
)
|
|
|
|
return {
|
|
...state,
|
|
...data,
|
|
items,
|
|
enrollments: []
|
|
}
|
|
}
|
|
|
|
return {
|
|
...state,
|
|
...data
|
|
}
|
|
}),
|
|
|
|
reset: () => set({ ...emptyWizard })
|
|
}),
|
|
{
|
|
name: 'wizard_cart'
|
|
}
|
|
)
|
|
)
|