add payment details page

This commit is contained in:
2026-01-13 04:29:23 -03:00
parent 7e09e49ed8
commit 091499399f
6 changed files with 82 additions and 7 deletions

View File

@@ -15,7 +15,7 @@ import {
export function meta() { export function meta() {
return [ return [
{ {
title: 'Pagamento' title: 'Detalhes do pagamento'
} }
] ]
} }
@@ -46,7 +46,7 @@ export default function Route({ loaderData: { order } }: Route.ComponentProps) {
</BreadcrumbItem> </BreadcrumbItem>
<BreadcrumbSeparator /> <BreadcrumbSeparator />
<BreadcrumbItem> <BreadcrumbItem>
<BreadcrumbPage>Pagamento</BreadcrumbPage> <BreadcrumbPage>Detalhes do pagamento</BreadcrumbPage>
</BreadcrumbItem> </BreadcrumbItem>
</BreadcrumbList> </BreadcrumbList>
</Breadcrumb> </Breadcrumb>

View File

@@ -0,0 +1,56 @@
import type { Route } from './+types/route'
import { Link } from 'react-router'
import { request as req } from '@repo/util/request'
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator
} from '@repo/ui/components/ui/breadcrumb'
export function meta() {
return [
{
title: 'Detalhes do pagamento'
}
]
}
export async function loader({ params, request, context }: Route.LoaderArgs) {
const r = await req({
url: `/orders/${params.id}`,
request,
context
})
if (!r.ok) {
throw new Response(null, { status: r.status })
}
return { order: await r.json() }
}
export default function Route({ loaderData: { order } }: Route.ComponentProps) {
return (
<div className="space-y-2.5">
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink asChild>
<Link to="../payments">Pagamentos</Link>
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem>
<BreadcrumbPage>Detalhes do pagamento</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
<pre>{JSON.stringify(order, null, 2)}</pre>
</div>
)
}

View File

@@ -1,13 +1,15 @@
'use client' 'use client'
import { type ColumnDef } from '@tanstack/react-table' import { type ColumnDef } from '@tanstack/react-table'
import { EllipsisIcon } from 'lucide-react' import { EllipsisIcon, ReceiptTextIcon } from 'lucide-react'
import { Abbr } from '@repo/ui/components/abbr' import { Abbr } from '@repo/ui/components/abbr'
import { Button } from '@repo/ui/components/ui/button' import { Button } from '@repo/ui/components/ui/button'
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger DropdownMenuTrigger
} from '@repo/ui/components/ui/dropdown-menu' } from '@repo/ui/components/ui/dropdown-menu'
import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar' import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar'
@@ -20,6 +22,8 @@ import {
import { columns as columns_, type Order } from '@repo/ui/routes/orders/columns' import { columns as columns_, type Order } from '@repo/ui/routes/orders/columns'
import { CopyToClipboardItem } from '../_app.users._index/columns' import { CopyToClipboardItem } from '../_app.users._index/columns'
import { Spinner } from '@repo/ui/components/ui/spinner'
import { NavLink } from 'react-router'
export type { Order } export type { Order }
@@ -83,6 +87,17 @@ function ActionMenu({ row }: { row: any }) {
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-36 *:cursor-pointer"> <DropdownMenuContent align="end" className="w-36 *:cursor-pointer">
<DropdownMenuItem asChild onSelect={(e) => e.preventDefault()}>
<NavLink to={`${row.id}`}>
{({ isPending }) => (
<>
{isPending ? <Spinner /> : <ReceiptTextIcon />}
Detalhes
</>
)}
</NavLink>
</DropdownMenuItem>
<DropdownMenuSeparator />
<CopyToClipboardItem text={row.id} /> <CopyToClipboardItem text={row.id} />
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>

View File

@@ -30,11 +30,14 @@ class InvoiceNotFoundError(NotFoundError): ...
class StatusAttr(Enum): class StatusAttr(Enum):
PAID = 'paid_at' # Post-migration (orders): uncomment the following lines
# PAID = 'paid_at'
# EXTERNALLY_PAID = 'paid_at'
EXTERNALLY_PAID = 'payment_date'
PAID = 'payment_date'
CANCELED = 'canceled_at' CANCELED = 'canceled_at'
REFUNDED = 'refunded_at' REFUNDED = 'refunded_at'
EXPIRED = 'expired_at' EXPIRED = 'expired_at'
EXTERNALLY_PAID = 'paid_at'
def _status_attr(status: str) -> StatusAttr | None: def _status_attr(status: str) -> StatusAttr | None:

View File

@@ -62,8 +62,9 @@ def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
if charge['success'] is True: if charge['success'] is True:
transact.update( transact.update(
key=KeyPair(order_id, '0'), key=KeyPair(order_id, '0'),
# Post-migration (orders): rename `payment_date` to `paid_at`
update_expr='SET #status = :status, \ update_expr='SET #status = :status, \
paid_at = :now, \ payment_date = :now, \
updated_at = :now', updated_at = :now',
expr_attr_names={ expr_attr_names={
'#status': 'status', '#status': 'status',

View File

@@ -55,7 +55,7 @@ export const columns: ColumnDef<Order>[] = [
{ {
accessorKey: 'due_date', accessorKey: 'due_date',
enableSorting: true, enableSorting: true,
meta: { title: 'Vencimento em' }, meta: { title: 'Vence em' },
header: DataTableColumnHeaderSort, header: DataTableColumnHeaderSort,
cell: DataTableColumnDatetime cell: DataTableColumnDatetime
}, },