feat: enhance ticket order detail and status display
- Updated ticket status display logic to handle "failed" status and improve "settled_win" condition. - Refactored TicketOrderDetailScreen to utilize search parameters for dynamic navigation and grouping of tickets. - Enhanced TicketOrdersListScreen to group ticket items and improve rendering of order details. - Added new translations for order-related terms in multiple languages to support enhanced user experience.
This commit is contained in:
@@ -14,11 +14,16 @@ import { Input } from "@/components/ui/input";
|
||||
import { PlayerPanel } from "@/components/layout/player-panel";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import {
|
||||
groupTicketItems,
|
||||
orderGroupHref,
|
||||
persistOrderGroup,
|
||||
} from "@/features/orders/group-ticket-items";
|
||||
import { OrderMetaLine } from "@/features/orders/order-meta-line";
|
||||
import { StatusDot, ticketStatusDisplay } from "@/features/orders/ticket-item-status";
|
||||
import { useCurrencyCatalog } from "@/hooks/use-currency-catalog";
|
||||
import { useIsMobile } from "@/hooks/use-mobile";
|
||||
import { formatMinorAsCurrency } from "@/lib/money";
|
||||
import { formatLotteryInstant } from "@/lib/player-datetime";
|
||||
import { useActivePlayerCurrency } from "@/hooks/use-active-player-currency";
|
||||
import { playLabel } from "@/lib/play-labels";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -92,6 +97,7 @@ export function TicketOrdersListScreen() {
|
||||
return `${fromDate ? formatCompactDate(fromDate) : "..." } ~ ${toDate ? formatCompactDate(toDate) : "..."}`;
|
||||
}, [formatCompactDate, fromDate, t, toDate]);
|
||||
const visiblePages = useMemo(() => buildPageWindow(page, lastPage), [lastPage, page]);
|
||||
const orderGroups = useMemo(() => groupTicketItems(items), [items]);
|
||||
|
||||
const fetchPage = useCallback(
|
||||
async (nextPage: number, append: boolean) => {
|
||||
@@ -354,49 +360,62 @@ export function TicketOrdersListScreen() {
|
||||
) : (
|
||||
<>
|
||||
<div className="space-y-3">
|
||||
{items.map((row) => {
|
||||
const cur = row.currency_code ?? activeCurrency;
|
||||
const st = ticketStatusDisplay(row.status, row.win_amount, row.jackpot_win_amount, t);
|
||||
const totalWin = row.win_amount + row.jackpot_win_amount;
|
||||
{orderGroups.map((group) => {
|
||||
const cur = group.currency_code ?? activeCurrency;
|
||||
const st = ticketStatusDisplay(
|
||||
group.status,
|
||||
group.win_amount,
|
||||
group.jackpot_win_amount,
|
||||
t,
|
||||
);
|
||||
const totalWin = group.win_amount + group.jackpot_win_amount;
|
||||
return (
|
||||
<Link
|
||||
key={row.ticket_no}
|
||||
href={`/orders/${encodeURIComponent(row.ticket_no)}`}
|
||||
key={group.key}
|
||||
href={orderGroupHref(group)}
|
||||
onClick={() => persistOrderGroup(group)}
|
||||
className="block rounded-xl border border-[#e5edf8] bg-white p-3 shadow-[0_8px_24px_rgba(15,23,42,0.05)] transition-colors hover:border-[#b9ccf6]"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<p className="truncate font-mono text-sm font-black text-[#0b3f96]">
|
||||
{row.draw_no ?? "—"}
|
||||
</p>
|
||||
<p className="mt-1 truncate text-xs text-slate-500">
|
||||
<p className="min-w-0 truncate font-mono text-sm font-black text-[#0b3f96]">
|
||||
{group.draw_no ?? "—"}
|
||||
</p>
|
||||
<StatusDot label={st.label} dotClass={st.dotClass} ring={st.ring} />
|
||||
</div>
|
||||
<OrderMetaLine
|
||||
orderNo={group.order_no}
|
||||
placedAt={group.placed_at}
|
||||
t={t}
|
||||
/>
|
||||
<div className="mt-2.5 space-y-1">
|
||||
{group.items.map((row) => (
|
||||
<p
|
||||
key={row.ticket_no}
|
||||
className="text-sm font-semibold text-[#32518d]"
|
||||
>
|
||||
{playLabel(row.play_code, t)} · {row.original_number ?? row.play_code}
|
||||
</p>
|
||||
</div>
|
||||
<StatusDot label={st.label} dotClass={st.dotClass} ring={st.ring} />
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-3 grid grid-cols-2 gap-2">
|
||||
<div className="rounded-lg bg-[#f8fbff] px-3 py-2">
|
||||
<p className="text-[10px] font-bold uppercase text-[#7890b8]">{t("orders.stake")}</p>
|
||||
<p className="mt-1 text-sm font-black text-slate-900">
|
||||
{formatMinorAsCurrency(row.total_bet_amount, cur)}
|
||||
{formatMinorAsCurrency(group.total_bet_amount, cur)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg bg-[#f8fbff] px-3 py-2">
|
||||
<p className="text-[10px] font-bold uppercase text-[#7890b8]">{t("orders.deduction")}</p>
|
||||
<p className="mt-1 text-sm font-black text-[#0b3f96]">
|
||||
{formatMinorAsCurrency(row.actual_deduct_amount, cur)}
|
||||
{formatMinorAsCurrency(group.actual_deduct_amount, cur)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{totalWin > 0 && row.status === "settled_win" ? (
|
||||
{totalWin > 0 && group.status === "settled_win" ? (
|
||||
<p className="mt-2 text-xs font-bold text-emerald-600">
|
||||
{t("orders.win", { amount: formatMinorAsCurrency(totalWin, cur) })}
|
||||
</p>
|
||||
) : null}
|
||||
<p className="mt-2 text-[11px] text-slate-500">
|
||||
{formatLotteryInstant(row.placed_at ?? null)}
|
||||
</p>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user