feat: 增强大厅与结果展示功能
- 在 .env.example 中新增可选配置项 NEXT_PUBLIC_LOTTERY_PLAY_CURRENCY - 在 API 模块中导出 getPlayEffective 函数 - 在 HallScreen 组件中引入 HallPlayCatalogPanel 以展示玩法目录 - 在多个屏幕组件中使用 queueMicrotask 优化数据加载逻辑 - 在 lottery-locale.ts 中新增 getLotteryRequestLocale 函数以支持语言选择 - 在类型定义中新增与玩法相关的类型导出
This commit is contained in:
@@ -10,5 +10,8 @@ LOTTERY_API_PROXY_TARGET=http://127.0.0.1:8000
|
|||||||
# 一般本地开发建议留空,让请求走同源 /api 代理,避免 CORS。
|
# 一般本地开发建议留空,让请求走同源 /api 代理,避免 CORS。
|
||||||
# NEXT_PUBLIC_LOTTERY_API_BASE_URL=http://127.0.0.1:8000
|
# NEXT_PUBLIC_LOTTERY_API_BASE_URL=http://127.0.0.1:8000
|
||||||
|
|
||||||
|
# 可选:大厅「玩法与赔率」接口 `/api/v1/play/effective` 的 ?currency=(如 NPR);不设则由后端选默认可下注币种。
|
||||||
|
# NEXT_PUBLIC_LOTTERY_PLAY_CURRENCY=NPR
|
||||||
|
|
||||||
# 可选:入口授权失败时“返回主站重新进入”的地址。
|
# 可选:入口授权失败时“返回主站重新进入”的地址。
|
||||||
# NEXT_PUBLIC_MAIN_SITE_URL=http://localhost:5173
|
# NEXT_PUBLIC_MAIN_SITE_URL=http://localhost:5173
|
||||||
@@ -7,6 +7,7 @@ export {
|
|||||||
getDrawResultByNo,
|
getDrawResultByNo,
|
||||||
type GetDrawResultsParams,
|
type GetDrawResultsParams,
|
||||||
} from "@/api/draw";
|
} from "@/api/draw";
|
||||||
|
export { getPlayEffective, type GetPlayEffectiveParams } from "@/api/play";
|
||||||
export {
|
export {
|
||||||
getWalletBalance,
|
getWalletBalance,
|
||||||
getWalletLogs,
|
getWalletLogs,
|
||||||
|
|||||||
26
src/api/play.ts
Normal file
26
src/api/play.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { lotteryRequest } from "@/lib/lottery-http";
|
||||||
|
import { API_V1_PREFIX } from "@/api/paths";
|
||||||
|
import type { PlayEffectivePayload } from "@/types/api/play-effective";
|
||||||
|
|
||||||
|
export type GetPlayEffectiveParams = {
|
||||||
|
/** 不传则后端取首个可下注币种(通常为 NPR) */
|
||||||
|
currency?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `GET /api/v1/play/effective`(公开;无需登录)。
|
||||||
|
* 对齐阶段 4:生效玩法目录 + 赔率快照 + 封顶样本。
|
||||||
|
*/
|
||||||
|
export function getPlayEffective(
|
||||||
|
params?: GetPlayEffectiveParams,
|
||||||
|
): Promise<PlayEffectivePayload> {
|
||||||
|
return lotteryRequest.get<PlayEffectivePayload>(
|
||||||
|
`${API_V1_PREFIX}/play/effective`,
|
||||||
|
{
|
||||||
|
params:
|
||||||
|
params?.currency !== undefined && params.currency !== ""
|
||||||
|
? { currency: params.currency }
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
279
src/features/hall/hall-play-catalog-panel.tsx
Normal file
279
src/features/hall/hall-play-catalog-panel.tsx
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
|
import { getPlayEffective } from "@/api/play";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { getLotteryRequestLocale } from "@/lib/lottery-locale";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { LotteryApiBizError } from "@/types/api/errors";
|
||||||
|
import type {
|
||||||
|
PlayEffectivePayload,
|
||||||
|
PlayEffectivePlayRow,
|
||||||
|
} from "@/types/api/play-effective";
|
||||||
|
|
||||||
|
const DEFAULT_POLL_MS = 120_000;
|
||||||
|
|
||||||
|
function pickDisplayName(row: PlayEffectivePlayRow): string {
|
||||||
|
const loc = getLotteryRequestLocale();
|
||||||
|
if (loc === "zh") {
|
||||||
|
return row.display_name_zh ?? row.display_name_en ?? row.play_code;
|
||||||
|
}
|
||||||
|
if (loc === "ne") {
|
||||||
|
return row.display_name_ne ?? row.display_name_en ?? row.play_code;
|
||||||
|
}
|
||||||
|
return row.display_name_en ?? row.display_name_zh ?? row.play_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickRuleText(row: PlayEffectivePlayRow): string | null {
|
||||||
|
const c = row.config;
|
||||||
|
if (!c) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const loc = getLotteryRequestLocale();
|
||||||
|
if (loc === "zh") {
|
||||||
|
return c.rule_text_zh ?? c.rule_text_en;
|
||||||
|
}
|
||||||
|
if (loc === "ne") {
|
||||||
|
return c.rule_text_ne ?? c.rule_text_en;
|
||||||
|
}
|
||||||
|
return c.rule_text_en ?? c.rule_text_zh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 主数据开关 + 配置版本内开关,同时有 config 行才算对客开放 */
|
||||||
|
function isPlayOpenForPlayer(row: PlayEffectivePlayRow): boolean {
|
||||||
|
if (!row.master_enabled || row.config === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return row.config.is_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoadState =
|
||||||
|
| { kind: "loading" }
|
||||||
|
| { kind: "ok"; data: PlayEffectivePayload }
|
||||||
|
| { kind: "error"; message: string; notReady?: boolean };
|
||||||
|
|
||||||
|
function formatMoneyAmount(n: number): string {
|
||||||
|
return new Intl.NumberFormat(undefined, { maximumFractionDigits: 0 }).format(
|
||||||
|
n,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function HallPlayCatalogPanel() {
|
||||||
|
const [state, setState] = useState<LoadState>({ kind: "loading" });
|
||||||
|
const currencyParam = useMemo(() => {
|
||||||
|
const fromEnv = process.env.NEXT_PUBLIC_LOTTERY_PLAY_CURRENCY?.trim();
|
||||||
|
return fromEnv !== undefined && fromEnv !== "" ? fromEnv : undefined;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const load = useCallback(async () => {
|
||||||
|
setState((s) => (s.kind === "ok" ? s : { kind: "loading" }));
|
||||||
|
try {
|
||||||
|
const data = await getPlayEffective(
|
||||||
|
currencyParam !== undefined ? { currency: currencyParam } : undefined,
|
||||||
|
);
|
||||||
|
setState({ kind: "ok", data });
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof LotteryApiBizError && e.code === 9004) {
|
||||||
|
setState({
|
||||||
|
kind: "error",
|
||||||
|
message:
|
||||||
|
"玩法配置尚未初始化。请在 Laravel 执行含 OperationalConfigV1Seeder 的 seed。",
|
||||||
|
notReady: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const msg =
|
||||||
|
e instanceof LotteryApiBizError
|
||||||
|
? e.message
|
||||||
|
: "加载玩法配置失败,请稍后重试。";
|
||||||
|
setState({ kind: "error", message: msg });
|
||||||
|
}
|
||||||
|
}, [currencyParam]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
queueMicrotask(() => {
|
||||||
|
void load();
|
||||||
|
});
|
||||||
|
}, [load]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const id = window.setInterval(() => {
|
||||||
|
void load();
|
||||||
|
}, DEFAULT_POLL_MS);
|
||||||
|
return () => window.clearInterval(id);
|
||||||
|
}, [load]);
|
||||||
|
|
||||||
|
const body = (() => {
|
||||||
|
if (state.kind === "loading") {
|
||||||
|
return (
|
||||||
|
<p className="text-sm text-muted-foreground">加载玩法与赔率…</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (state.kind === "error") {
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<p className="text-sm text-destructive">{state.message}</p>
|
||||||
|
<Button type="button" size="sm" variant="secondary" onClick={() => void load()}>
|
||||||
|
重试
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = state;
|
||||||
|
const ordered = [...data.plays].sort(
|
||||||
|
(a, b) => a.sort_order - b.sort_order || a.play_code.localeCompare(b.play_code),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
币种 {data.currency_code} · 配置版本 play#{data.effective_versions.play_config.version_no}
|
||||||
|
/odds#{data.effective_versions.odds.version_no} · 限额单位为最小货币单位(与钱包一致)。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="overflow-x-auto rounded-md border">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead className="min-w-[140px]">玩法</TableHead>
|
||||||
|
<TableHead className="w-[88px] text-center">状态</TableHead>
|
||||||
|
<TableHead className="min-w-[160px] whitespace-nowrap">下注限额</TableHead>
|
||||||
|
<TableHead className="min-w-[100px]">赔率×</TableHead>
|
||||||
|
<TableHead className="min-w-[200px]">说明</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{ordered.map((row) => {
|
||||||
|
const open = isPlayOpenForPlayer(row);
|
||||||
|
const rule = pickRuleText(row);
|
||||||
|
const oddsMul =
|
||||||
|
row.odds === null
|
||||||
|
? "—"
|
||||||
|
: typeof row.odds.odds_multiplier === "number"
|
||||||
|
? row.odds.odds_multiplier.toFixed(4)
|
||||||
|
: (row.odds.odds_value / 10000).toFixed(4);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
key={row.play_code}
|
||||||
|
className={cn(!open && "opacity-60")}
|
||||||
|
>
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex flex-col gap-0.5">
|
||||||
|
<span className="font-medium">{pickDisplayName(row)}</span>
|
||||||
|
<span className="font-mono text-xs text-muted-foreground">
|
||||||
|
{row.play_code}
|
||||||
|
{row.dimension != null ? ` · ${row.dimension}D` : ""}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-center">
|
||||||
|
{open ? (
|
||||||
|
<Badge variant="default" className="font-normal">
|
||||||
|
开放
|
||||||
|
</Badge>
|
||||||
|
) : (
|
||||||
|
<Badge variant="secondary" className="font-normal">
|
||||||
|
关闭
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="font-mono text-sm tabular-nums">
|
||||||
|
{row.config ? (
|
||||||
|
<>
|
||||||
|
{formatMoneyAmount(row.config.min_bet_amount)}
|
||||||
|
{" — "}
|
||||||
|
{formatMoneyAmount(row.config.max_bet_amount)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
"—"
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="font-mono text-sm tabular-nums">
|
||||||
|
{row.odds ? oddsMul : "—"}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-muted-foreground text-sm leading-snug">
|
||||||
|
{rule ?? "—"}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{data.risk_cap_items.length > 0 ? (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<h3 className="text-sm font-medium text-foreground">风控封顶(示例号码)</h3>
|
||||||
|
<div className="overflow-x-auto rounded-md border">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>号码</TableHead>
|
||||||
|
<TableHead>封顶额</TableHead>
|
||||||
|
<TableHead>类型</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{data.risk_cap_items.map((r, i) => (
|
||||||
|
<TableRow key={`${r.normalized_number}-${i}`}>
|
||||||
|
<TableCell className="font-mono">{r.normalized_number}</TableCell>
|
||||||
|
<TableCell className="font-mono tabular-nums text-sm">
|
||||||
|
{formatMoneyAmount(r.cap_amount)}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-sm text-muted-foreground">
|
||||||
|
{r.cap_type}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<CardTitle className="text-base">玩法与赔率</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
数据来自 <code className="text-xs">GET /api/v1/play/effective</code>
|
||||||
|
;后台修改并发布后,最长约 {DEFAULT_POLL_MS / 1000}s 内自动刷新(亦可手动刷新)。
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="shrink-0"
|
||||||
|
onClick={() => void load()}
|
||||||
|
>
|
||||||
|
刷新配置
|
||||||
|
</Button>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>{body}</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -8,11 +8,12 @@ import {
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
|
|
||||||
|
import { HallPlayCatalogPanel } from "@/features/hall/hall-play-catalog-panel";
|
||||||
import { HallWalletStrip } from "@/features/hall/hall-wallet-strip";
|
import { HallWalletStrip } from "@/features/hall/hall-wallet-strip";
|
||||||
import { HallDrawPanel } from "@/features/hall/hall-draw-panel";
|
import { HallDrawPanel } from "@/features/hall/hall-draw-panel";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下注大厅:钱包条 §4 + 当期期号 §4.2;表格与封盘态见 docs/06 §11.7、§13.3。
|
* 下注大厅:钱包条 §4 + 当期期号 §4.2;玩法目录阶段 4(§12.3);下注表格阶段 5(§13.3)。
|
||||||
*/
|
*/
|
||||||
export function HallScreen() {
|
export function HallScreen() {
|
||||||
return (
|
return (
|
||||||
@@ -20,17 +21,18 @@ export function HallScreen() {
|
|||||||
<HallWalletStrip />
|
<HallWalletStrip />
|
||||||
<HallDrawPanel />
|
<HallDrawPanel />
|
||||||
|
|
||||||
|
<HallPlayCatalogPanel />
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-base">下注表格</CardTitle>
|
<CardTitle className="text-base">下注表格</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
2D / 3D / 4D 动态列在阶段 5 接入玩法配置后按界面 §4.2 渲染(实施计划 docs/06
|
阶段 5:按玩法配置动态渲染 2D / 3D / 4D 下注格;封盘整表置灰与「已封盘」按钮见实施计划
|
||||||
§13.3「承接阶段 3」)。
|
docs/06 §13.3、§16.2。
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="text-sm text-muted-foreground">
|
<CardContent className="text-sm text-muted-foreground">
|
||||||
封盘整表置灰、按钮「已封盘」与 WebSocket 倒计时见 docs/06 §11.7 表、§13.3、§16.2
|
当前已展示开放玩法、限额与赔率快照;真实下注与售罄校验将在阶段 5 接入。
|
||||||
第二轮。
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ export function DrawResultDetailScreen({ drawNo }: DrawResultDetailScreenProps)
|
|||||||
}, [drawNo]);
|
}, [drawNo]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void load();
|
queueMicrotask(() => {
|
||||||
|
void load();
|
||||||
|
});
|
||||||
}, [load]);
|
}, [load]);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
|
|||||||
@@ -44,7 +44,9 @@ export function DrawResultsListScreen() {
|
|||||||
}, [date]);
|
}, [date]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void fetchList();
|
queueMicrotask(() => {
|
||||||
|
void fetchList();
|
||||||
|
});
|
||||||
}, [fetchList]);
|
}, [fetchList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -58,7 +58,9 @@ export function WalletLogsScreen() {
|
|||||||
}, [filter]);
|
}, [filter]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void load();
|
queueMicrotask(() => {
|
||||||
|
void load();
|
||||||
|
});
|
||||||
}, [load]);
|
}, [load]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ function isLotteryLocale(value: string): value is LotteryLocale {
|
|||||||
return value === "zh" || value === "en" || value === "ne";
|
return value === "zh" || value === "en" || value === "ne";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 供前端展示文案选用语言(与请求头 `X-Locale` 逻辑一致)。 */
|
||||||
|
export function getLotteryRequestLocale(): LotteryLocale {
|
||||||
|
return requestLocale();
|
||||||
|
}
|
||||||
|
|
||||||
function requestLocale(): LotteryLocale {
|
function requestLocale(): LotteryLocale {
|
||||||
if (overrideLocale) {
|
if (overrideLocale) {
|
||||||
return overrideLocale;
|
return overrideLocale;
|
||||||
|
|||||||
@@ -16,3 +16,11 @@ export type {
|
|||||||
WalletLogsData,
|
WalletLogsData,
|
||||||
WalletPendingTransfer,
|
WalletPendingTransfer,
|
||||||
} from "./wallet-logs";
|
} from "./wallet-logs";
|
||||||
|
export type {
|
||||||
|
PlayEffectiveConfigSlice,
|
||||||
|
PlayEffectiveOddsSlice,
|
||||||
|
PlayEffectivePayload,
|
||||||
|
PlayEffectivePlayRow,
|
||||||
|
PlayEffectiveRiskCapRow,
|
||||||
|
PlayEffectiveVersionHead,
|
||||||
|
} from "./play-effective";
|
||||||
|
|||||||
62
src/types/api/play-effective.ts
Normal file
62
src/types/api/play-effective.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/** `GET /api/v1/play/effective` → `data` */
|
||||||
|
|
||||||
|
export type PlayEffectiveVersionHead = {
|
||||||
|
id: number;
|
||||||
|
version_no: number;
|
||||||
|
effective_at: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PlayEffectiveConfigSlice = {
|
||||||
|
is_enabled: boolean;
|
||||||
|
min_bet_amount: number;
|
||||||
|
max_bet_amount: number;
|
||||||
|
display_order: number;
|
||||||
|
rule_text_zh: string | null;
|
||||||
|
rule_text_en: string | null;
|
||||||
|
rule_text_ne: string | null;
|
||||||
|
extra_config_json: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PlayEffectiveOddsSlice = {
|
||||||
|
prize_scope: string;
|
||||||
|
odds_value: number;
|
||||||
|
rebate_rate: string;
|
||||||
|
commission_rate: string;
|
||||||
|
currency_code: string;
|
||||||
|
extra_config_json: unknown;
|
||||||
|
/** 赔率乘数小数(与 odds_value/10000 一致) */
|
||||||
|
odds_multiplier?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PlayEffectivePlayRow = {
|
||||||
|
play_code: string;
|
||||||
|
category: string;
|
||||||
|
dimension: number | null;
|
||||||
|
bet_mode: string | null;
|
||||||
|
display_name_zh: string | null;
|
||||||
|
display_name_en: string | null;
|
||||||
|
display_name_ne: string | null;
|
||||||
|
sort_order: number;
|
||||||
|
supports_multi_number: boolean;
|
||||||
|
master_enabled: boolean;
|
||||||
|
config: PlayEffectiveConfigSlice | null;
|
||||||
|
odds: PlayEffectiveOddsSlice | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PlayEffectiveRiskCapRow = {
|
||||||
|
draw_id: number | null;
|
||||||
|
normalized_number: string;
|
||||||
|
cap_amount: number;
|
||||||
|
cap_type: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PlayEffectivePayload = {
|
||||||
|
currency_code: string;
|
||||||
|
effective_versions: {
|
||||||
|
play_config: PlayEffectiveVersionHead;
|
||||||
|
odds: PlayEffectiveVersionHead;
|
||||||
|
risk_cap: PlayEffectiveVersionHead;
|
||||||
|
};
|
||||||
|
plays: PlayEffectivePlayRow[];
|
||||||
|
risk_cap_items: PlayEffectiveRiskCapRow[];
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user