feat: 项目接口联调
This commit is contained in:
@@ -1,131 +1,246 @@
|
||||
import { useState } from 'react'
|
||||
import {useEffect, useState} from 'react'
|
||||
import {useQuery} from '@tanstack/react-query'
|
||||
|
||||
import PageLayout from '@/components/layout'
|
||||
import {ORDER_STATUS} from '@/constant'
|
||||
import { cn } from '@/lib'
|
||||
import Modal from '@/components/modal'
|
||||
import type { RecordButtonType } from '@/types'
|
||||
import Button from '@/components/button'
|
||||
import type { OrderCardProps, OrderRecord, PointsCardProps, RecordButtonType, TabButtonProps } from '@/types'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { ArrowLeft, ChevronRight, Coins, PackageSearch } from 'lucide-react'
|
||||
import {orders, pointsLogs} from '@/api/business.ts'
|
||||
import {queryKeys} from '@/lib/queryKeys.ts'
|
||||
import { useUserStore } from '@/store/user.ts'
|
||||
import type {OrderItem, PointsLogItem} from '@/types/business.type.ts'
|
||||
|
||||
type OrderRecord = {
|
||||
id: string
|
||||
date: string
|
||||
time: string
|
||||
category: string
|
||||
title: string
|
||||
trackingNumber?: string
|
||||
status: string
|
||||
points: string
|
||||
}
|
||||
|
||||
type PointsRecord = {
|
||||
id: string
|
||||
title: string
|
||||
date: string
|
||||
time: string
|
||||
amount: string
|
||||
tone: 'positive' | 'negative'
|
||||
}
|
||||
|
||||
const orderRecords: OrderRecord[] = [
|
||||
{
|
||||
id: 'order-1',
|
||||
date: '2025-03-04',
|
||||
time: '10:20',
|
||||
category: 'Bonus',
|
||||
title: 'Daily Rebate 50',
|
||||
status: 'Issued',
|
||||
points: '-500 points',
|
||||
},
|
||||
{
|
||||
id: 'order-2',
|
||||
date: '2025-03-03',
|
||||
time: '14:00',
|
||||
category: 'Physical',
|
||||
title: 'Weekly Bonus 200',
|
||||
trackingNumber: 'SF1234567890',
|
||||
status: 'Shipped',
|
||||
points: '-1200 points',
|
||||
},
|
||||
{
|
||||
id: 'order-3',
|
||||
date: '2025-03-02',
|
||||
time: '09:15',
|
||||
category: 'Withdrawal',
|
||||
title: 'Wireless Earbuds',
|
||||
status: 'Issued',
|
||||
points: '-1000 points',
|
||||
},
|
||||
{
|
||||
id: 'order-4',
|
||||
date: '2025-03-01',
|
||||
time: '16:30',
|
||||
category: 'Bonus',
|
||||
title: 'Fitness Tracker',
|
||||
status: 'Pending',
|
||||
points: '-1800 points',
|
||||
},
|
||||
{
|
||||
id: 'order-5',
|
||||
date: '2025-02-28',
|
||||
time: '11:00',
|
||||
category: 'Physical',
|
||||
title: 'Withdraw 100',
|
||||
status: 'Rejected',
|
||||
points: '-2500 points',
|
||||
},
|
||||
]
|
||||
|
||||
const pointsRecords: PointsRecord[] = [
|
||||
{
|
||||
id: 'points-1',
|
||||
title: 'Bonus Redemption - Daily Rewards 50',
|
||||
date: '2025-03-04',
|
||||
time: '10:20',
|
||||
amount: '-500',
|
||||
tone: 'negative',
|
||||
},
|
||||
{
|
||||
id: 'points-2',
|
||||
title: "Claim Yesterday's Protection Funds",
|
||||
date: '2025-03-04',
|
||||
time: '09:20',
|
||||
amount: '+800',
|
||||
tone: 'positive',
|
||||
},
|
||||
{
|
||||
id: 'points-3',
|
||||
title: 'Physical Item Redemption - Bluetooth Headphones',
|
||||
date: '2025-03-03',
|
||||
time: '14:00',
|
||||
amount: '-1200',
|
||||
tone: 'negative',
|
||||
},
|
||||
{
|
||||
id: 'points-4',
|
||||
title: "Claim Yesterday's Protection Funds",
|
||||
date: '2025-03-03',
|
||||
time: '09:00',
|
||||
amount: '+700',
|
||||
tone: 'positive',
|
||||
},
|
||||
{
|
||||
id: 'points-5',
|
||||
title: 'Withdraw to Platform - 100',
|
||||
date: '2025-03-02',
|
||||
time: '09:15',
|
||||
amount: '-1000',
|
||||
tone: 'negative',
|
||||
},
|
||||
]
|
||||
|
||||
const amountToneClassName: Record<PointsRecord['tone'], string> = {
|
||||
const pointsRecordToneClassName = {
|
||||
positive: 'bg-[#9BFFC0] text-[#176640]',
|
||||
negative: 'bg-[#FF9BA4] text-[#7B2634]',
|
||||
} as const
|
||||
|
||||
function formatDatePart(value: number) {
|
||||
return String(value).padStart(2, '0')
|
||||
}
|
||||
|
||||
function getDateTimeParts(value?: number | string) {
|
||||
if (value == null) {
|
||||
return { date: '--', time: '--:--' }
|
||||
}
|
||||
|
||||
const numericValue = typeof value === 'string' && /^\d+$/.test(value) ? Number(value) : value
|
||||
const date = new Date(
|
||||
typeof numericValue === 'number'
|
||||
? String(numericValue).length === 13
|
||||
? numericValue
|
||||
: numericValue * 1000
|
||||
: numericValue,
|
||||
)
|
||||
|
||||
if (Number.isNaN(date.getTime())) {
|
||||
return { date: '--', time: '--:--' }
|
||||
}
|
||||
|
||||
return {
|
||||
date: `${date.getFullYear()}-${formatDatePart(date.getMonth() + 1)}-${formatDatePart(date.getDate())}`,
|
||||
time: `${formatDatePart(date.getHours())}:${formatDatePart(date.getMinutes())}`,
|
||||
}
|
||||
}
|
||||
|
||||
function toTitleCase(value: string) {
|
||||
return value
|
||||
.toLowerCase()
|
||||
.split(/[\s_-]+/)
|
||||
.filter(Boolean)
|
||||
.map((part) => part[0]?.toUpperCase() + part.slice(1))
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
function getOrderStatus(status?: string | number) {
|
||||
if (typeof status === 'string') {
|
||||
const normalizedStatus = status.trim().toUpperCase()
|
||||
const matchedStatus = ORDER_STATUS.find((item) => item === normalizedStatus)
|
||||
if (matchedStatus) {
|
||||
switch (matchedStatus) {
|
||||
case 'PENDING':
|
||||
return 'Pending'
|
||||
case 'COMPLETED':
|
||||
return 'Completed'
|
||||
case 'SHIPPED':
|
||||
return 'Shipped'
|
||||
case 'REJECTED':
|
||||
return 'Rejected'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const normalizedStatus = typeof status === 'string' && /^\d+$/.test(status)
|
||||
? Number(status)
|
||||
: status
|
||||
|
||||
if (typeof normalizedStatus === 'number') {
|
||||
switch (normalizedStatus) {
|
||||
case 0:
|
||||
return 'Pending'
|
||||
case 1:
|
||||
return 'Completed'
|
||||
case 2:
|
||||
return 'Shipped'
|
||||
case 3:
|
||||
return 'Rejected'
|
||||
default:
|
||||
return String(normalizedStatus)
|
||||
}
|
||||
}
|
||||
|
||||
if (!normalizedStatus) {
|
||||
return 'Pending'
|
||||
}
|
||||
|
||||
return toTitleCase(normalizedStatus)
|
||||
}
|
||||
|
||||
function getOrderCategory(item: OrderItem) {
|
||||
if (item.type?.trim()) {
|
||||
return item.type.trim().toUpperCase()
|
||||
}
|
||||
|
||||
if (item.type_title) {
|
||||
return item.type_title
|
||||
}
|
||||
|
||||
if (item.category_title) {
|
||||
return item.category_title
|
||||
}
|
||||
|
||||
if (item.type) {
|
||||
switch (item.type) {
|
||||
case 'BONUS':
|
||||
return 'Bonus'
|
||||
case 'PHYSICAL':
|
||||
return 'Physical'
|
||||
case 'WITHDRAW':
|
||||
return 'Transfer to Platform'
|
||||
default:
|
||||
return toTitleCase(item.type)
|
||||
}
|
||||
}
|
||||
|
||||
if (item.category) {
|
||||
return toTitleCase(item.category)
|
||||
}
|
||||
|
||||
return 'Order'
|
||||
}
|
||||
|
||||
function getOrderPoints(item: OrderItem) {
|
||||
const rawValue = item.points_cost
|
||||
|
||||
if (rawValue == null || rawValue === '') {
|
||||
return {display: '--'}
|
||||
}
|
||||
|
||||
const numericValue = typeof rawValue === 'string' ? Number(rawValue) : rawValue
|
||||
if (Number.isNaN(numericValue)) {
|
||||
const textValue = String(rawValue)
|
||||
return {display: `${textValue} points`}
|
||||
}
|
||||
|
||||
return {
|
||||
display: `${numericValue} points`,
|
||||
}
|
||||
}
|
||||
|
||||
function getTrackingNumber(item: OrderItem) {
|
||||
const shippingNumber = item.shipping_no?.trim()
|
||||
const logisticsNumber = item.logistics_no?.trim()
|
||||
const trackingNumber = item.tracking_no?.trim()
|
||||
const shippingCompany = item.shipping_company?.trim()
|
||||
const resolvedNumber = shippingNumber || logisticsNumber || trackingNumber
|
||||
|
||||
if (!resolvedNumber) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return shippingCompany ? `${shippingCompany} ${resolvedNumber}` : resolvedNumber
|
||||
}
|
||||
|
||||
function mapOrderItemToRecord(item: OrderItem): OrderRecord {
|
||||
const { date, time } = getDateTimeParts(item.create_time ?? item.created_at)
|
||||
const orderNumber = item.external_transaction_id?.trim() || item.order_no ? String(item.external_transaction_id?.trim() || item.order_no) : undefined
|
||||
const points = getOrderPoints(item)
|
||||
|
||||
return {
|
||||
id: String(item.id),
|
||||
orderNumber,
|
||||
date,
|
||||
time,
|
||||
category: getOrderCategory(item),
|
||||
title: item.item_title ?? item.title ?? item.mallItem?.title ?? 'Untitled Order',
|
||||
trackingNumber: getTrackingNumber(item),
|
||||
status: getOrderStatus(item.status),
|
||||
points: points.display,
|
||||
}
|
||||
}
|
||||
|
||||
function getPointsRecordAmount(item: PointsLogItem) {
|
||||
const rawValue = item.points
|
||||
|
||||
if (rawValue == null || rawValue === '') {
|
||||
return {
|
||||
amount: '--',
|
||||
tone: 'negative' as const,
|
||||
}
|
||||
}
|
||||
|
||||
const numericValue = typeof rawValue === 'string' ? Number(rawValue) : rawValue
|
||||
if (Number.isNaN(numericValue)) {
|
||||
const textValue = String(rawValue).trim()
|
||||
return {
|
||||
amount: item.direction === 'IN'
|
||||
? textValue.startsWith('+') ? textValue : `+${textValue.replace(/^[+-]/, '')}`
|
||||
: item.direction === 'OUT'
|
||||
? textValue.startsWith('-') ? textValue : `-${textValue.replace(/^[+-]/, '')}`
|
||||
: textValue,
|
||||
tone: item.direction === 'IN' ? 'positive' as const : 'negative' as const,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
amount: `${item.direction === 'IN' ? '+' : item.direction === 'OUT' ? '-' : ''}${Math.abs(numericValue)}`,
|
||||
tone: item.direction === 'IN' ? 'positive' as const : 'negative' as const,
|
||||
}
|
||||
}
|
||||
|
||||
function getPointsRecordTitle(item: PointsLogItem) {
|
||||
return [
|
||||
item.item_title,
|
||||
item.title,
|
||||
item.mallItem?.title,
|
||||
item.biz_type,
|
||||
item.type,
|
||||
item.description,
|
||||
item.remark?.split('\n')[0],
|
||||
].find((value) => typeof value === 'string' && value.trim())?.trim() ?? 'Points Record'
|
||||
}
|
||||
|
||||
function mapPointsLogItemToRecord(item: PointsLogItem) {
|
||||
const {date, time} = getDateTimeParts(item.ts ?? item.create_time ?? item.created_at ?? item.update_time)
|
||||
const amount = getPointsRecordAmount(item)
|
||||
|
||||
return {
|
||||
id: String(item.id),
|
||||
title: getPointsRecordTitle(item),
|
||||
date,
|
||||
time,
|
||||
amount: amount.amount,
|
||||
tone: amount.tone,
|
||||
}
|
||||
}
|
||||
|
||||
function getOrderStatusClassName(status: string) {
|
||||
switch (status.toLowerCase()) {
|
||||
case 'issued':
|
||||
case 'completed':
|
||||
return 'bg-[#9BFFC0] text-[#176640]'
|
||||
case 'shipped':
|
||||
return 'bg-[#95F0FF] text-[#116A79]'
|
||||
@@ -138,29 +253,19 @@ function getOrderStatusClassName(status: string) {
|
||||
}
|
||||
}
|
||||
|
||||
type TabButtonProps = {
|
||||
active: boolean
|
||||
label: string
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
type OrderCardProps = {
|
||||
record: OrderRecord
|
||||
onOpenDetails: (record: OrderRecord) => void
|
||||
}
|
||||
|
||||
function TabButton({ active, label, onClick }: TabButtonProps) {
|
||||
function TabButton({ active, label, icon: Icon, onClick }: TabButtonProps) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'min-w-[92px] cursor-pointer rounded-[6px] border px-[14px] py-[7px] text-[13px] transition-colors',
|
||||
'inline-flex min-w-[140px] cursor-pointer items-center justify-center gap-[8px] rounded-[10px] border px-[14px] py-[9px] text-[13px] transition-colors focus-visible:ring-2 focus-visible:ring-[#FE9F00] focus-visible:ring-offset-2 focus-visible:ring-offset-[#08070E]',
|
||||
active
|
||||
? 'border-[#F99A0B] bg-linear-to-r from-[#F96C02] to-[#FE9F00] text-white shadow-[0_0_16px_rgba(249,108,2,0.22)]'
|
||||
: 'border-white/35 bg-white/3 text-[#B8B1AA] hover:bg-white/6',
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Icon className="h-[15px] w-[15px]" aria-hidden="true" />
|
||||
{label}
|
||||
</button>
|
||||
)
|
||||
@@ -168,12 +273,12 @@ function TabButton({ active, label, onClick }: TabButtonProps) {
|
||||
|
||||
function OrderCard({ record, onOpenDetails }: OrderCardProps) {
|
||||
return (
|
||||
<div className="overflow-hidden rounded-[10px] shadow-[0_10px_30px_rgba(0,0,0,0.24)]">
|
||||
<div className="bg-linear-to-r from-[#F96C02] to-[#FE9F00] px-[12px] py-[8px] text-[14px] text-white">
|
||||
<div className="overflow-hidden rounded-[12px] shadow-[0_10px_30px_rgba(0,0,0,0.24)]">
|
||||
<div className="bg-linear-to-r from-[#F96C02] to-[#FE9F00] px-[12px] py-[9px] text-[13px] text-white sm:text-[14px]">
|
||||
{record.date} {record.time} • {record.category}
|
||||
</div>
|
||||
<div className="liquid-glass-bg !rounded-t-none flex items-center justify-between px-[12px] py-[12px]">
|
||||
<div className="min-w-0 flex-1 pr-[16px]">
|
||||
<div className="liquid-glass-bg !rounded-t-none flex flex-col gap-[14px] px-[12px] py-[14px] sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="text-[16px] font-medium text-white">{record.title}</div>
|
||||
{record.trackingNumber ? (
|
||||
<div className="mt-[4px] text-[13px] text-white/45">
|
||||
@@ -182,14 +287,15 @@ function OrderCard({ record, onOpenDetails }: OrderCardProps) {
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
className="mt-[8px] text-[13px] text-[#FA6A00]"
|
||||
className="mt-[10px] inline-flex items-center gap-[5px] text-[13px] text-[#FA6A00] focus-visible:ring-2 focus-visible:ring-[#FE9F00] focus-visible:ring-offset-2 focus-visible:ring-offset-[#08070E]"
|
||||
onClick={() => onOpenDetails(record)}
|
||||
>
|
||||
Check the details
|
||||
<ChevronRight className="h-[14px] w-[14px]" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 flex-col items-end gap-[10px]">
|
||||
<div className="flex shrink-0 items-center justify-between gap-[10px] sm:flex-col sm:items-end">
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-[6px] px-[8px] py-[3px] text-[11px] leading-none',
|
||||
@@ -198,27 +304,27 @@ function OrderCard({ record, onOpenDetails }: OrderCardProps) {
|
||||
>
|
||||
{record.status}
|
||||
</div>
|
||||
<div className="text-[13px] text-white/85">{record.points}</div>
|
||||
<div className="text-[13px] text-white/85">{record.points.replace(/^-/, '')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function PointsCard({ record }: { record: PointsRecord }) {
|
||||
function PointsCard({ record }: PointsCardProps) {
|
||||
return (
|
||||
<div className="overflow-hidden rounded-[10px] shadow-[0_10px_30px_rgba(0,0,0,0.24)]">
|
||||
<div className="bg-linear-to-r from-[#F96C02] to-[#FE9F00] px-[12px] py-[8px] text-[15px] text-white">
|
||||
<div className="overflow-hidden rounded-[12px] shadow-[0_10px_30px_rgba(0,0,0,0.24)]">
|
||||
<div className="bg-linear-to-r from-[#F96C02] to-[#FE9F00] px-[12px] py-[9px] text-[14px] text-white sm:text-[15px]">
|
||||
{record.title}
|
||||
</div>
|
||||
<div className="liquid-glass-bg !rounded-t-none flex items-center justify-between px-[12px] py-[22px]">
|
||||
<div className="liquid-glass-bg !rounded-t-none flex flex-col gap-[10px] px-[12px] py-[18px] sm:flex-row sm:items-center sm:justify-between sm:py-[22px]">
|
||||
<div className="text-[13px] text-white/40">
|
||||
{record.date} {record.time}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-[6px] px-[10px] py-[3px] text-[12px] leading-none',
|
||||
amountToneClassName[record.tone],
|
||||
'inline-flex w-fit rounded-[6px] px-[10px] py-[3px] text-[12px] leading-none',
|
||||
pointsRecordToneClassName[record.tone],
|
||||
)}
|
||||
>
|
||||
{record.amount}
|
||||
@@ -228,7 +334,92 @@ function PointsCard({ record }: { record: PointsRecord }) {
|
||||
)
|
||||
}
|
||||
|
||||
function OrdersTabContent({
|
||||
sessionId,
|
||||
onOpenDetails,
|
||||
}: {
|
||||
sessionId: string
|
||||
onOpenDetails: (record: OrderRecord) => void
|
||||
}) {
|
||||
const ordersQuery = useQuery({
|
||||
queryKey: queryKeys.orders(sessionId),
|
||||
enabled: Boolean(sessionId),
|
||||
gcTime: 0,
|
||||
queryFn: async () => {
|
||||
const response = await orders({
|
||||
session_id: sessionId,
|
||||
})
|
||||
|
||||
return (response.data.list ?? []).map(mapOrderItemToRecord)
|
||||
},
|
||||
})
|
||||
const orderRecords = ordersQuery.data ?? []
|
||||
|
||||
if (ordersQuery.isPending) {
|
||||
return (
|
||||
<div className="liquid-glass-bg px-[16px] py-[18px] text-[14px] text-white/60">
|
||||
Loading...
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!orderRecords.length) {
|
||||
return (
|
||||
<div className="liquid-glass-bg px-[16px] py-[18px] text-[14px] text-white/60">
|
||||
No Data
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{orderRecords.map((record) => (
|
||||
<OrderCard key={record.id} record={record} onOpenDetails={onOpenDetails} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function PointsTabContent({sessionId}: {sessionId: string}) {
|
||||
const pointsLogsQuery = useQuery({
|
||||
queryKey: queryKeys.pointsLogs(sessionId),
|
||||
enabled: Boolean(sessionId),
|
||||
gcTime: 0,
|
||||
queryFn: async () => {
|
||||
const response = await pointsLogs({
|
||||
session_id: sessionId,
|
||||
})
|
||||
|
||||
return (response.data.list ?? []).map(mapPointsLogItemToRecord)
|
||||
},
|
||||
})
|
||||
const pointsRecords = pointsLogsQuery.data ?? []
|
||||
|
||||
if (pointsLogsQuery.isPending) {
|
||||
return (
|
||||
<div className="liquid-glass-bg px-[16px] py-[18px] text-[14px] text-white/60">
|
||||
Loading...
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!pointsRecords.length) {
|
||||
return (
|
||||
<div className="liquid-glass-bg px-[16px] py-[18px] text-[14px] text-white/60">
|
||||
No Data
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{pointsRecords.map((record) => <PointsCard key={record.id} record={record} />)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function RecordPage() {
|
||||
const sessionId = useUserStore((state) => state.authInfo?.session_id ?? '')
|
||||
const [tab, setTab] = useState<RecordButtonType>('order')
|
||||
const [selectedOrder, setSelectedOrder] = useState<OrderRecord | null>(null)
|
||||
|
||||
@@ -236,30 +427,50 @@ function RecordPage() {
|
||||
setSelectedOrder(null)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedOrder(null)
|
||||
}, [tab])
|
||||
|
||||
return (
|
||||
<PageLayout contentClassName="min-h-screen">
|
||||
<PageLayout contentClassName="mx-auto flex min-h-screen w-full max-w-[980px] flex-col px-4 pb-8 sm:px-6 lg:px-8">
|
||||
<Link
|
||||
to="/"
|
||||
className="relative flex h-[40px] w-full items-center justify-center bg-[#08070E]/70 text-[#F56E10]"
|
||||
className="mt-[12px] flex h-[44px] items-center justify-between rounded-[12px] bg-[#08070E]/72 px-[14px] text-[#F56E10] transition-colors hover:bg-[#0D0A14]/80 focus-visible:ring-2 focus-visible:ring-[#FE9F00] focus-visible:ring-offset-2 focus-visible:ring-offset-[#08070E] sm:mt-[16px]"
|
||||
>
|
||||
<div className="absolute left-[16px]"><</div>
|
||||
<div>Record</div>
|
||||
<div className="flex items-center gap-[8px]">
|
||||
<ArrowLeft className="h-[16px] w-[16px]" aria-hidden="true" />
|
||||
<span className="text-[14px] font-medium text-white/92">Back</span>
|
||||
</div>
|
||||
<div className="text-[15px] font-semibold text-[#F56E10]">Record</div>
|
||||
<div className="w-[52px]"></div>
|
||||
</Link>
|
||||
|
||||
<div className="mx-auto w-[60%] pt-[14px] pb-[24px]">
|
||||
<div className="flex gap-[8px]">
|
||||
<TabButton active={tab === 'order'} label="My Orders" onClick={() => setTab('order')} />
|
||||
<TabButton active={tab === 'record'} label="Points Record" onClick={() => setTab('record')} />
|
||||
<div className="mx-auto w-full max-w-[860px] pt-[18px] pb-[24px]">
|
||||
<div className="mb-[12px] flex justify-end">
|
||||
<div className="flex gap-[8px]">
|
||||
<TabButton
|
||||
active={tab === 'order'}
|
||||
icon={PackageSearch}
|
||||
label="My Orders"
|
||||
onClick={() => setTab('order')}
|
||||
/>
|
||||
<TabButton
|
||||
active={tab === 'record'}
|
||||
icon={Coins}
|
||||
label="Points Record"
|
||||
onClick={() => setTab('record')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-[10px] border-t border-white/20"></div>
|
||||
<div className="h-px bg-white/16"></div>
|
||||
|
||||
<div className="mt-[12px] flex flex-col gap-[12px]">
|
||||
{tab === 'order'
|
||||
? orderRecords.map((record) => (
|
||||
<OrderCard key={record.id} record={record} onOpenDetails={setSelectedOrder} />
|
||||
))
|
||||
: pointsRecords.map((record) => <PointsCard key={record.id} record={record} />)}
|
||||
<div className="mt-[14px] flex flex-col gap-[12px]">
|
||||
{tab === 'order' ? (
|
||||
<OrdersTabContent key="order" sessionId={sessionId} onOpenDetails={setSelectedOrder} />
|
||||
) : (
|
||||
<PointsTabContent key="record" sessionId={sessionId} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -267,22 +478,25 @@ function RecordPage() {
|
||||
open={Boolean(selectedOrder)}
|
||||
title="Order Details"
|
||||
onClose={handleCloseDetails}
|
||||
className="max-w-[380px]"
|
||||
className="max-w-[420px]"
|
||||
bodyClassName="pt-[0px]"
|
||||
footer={
|
||||
<button type="button" className="button-play h-[36px] min-w-[94px]" onClick={handleCloseDetails}>
|
||||
<Button type="button" className="h-[36px] w-full sm:min-w-[94px] sm:w-auto" onClick={handleCloseDetails}>
|
||||
Close
|
||||
</button>
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
{selectedOrder ? (
|
||||
<div className="rounded-[8px] bg-[#1C1818]/78 px-[12px] py-[6px]">
|
||||
<div className="rounded-[10px] bg-[#1C1818]/78 px-[12px] py-[6px]">
|
||||
{[
|
||||
{ label: 'Order Number', value: `ORD${selectedOrder.date.replaceAll('-', '')}${selectedOrder.time.replace(':', '')}001` },
|
||||
{ label: 'Order Number', value: selectedOrder.orderNumber ?? '--' },
|
||||
{ label: 'Order Time', value: `${selectedOrder.date} ${selectedOrder.time}` },
|
||||
{ label: 'Order Type', value: selectedOrder.category },
|
||||
{ label: 'Item Name', value: selectedOrder.title },
|
||||
{ label: 'Points', value: selectedOrder.points.replace('-', '') },
|
||||
...(selectedOrder.trackingNumber
|
||||
? [{ label: 'Tracking Number', value: selectedOrder.trackingNumber }]
|
||||
: []),
|
||||
{ label: 'Points', value: selectedOrder.points.replace(/^-/, '') },
|
||||
{ label: 'Status', value: selectedOrder.status },
|
||||
].map((item) => (
|
||||
<div key={item.label} className="border-b border-white/8 py-[10px] last:border-b-0">
|
||||
|
||||
Reference in New Issue
Block a user