From e5e64c504ded9bc286f0ba1e1b7f727159daf68c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Rafael=20Siqueira?= Date: Sat, 3 Jan 2026 17:14:15 -0300 Subject: [PATCH] add address --- .../app/routes/orgs/address.py | 4 +- .../app/components/workspace-switcher.tsx | 6 ++ .../_.$orgid.enrollments.buy/discount.tsx | 3 +- .../_.$orgid.enrollments.buy/payment.tsx | 16 +-- .../_.$orgid.enrollments.buy/review.tsx | 98 +++++++++++++------ .../routes/_.$orgid.enrollments.buy/route.tsx | 15 ++- .../app/routes/_.$orgid/route.tsx | 12 +-- 7 files changed, 98 insertions(+), 56 deletions(-) diff --git a/api.saladeaula.digital/app/routes/orgs/address.py b/api.saladeaula.digital/app/routes/orgs/address.py index 05a6b1c..9f806ff 100644 --- a/api.saladeaula.digital/app/routes/orgs/address.py +++ b/api.saladeaula.digital/app/routes/orgs/address.py @@ -19,7 +19,7 @@ dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client) @router.get('//address') def address(org_id: str): return dyn.collection.get_item( - KeyPair(org_id, 'ADDRESS'), + KeyPair(org_id, 'METADATA#ADDRESS'), raise_on_error=False, default={}, ) @@ -44,7 +44,7 @@ class Address(BaseModel): @router.post('//address') def update(org_id: str, address: Annotated[Address, Body(embed=True)]): dyn.collection.put_item( - key=KeyPair(org_id, 'ADDRESS'), + key=KeyPair(org_id, 'METADATA#ADDRESS'), updated_at=now(), **address.model_dump(exclude_none=True), ) diff --git a/apps/admin.saladeaula.digital/app/components/workspace-switcher.tsx b/apps/admin.saladeaula.digital/app/components/workspace-switcher.tsx index 16a6527..f7c2447 100644 --- a/apps/admin.saladeaula.digital/app/components/workspace-switcher.tsx +++ b/apps/admin.saladeaula.digital/app/components/workspace-switcher.tsx @@ -30,6 +30,7 @@ import { initials } from '@repo/ui/lib/utils' import { Link } from 'react-router' import type { Workspace, WorkspaceContextProps } from '@/middleware/workspace' +import type { Address } from '@/routes/_.$orgid.enrollments.buy/review' type Subscription = { billing_day: number @@ -39,6 +40,7 @@ type Subscription = { const WorkspaceContext = createContext< | (WorkspaceContextProps & { subscription: Subscription | null + address: Address | null }) | null >(null) @@ -57,11 +59,13 @@ export function WorkspaceProvider({ activeWorkspace, workspaces, subscription, + address, children }: { activeWorkspace: Workspace workspaces: Workspace[] subscription?: Subscription + address?: Address children: React.ReactNode }) { return ( @@ -69,6 +73,7 @@ export function WorkspaceProvider({ value={{ activeWorkspace, workspaces, + address: address && Object.keys(address).length > 0 ? address : null, subscription: subscription && Object.keys(subscription).length > 0 ? subscription @@ -106,6 +111,7 @@ export function WorkspaceSwitcher() { {subscription && ( )} + {initials(activeWorkspace.name)}
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 7d99a74..8be8cd7 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 @@ -46,6 +46,7 @@ export function Discount({ onChange, ...props }: DiscountProps) { resolver: zodResolver(formSchema) }) const [open, { toggle, set }] = useToggle() + const { handleSubmit, control, formState, setError, reset } = form const { runAsync } = useRequest( async (coupon) => { return await fetch(`/~/api/coupons/${coupon}`, { @@ -54,7 +55,6 @@ export function Discount({ onChange, ...props }: DiscountProps) { }, { manual: true } ) - const { handleSubmit, control, formState, setError, reset } = form const onSubmit = async (data: Schema) => { const r = await runAsync(data.coupon) @@ -62,6 +62,7 @@ export function Discount({ onChange, ...props }: DiscountProps) { if (!r.ok) { return setError('coupon', { message: 'Cupom inválido' }) } + const { sk: code, discount_amount: amount, 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 b506d52..c1ce7ae 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 @@ -33,8 +33,6 @@ import { Currency } from '@repo/ui/components/currency' import { useWizard } from '@/components/wizard' import { isName } from '../_.$orgid.users.add/data' -import type { PaymentMethod } from '@repo/ui/routes/orders/data' -import type { WizardState } from './store' import { applyDiscount } from './discount' import { useWizardStore } from './store' @@ -86,7 +84,8 @@ export type CreditCard = z.infer export function Payment({}) { const wizard = useWizard() - const { update, ...state } = useWizardStore() + const { update, summary, ...state } = useWizardStore() + const { total } = summary() const { control, handleSubmit } = useForm({ defaultValues: { payment_method: state.payment_method, @@ -96,17 +95,6 @@ export function Payment({}) { resolver: zodResolver(formSchema) }) const paymentMethod = useWatch({ control, name: 'payment_method' }) - const subtotal = state.items.reduce( - (acc, { course, quantity }) => - acc + - (course?.unit_price || 0) * - (Number.isFinite(quantity) && quantity > 0 ? quantity : 1), - 0 - ) - const discount = state.coupon - ? applyDiscount(subtotal, state.coupon.amount, state.coupon.type) * -1 - : 0 - const total = subtotal > 0 ? subtotal + discount : 0 const onSubmit = async ({ payment_method, ...data }: Schema) => { if (payment_method === 'CREDIT_CARD') { diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/review.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/review.tsx index 7e702b6..2d1480e 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/review.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/review.tsx @@ -41,7 +41,6 @@ import { import { paymentMethods } from '@repo/ui/routes/orders/data' import { useWizard } from '@/components/wizard' -import { type WizardState } from './store' import { Field, FieldDescription, @@ -59,6 +58,14 @@ import { } from '@repo/ui/components/ui/input-group' import { useWizardStore } from './store' +import { useParams } from 'react-router' +import { + Empty, + EmptyContent, + EmptyDescription, + EmptyHeader, + EmptyTitle +} from '@repo/ui/components/ui/empty' type ReviewProps = { onSubmit: () => void | Promise @@ -66,7 +73,7 @@ type ReviewProps = { export function Review({ onSubmit }: ReviewProps) { const wizard = useWizard() - const { items, summary } = useWizardStore() + const { items, summary, address } = useWizardStore() const { subtotal, discount, interest_amount, total } = summary() const [loading, { set }] = useToggle() @@ -165,7 +172,10 @@ export function Review({ onSubmit }: ReviewProps) { Voltar - @@ -183,10 +193,10 @@ export function Summary() { return ( - - - Endereço de cobrança - {address && ( + {address ? ( + + + Endereço de cobrança
    {address?.address1} {address?.address2 ? <>, {address?.address2} : null} @@ -197,12 +207,38 @@ export function Summary() {
    {formatCEP(address?.postcode)}
- )} -
- - - -
+
+ + + + + +
+ ) : ( + + + Nenhum endereço ainda + + Você ainda não cadastrou nenhum endereço. + + + + + + + + + )} @@ -241,32 +277,36 @@ const formSchema = z.object({ export type Address = z.infer -export function AddressDialog() { - const [open, { toggle, set: setOpen }] = useToggle() +export function AddressDialog({ children }) { const { update, address } = useWizardStore() - const { handleSubmit, control, reset } = useForm({ + const { orgid } = useParams() + const { runAsync } = useRequest( + async (address: Address) => { + await fetch(`/~/api/orgs/${orgid}/address`, { + method: 'POST', + body: JSON.stringify({ address }), + headers: new Headers({ 'Content-Type': 'application/json' }) + }) + }, + { + manual: true + } + ) + const { handleSubmit, control, reset, formState } = useForm({ defaultValues: address, resolver: zodResolver(formSchema) }) + const [open, { toggle, set: setOpen }] = useToggle(address === undefined) const onSubmit = async (address: Address) => { + await runAsync(address) setOpen(false) update({ address }) } return ( - - - + {children} Editar endereço @@ -400,7 +440,9 @@ export function AddressDialog() { - + diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/route.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/route.tsx index 44629c6..9bb615f 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/route.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid.enrollments.buy/route.tsx @@ -34,8 +34,9 @@ import { Payment } from './payment' import { Assigned } from './assigned' import { Review } from './review' -import { useWizardStore, type WizardState } from './store' -import { useState } from 'react' +import { useWizardStore } from './store' +import { useEffect, useState } from 'react' +import { useWorksapce } from '@/components/workspace-switcher' export function meta({}: Route.MetaArgs) { return [{ title: 'Comprar matrículas' }] @@ -65,7 +66,9 @@ export default function Route({ }: Route.ComponentProps) { const fetcher = useFetcher() const [mounted, setMounted] = useState(false) - const { index, kind, setIndex, setKind, reset, ...state } = useWizardStore() + const { address } = useWorksapce() + const { index, kind, setIndex, setKind, reset, update, ...state } = + useWizardStore() const onSubmit = async () => { await fetcher.submit(JSON.stringify(state), { @@ -79,6 +82,12 @@ export default function Route({ setMounted(true) }) + useEffect(() => { + if (address) { + update({ address }) + } + }, [address]) + if (!mounted) { return } diff --git a/apps/admin.saladeaula.digital/app/routes/_.$orgid/route.tsx b/apps/admin.saladeaula.digital/app/routes/_.$orgid/route.tsx index 4bcf3e7..016d894 100644 --- a/apps/admin.saladeaula.digital/app/routes/_.$orgid/route.tsx +++ b/apps/admin.saladeaula.digital/app/routes/_.$orgid/route.tsx @@ -64,14 +64,9 @@ export function shouldRevalidate({ } export default function Route({ loaderData }: Route.ComponentProps) { - const { - user, - activeWorkspace, - workspaces, - sidebar_state, - subscription: subscription_ - } = loaderData - const subscription = use(subscription_) + const { user, activeWorkspace, workspaces, sidebar_state } = loaderData + const subscription = use(loaderData.subscription) + const address = use(loaderData.address) useEffect(() => { if (typeof window !== 'undefined' && window.rybbit) { @@ -88,6 +83,7 @@ export default function Route({ loaderData }: Route.ComponentProps) { activeWorkspace={activeWorkspace} workspaces={workspaces} subscription={subscription} + address={address} >