diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs index 3fec4b3..4ceadfe 100644 --- a/ecosystem.config.cjs +++ b/ecosystem.config.cjs @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-require-imports */ /** * 玩家端(lotteryfront)宝塔 PM2 配置 * diff --git a/src/components/layout/player-panel.tsx b/src/components/layout/player-panel.tsx index 950c981..39ce2ee 100644 --- a/src/components/layout/player-panel.tsx +++ b/src/components/layout/player-panel.tsx @@ -11,7 +11,6 @@ import { PlayerNotificationBell } from "@/components/layout/player-notification- import { playerHeaderControl, playerPageHeader, - playerPageInset, } from "@/lib/player-spacing"; import { cn } from "@/lib/utils"; @@ -32,7 +31,6 @@ export function PlayerPanel({ className, containerClassName, }: PlayerPanelProps) { - const { t } = useTranslation("common"); const { t: tp } = useTranslation("player"); const resolvedBackLabel = backLabel ?? tp("panel.home"); diff --git a/src/features/hall/hall-betting-grid.tsx b/src/features/hall/hall-betting-grid.tsx index d65cdda..1768b1b 100644 --- a/src/features/hall/hall-betting-grid.tsx +++ b/src/features/hall/hall-betting-grid.tsx @@ -26,7 +26,6 @@ import type { HallDrawLiveSnapshot } from "@/features/hall/use-hall-draw-live"; import { useActivePlayerCurrency } from "@/hooks/use-active-player-currency"; import { triggerWalletPollingAfterBet } from "@/hooks/use-wallet-polling"; import { getLotteryEcho } from "@/lib/lottery-echo"; -import { getLotteryRequestLocale } from "@/lib/lottery-locale"; import { formatMinorAsCurrency, parseDecimalInputToMinor } from "@/lib/money"; import { playLabel } from "@/lib/play-labels"; import { @@ -459,6 +458,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } const [resultOpen, setResultOpen] = useState(false); const [resultData, setResultData] = useState(null); const [quickFillState, setQuickFillState] = useState(() => loadQuickFillState()); + const [riskStateDrawNo, setRiskStateDrawNo] = useState(display?.draw_no ?? null); const [liveSoldOutNumbers, setLiveSoldOutNumbers] = useState>(() => new Set()); const [liveWarningNumbers, setLiveWarningNumbers] = useState>(() => new Set()); const [debouncedSummary, setDebouncedSummary] = useState({ bet: 0, rebate: 0, actual: 0 }); @@ -475,9 +475,9 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } ? crypto.randomUUID() : `pl-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; - const clearPlaceTraceId = () => { + const clearPlaceTraceId = useCallback(() => { placeTraceIdRef.current = null; - }; + }, []); const loadCatalog = useCallback(async () => { setCatalogState((s) => (s.kind === "ok" ? s : { kind: "loading" })); try { @@ -576,34 +576,17 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } const drawNo = display?.draw_no ?? null; - useEffect(() => { + if (riskStateDrawNo !== drawNo) { + setRiskStateDrawNo(drawNo); setLiveSoldOutNumbers(new Set()); setLiveWarningNumbers(new Set()); - }, [drawNo]); + } const alertRows = display?.risk_pool_alerts ?? []; const jackpot = display?.jackpot; const currentQuickFill = quickFillState[activeCategory] ?? { favorites: [], history: [] }; const favorites = currentQuickFill.favorites; const historyNumbers = currentQuickFill.history; - const jackpotPanelCopy = !jackpot?.enabled - ? { - title: t("hall.jackpotPanel.disabledTitle"), - subtitle: t("hall.jackpotPanel.disabledSubtitle"), - description: t("hall.jackpotPanel.disabledDescription"), - } - : isBettable - ? { - title: t("hall.jackpotPanel.infoTitle"), - subtitle: t("hall.jackpotPanel.infoSubtitle"), - description: t("hall.jackpotPanel.infoDescription"), - } - : { - title: t("hall.jackpotPanel.closedInfoTitle"), - subtitle: t("hall.jackpotPanel.closedInfoSubtitle"), - description: t("hall.jackpotPanel.closedInfoDescription"), - }; - const tableDisabled = !isBettable || catalogState.kind !== "ok"; const sealedBetUi = Boolean(display && isHallSealedCountdownUi(display.status)); const defaultNumberPlaceholder = @@ -715,8 +698,18 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } }); }; - const clearAmountsForPlay = useCallback((playCode: string): boolean => { - let removed = false; + const hasAmountsForPlay = useCallback( + (playCode: string): boolean => + rows.some((row) => + Object.entries(row.amounts).some( + ([amountKey, amountValue]) => + amountKey.split("@")[0] === playCode && Boolean(amountValue?.trim()), + ), + ), + [rows], + ); + + const clearAmountsForPlay = useCallback((playCode: string) => { setRows((current) => current.map((row) => { const nextAmounts = { ...row.amounts }; @@ -726,13 +719,10 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } if (keyPlayCode !== playCode || !nextAmounts[amountKey]) return; nextAmounts[amountKey] = ""; changed = true; - removed = true; }); return changed ? { ...row, amounts: nextAmounts } : row; }), ); - - return removed; }, []); useEffect(() => { @@ -743,7 +733,8 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } const onPlayToggle = (evt: PlayToggleWsEvent) => { if (evt.enabled === false && typeof evt.play_code === "string") { - const removed = clearAmountsForPlay(evt.play_code); + const removed = hasAmountsForPlay(evt.play_code); + clearAmountsForPlay(evt.play_code); setPreviewOpen(false); setPreviewData(null); clearPlaceTraceId(); @@ -809,7 +800,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } channel.stopListening(".risk.sold_out"); channel.stopListening(".risk.warning"); }; - }, [clearAmountsForPlay, drawNo, loadCatalog, reloadDraw, t]); + }, [clearAmountsForPlay, clearPlaceTraceId, drawNo, hasAmountsForPlay, reloadDraw, t]); const collectDraftLineIssues = useCallback((): DraftLineIssue[] => { if (activeCategory === "JACKPOT") return []; diff --git a/src/features/orders/ticket-order-detail-screen.tsx b/src/features/orders/ticket-order-detail-screen.tsx index b9dfaf5..8551daf 100644 --- a/src/features/orders/ticket-order-detail-screen.tsx +++ b/src/features/orders/ticket-order-detail-screen.tsx @@ -144,7 +144,7 @@ export function TicketOrderDetailScreen({ ticketNo }: { ticketNo: string }) { window.removeEventListener("lottery-hall-refresh", refresh); window.removeEventListener("lottery-wallet-refresh", refresh); }; - }, [data?.status, load]); + }, [data, load]); if (loading) { return ( diff --git a/src/features/orders/ticket-orders-list-screen.tsx b/src/features/orders/ticket-orders-list-screen.tsx index a1e044b..4787d81 100644 --- a/src/features/orders/ticket-orders-list-screen.tsx +++ b/src/features/orders/ticket-orders-list-screen.tsx @@ -69,7 +69,10 @@ export function TicketOrdersListScreen() { const [loading, setLoading] = useState(true); const [loadingMore, setLoadingMore] = useState(false); const [error, setError] = useState(null); - const [queryDrawNo, setQueryDrawNo] = useState(drawNoFilter); + const [queryDrawNoState, setQueryDrawNoState] = useState(() => ({ + base: drawNoFilter, + draft: drawNoFilter, + })); const [queryNumber, setQueryNumber] = useState(""); const [queryStatuses, setQueryStatuses] = useState(statusFilter); const [fromDate, setFromDate] = useState(""); @@ -82,9 +85,9 @@ export function TicketOrdersListScreen() { const isMobile = useIsMobile(); const initialLoadDone = useRef(false); - useEffect(() => { - setQueryDrawNo(drawNoFilter); - }, [drawNoFilter]); + const queryDrawNoInput = + queryDrawNoState.base === drawNoFilter ? queryDrawNoState.draft : drawNoFilter; + const queryDrawNo = queryDrawNoInput || drawNoFilter; const selectedRange = useMemo(() => { const from = parseSchedulePickerYmd(fromDate); @@ -118,7 +121,7 @@ export function TicketOrdersListScreen() { const res = await getTicketItems({ page: nextPage, per_page: ORDERS_PAGE_SIZE, - draw_no: queryDrawNo || drawNoFilter || undefined, + draw_no: queryDrawNo || undefined, number: queryNumber || undefined, status: queryStatuses.length ? queryStatuses : undefined, start_date: fromDate || undefined, @@ -136,7 +139,7 @@ export function TicketOrdersListScreen() { setLoadingMore(false); } }, - [drawNoFilter, fromDate, queryDrawNo, queryNumber, queryStatuses, t, toDate], + [fromDate, queryDrawNo, queryNumber, queryStatuses, t, toDate], ); useEffect(() => { @@ -219,13 +222,13 @@ export function TicketOrdersListScreen() { > {t("orders.betNow")} - {(queryDrawNo || queryNumber || fromDate || toDate || queryStatuses.length > 0) ? ( + {(queryDrawNoInput || queryNumber || fromDate || toDate || queryStatuses.length > 0) ? (