From 788c7998ebf89862818416fd636d8f1edec8ddab Mon Sep 17 00:00:00 2001 From: kang Date: Wed, 27 May 2026 16:51:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(integration):=20=E4=B8=BA=E9=9B=86?= =?UTF-8?q?=E6=88=90=E7=AB=99=E7=82=B9=E4=B8=8E=E5=BC=80=E5=A5=96=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E6=96=B0=E5=A2=9E=20AdminPermissionGate=20=E6=9D=83?= =?UTF-8?q?=E9=99=90=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 使用 AdminPermissionGate 包裹集成站点与开奖相关组件,根据权限进行访问控制。 新增集成管理与开奖管理相关权限常量。 更新相关 UI 组件以适配权限校验逻辑,提升系统安全性与用户体验。 增强国际化支持,在英文、尼泊尔语与中文语言包中新增集成相关文案。 --- .../(shell)/config/integration-sites/page.tsx | 6 +- .../(shell)/draws/[drawId]/finance/page.tsx | 8 ++- src/app/admin/(shell)/draws/[drawId]/page.tsx | 8 ++- .../(shell)/draws/[drawId]/results/page.tsx | 8 ++- .../(shell)/draws/[drawId]/review/page.tsx | 8 ++- src/app/admin/(shell)/draws/page.tsx | 10 ++- .../[batchId]/details/page.tsx | 8 ++- .../admin/(shell)/settlement-batches/page.tsx | 8 ++- src/components/admin/admin-breadcrumb.tsx | 17 +++++ src/hooks/use-admin-site-code-options.ts | 6 +- src/i18n/locales/en/common.json | 4 +- src/i18n/locales/ne/common.json | 4 +- src/i18n/locales/ne/config.json | 72 ++++++++++++++++++- src/i18n/locales/zh/common.json | 4 +- src/lib/admin-permission-bundles.ts | 41 +++++++++++ src/lib/admin-prd.ts | 21 ++++++ src/modules/config/config-hub-screen.tsx | 3 +- src/modules/draws/draw-finance-console.tsx | 6 +- src/modules/draws/draw-results-console.tsx | 4 +- .../integration/integration-sites-console.tsx | 64 ++++++++--------- src/modules/jackpot/jackpot-pools-console.tsx | 7 +- src/modules/players/players-console.tsx | 5 +- src/modules/reports/reports-console.tsx | 16 +++-- .../tickets/player-tickets-console.tsx | 2 +- 24 files changed, 276 insertions(+), 64 deletions(-) create mode 100644 src/lib/admin-permission-bundles.ts diff --git a/src/app/admin/(shell)/config/integration-sites/page.tsx b/src/app/admin/(shell)/config/integration-sites/page.tsx index cf48f17..41e61e8 100644 --- a/src/app/admin/(shell)/config/integration-sites/page.tsx +++ b/src/app/admin/(shell)/config/integration-sites/page.tsx @@ -1,6 +1,8 @@ +import { AdminPermissionGate } from "@/components/admin/admin-permission-gate"; import { ModuleScaffold } from "@/components/admin/module-scaffold"; import { IntegrationSitesConsole } from "@/modules/integration/integration-sites-console"; import { buildPageMetadata } from "@/lib/page-metadata"; +import { PRD_INTEGRATION_ACCESS_ANY } from "@/lib/admin-prd"; import type { Metadata } from "next"; export const metadata: Metadata = buildPageMetadata("config", "integrationSites.title"); @@ -8,7 +10,9 @@ export const metadata: Metadata = buildPageMetadata("config", "integrationSites. export default function AdminIntegrationSitesPage() { return ( - + + + ); } diff --git a/src/app/admin/(shell)/draws/[drawId]/finance/page.tsx b/src/app/admin/(shell)/draws/[drawId]/finance/page.tsx index 196f60f..1fdf3e6 100644 --- a/src/app/admin/(shell)/draws/[drawId]/finance/page.tsx +++ b/src/app/admin/(shell)/draws/[drawId]/finance/page.tsx @@ -1,4 +1,6 @@ +import { AdminPermissionGate } from "@/components/admin/admin-permission-gate"; import { DrawFinanceConsole } from "@/modules/draws/draw-finance-console"; +import { PRD_DRAW_ACCESS_ANY } from "@/lib/admin-prd"; import { buildPageMetadata } from "@/lib/page-metadata"; import type { Metadata } from "next"; @@ -8,5 +10,9 @@ export default async function AdminDrawFinancePage(props: { params: Promise<{ drawId: string }>; }) { const { drawId } = await props.params; - return ; + return ( + + + + ); } diff --git a/src/app/admin/(shell)/draws/[drawId]/page.tsx b/src/app/admin/(shell)/draws/[drawId]/page.tsx index e1b646c..9d1dd87 100644 --- a/src/app/admin/(shell)/draws/[drawId]/page.tsx +++ b/src/app/admin/(shell)/draws/[drawId]/page.tsx @@ -1,8 +1,14 @@ +import { AdminPermissionGate } from "@/components/admin/admin-permission-gate"; import { DrawDetailConsole } from "@/modules/draws/draw-detail-console"; +import { PRD_DRAW_ACCESS_ANY } from "@/lib/admin-prd"; export default async function AdminDrawDetailPage(props: { params: Promise<{ drawId: string }>; }) { const { drawId } = await props.params; - return ; + return ( + + + + ); } diff --git a/src/app/admin/(shell)/draws/[drawId]/results/page.tsx b/src/app/admin/(shell)/draws/[drawId]/results/page.tsx index 5e3b75a..e17201e 100644 --- a/src/app/admin/(shell)/draws/[drawId]/results/page.tsx +++ b/src/app/admin/(shell)/draws/[drawId]/results/page.tsx @@ -1,8 +1,14 @@ +import { AdminPermissionGate } from "@/components/admin/admin-permission-gate"; import { DrawResultsConsole } from "@/modules/draws/draw-results-console"; +import { PRD_DRAW_ACCESS_ANY } from "@/lib/admin-prd"; export default async function AdminDrawResultsPage(props: { params: Promise<{ drawId: string }>; }) { const { drawId } = await props.params; - return ; + return ( + + + + ); } diff --git a/src/app/admin/(shell)/draws/[drawId]/review/page.tsx b/src/app/admin/(shell)/draws/[drawId]/review/page.tsx index 5d69050..dd46b43 100644 --- a/src/app/admin/(shell)/draws/[drawId]/review/page.tsx +++ b/src/app/admin/(shell)/draws/[drawId]/review/page.tsx @@ -1,8 +1,14 @@ +import { AdminPermissionGate } from "@/components/admin/admin-permission-gate"; import { DrawReviewConsole } from "@/modules/draws/draw-review-console"; +import { PRD_DRAW_ACCESS_ANY } from "@/lib/admin-prd"; export default async function AdminDrawReviewPage(props: { params: Promise<{ drawId: string }>; }) { const { drawId } = await props.params; - return ; + return ( + + + + ); } diff --git a/src/app/admin/(shell)/draws/page.tsx b/src/app/admin/(shell)/draws/page.tsx index 8061e61..db7dc83 100644 --- a/src/app/admin/(shell)/draws/page.tsx +++ b/src/app/admin/(shell)/draws/page.tsx @@ -1,5 +1,7 @@ import { ModuleScaffold } from "@/components/admin/module-scaffold"; +import { AdminPermissionGate } from "@/components/admin/admin-permission-gate"; import { DrawsIndexConsole } from "@/modules/draws/draws-index-console"; +import { PRD_DRAW_ACCESS_ANY } from "@/lib/admin-prd"; import { buildPageMetadata } from "@/lib/page-metadata"; import type { Metadata } from "next"; @@ -7,8 +9,10 @@ export const metadata: Metadata = buildPageMetadata("draws", "statusListTitle"); export default function AdminDrawsPage() { return ( - - - + + + + + ); } diff --git a/src/app/admin/(shell)/settlement-batches/[batchId]/details/page.tsx b/src/app/admin/(shell)/settlement-batches/[batchId]/details/page.tsx index 5f72ed8..f2816db 100644 --- a/src/app/admin/(shell)/settlement-batches/[batchId]/details/page.tsx +++ b/src/app/admin/(shell)/settlement-batches/[batchId]/details/page.tsx @@ -1,5 +1,7 @@ +import { AdminPermissionGate } from "@/components/admin/admin-permission-gate"; import { InvalidSettlementBatchId } from "@/modules/settlement/invalid-settlement-batch-id"; import { SettlementBatchDetailsConsole } from "@/modules/settlement/settlement-batch-details-console"; +import { PRD_PAYOUT_ACCESS_ANY } from "@/lib/admin-prd"; import { buildPageMetadata } from "@/lib/page-metadata"; import type { Metadata } from "next"; @@ -14,5 +16,9 @@ export default async function AdminSettlementBatchDetailsPage(props: { return ; } - return ; + return ( + + + + ); } diff --git a/src/app/admin/(shell)/settlement-batches/page.tsx b/src/app/admin/(shell)/settlement-batches/page.tsx index 09155dc..77a248c 100644 --- a/src/app/admin/(shell)/settlement-batches/page.tsx +++ b/src/app/admin/(shell)/settlement-batches/page.tsx @@ -1,9 +1,15 @@ +import { AdminPermissionGate } from "@/components/admin/admin-permission-gate"; import { SettlementBatchesConsole } from "@/modules/settlement/settlement-batches-console"; +import { PRD_PAYOUT_ACCESS_ANY } from "@/lib/admin-prd"; import { buildPageMetadata } from "@/lib/page-metadata"; import type { Metadata } from "next"; export const metadata: Metadata = buildPageMetadata("settlement", "batchList"); export default function AdminSettlementBatchesPage() { - return ; + return ( + + + + ); } diff --git a/src/components/admin/admin-breadcrumb.tsx b/src/components/admin/admin-breadcrumb.tsx index bc60072..f14445f 100644 --- a/src/components/admin/admin-breadcrumb.tsx +++ b/src/components/admin/admin-breadcrumb.tsx @@ -40,6 +40,8 @@ const NAV_TRANSLATION_KEYS: Record = { tickets: "tickets", audit: "audit", settings: "settings", + integration: "integration", + config: "config", }; const RULES_ROUTE_LABELS: Record = { @@ -55,6 +57,16 @@ const SETTINGS_ROUTE_LABELS: Record = { currencies: "currencies.title", }; +const CONFIG_ROUTE_LABELS: Record = { + "integration-sites": "integrationSites.title", + plays: "nav.items.plays", + odds: "nav.items.odds", + rebate: "nav.items.rebate", + jackpot: "nav.items.jackpot", + "risk-cap": "nav.items.risk-cap", + wallet: "wallet.title", +}; + function titleCase(value: string): string { return value .split("-") @@ -146,6 +158,11 @@ export function AdminBreadcrumb() { ns: "config", defaultValue: titleCase(subSegment), }); + } else if (businessSegment === "config" && subSegment) { + const key = CONFIG_ROUTE_LABELS[subSegment]; + subLabel = key + ? t(key, { ns: "config", defaultValue: titleCase(subSegment) }) + : titleCase(subSegment); } else { subLabel = subSegment ? t(`subnav.${subSegment}`, { diff --git a/src/hooks/use-admin-site-code-options.ts b/src/hooks/use-admin-site-code-options.ts index 2a6499b..cdf166e 100644 --- a/src/hooks/use-admin-site-code-options.ts +++ b/src/hooks/use-admin-site-code-options.ts @@ -4,6 +4,7 @@ import { useCallback, useEffect, useState } from "react"; import { getAdminIntegrationSites } from "@/api/admin-integration-sites"; import { adminHasAnyPermission } from "@/lib/admin-permissions"; +import { PRD_INTEGRATION_ACCESS_ANY } from "@/lib/admin-prd"; import { useAdminProfile } from "@/stores/admin-session"; export type AdminSiteCodeOption = { @@ -21,10 +22,7 @@ export function useAdminSiteCodeOptions(): { reload: () => Promise; } { const profile = useAdminProfile(); - const canLoad = adminHasAnyPermission(profile?.permissions, [ - "prd.integration.view", - "prd.integration.manage", - ]); + const canLoad = adminHasAnyPermission(profile?.permissions, PRD_INTEGRATION_ACCESS_ANY); const [sites, setSites] = useState([]); const [loading, setLoading] = useState(false); diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index 03295b1..8631d46 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -147,7 +147,9 @@ "tickets": "Ticket list", "audit": "Audit Logs", "settings": "Settings", - "account": "Account settings" + "account": "Account settings", + "integration": "Integration sites", + "config": "Operations config" }, "sidebar": { "workspace": "Workspace" diff --git a/src/i18n/locales/ne/common.json b/src/i18n/locales/ne/common.json index 9885e72..7389c01 100644 --- a/src/i18n/locales/ne/common.json +++ b/src/i18n/locales/ne/common.json @@ -147,7 +147,9 @@ "tickets": "टिकट सूची", "audit": "अडिट लग", "settings": "सेटिङ", - "account": "खाता सेटिङ" + "account": "खाता सेटिङ", + "integration": "मुख्य साइट एकीकरण", + "config": "सञ्चालन कन्फिगरेसन" }, "sidebar": { "workspace": "कार्यस्थान" diff --git a/src/i18n/locales/ne/config.json b/src/i18n/locales/ne/config.json index db5c4ae..b52a6a3 100644 --- a/src/i18n/locales/ne/config.json +++ b/src/i18n/locales/ne/config.json @@ -29,7 +29,77 @@ "jackpotTitle": "Jackpot", "jackpotDesc": "पूल प्यारामिटर र लेजर", "riskCapTitle": "जोखिम क्याप", - "riskCapDesc": "नम्बर क्याप र ओगट उपस्थिति" + "riskCapDesc": "नम्बर क्याप र ओगट उपस्थिति", + "integrationTitle": "मुख्य साइट एकीकरण", + "integrationDesc": "site_code, JWT गोप्य, पार्टनर वालेट URL र iframe श्वेतसूची" + }, + "integrationSites": { + "title": "मुख्य साइट एकीकरण साइटहरू", + "description": "एडमिनमा पार्टनर एकीकरण सेटिङ मिलाउनुहोस्। site_code सिर्जना पछि परिवर्तन गर्न मिल्दैन।", + "create": "नयाँ साइट", + "edit": "सम्पादन", + "save": "बचत", + "saving": "बचत हुँदैछ…", + "cancel": "रद्द", + "copy": "प्रतिलिपि", + "loading": "लोड हुँदैछ…", + "empty": "कुनै एकीकरण साइट छैन", + "loadFailed": "एकीकरण साइट लोड असफल", + "saveFailed": "बचत असफल", + "createSuccess": "साइट {{code}} सिर्जना भयो", + "updateSuccess": "साइट {{code}} अद्यावधिक भयो", + "connectivityTest": "जडान परीक्षण", + "connectivityTitle": "पार्टनर वालेट जडान परीक्षण", + "connectivityDescription": "परीक्षण खेलाडीबाट साइट {{code}} को balance API कल गर्नुहोस्।", + "connectivityPlayerId": "परीक्षण site_player_id", + "connectivityRun": "परीक्षण सुरु", + "connectivityRunning": "परीक्षण हुँदैछ…", + "connectivitySuccess": "जडान सफल", + "connectivityFailed": "जडान असफल", + "exportParams": "प्यारामिटर निर्यात", + "exportSuccess": "{{code}} को प्यारामिटर चिट्ठा निर्यात भयो", + "exportFailed": "निर्यात असफल", + "rotateSecrets": "गोप्य कुञ्जी पुनः सिर्जना", + "rotateSuccess": "साइट {{code}} का गोप्य कुञ्जी पुनः सिर्जना भयो", + "rotateFailed": "गोप्य कुञ्जी पुनः सिर्जना असफल", + "rotateConfirmTitle": "गोप्य कुञ्जी पुनः सिर्जना गर्ने?", + "rotateConfirmDescription": "साइट {{code}} का नयाँ SSO र वालेट कुञ्जी सिर्जना हुन्छ। पुराना कुञ्जी तुरुन्त अमान्य हुन्छन्।", + "rotateConfirm": "पुष्टि", + "secretsTitle": "गोप्य कुञ्जी अहिले नै सुरक्षित राख्नुहोस्", + "secretsDescription": "साइट {{code}} का गोप्य कुञ्जी एक पटक मात्र देखिन्छ।", + "secretsDismiss": "सुरक्षित गरिसके", + "copied": "{{field}} प्रतिलिपि भयो", + "copyFailed": "प्रतिलिपि असफल", + "noPermission": "एकीकरण साइट हेर्ने अनुमति छैन", + "codeImmutable": "site_code सिर्जना पछि परिवर्तन गर्न मिल्दैन", + "statusEnabled": "सक्रिय", + "statusDisabled": "निष्क्रिय", + "dialogCreateTitle": "नयाँ एकीकरण साइट", + "dialogEditTitle": "एकीकरण साइट सम्पादन", + "dialogDescription": "पार्टनरले अनुकूल URL नभएसम्म पूर्वनिर्धारित वालेट path प्रयोग गर्न सकिन्छ।", + "form": { + "required": "साइट नाम अनिवार्य छ", + "codeRequired": "site_code अनिवार्य छ" + }, + "columns": { + "code": "site_code", + "name": "नाम", + "status": "स्थिति", + "walletUrl": "वालेट API", + "actions": "कार्य" + }, + "fields": { + "code": "site_code", + "name": "साइट नाम", + "currency": "पूर्वनिर्धारित मुद्रा", + "status": "स्थिति", + "walletApiUrl": "पार्टनर वालेट आधार URL", + "lotteryH5BaseUrl": "लटरी H5 आधार URL (वैकल्पिक)", + "iframeOrigins": "iframe श्वेतसूची (प्रति लाइन एक origin)", + "notes": "टिप्पणी", + "ssoSecret": "SSO गोप्य", + "walletApiKey": "वालेट API कुञ्जी" + } }, "versionStatus": { "active": "सक्रिय", diff --git a/src/i18n/locales/zh/common.json b/src/i18n/locales/zh/common.json index 9327e63..1110bc8 100644 --- a/src/i18n/locales/zh/common.json +++ b/src/i18n/locales/zh/common.json @@ -147,7 +147,9 @@ "tickets": "注单列表", "audit": "审计日志", "settings": "系统设置", - "account": "账号设置" + "account": "账号设置", + "integration": "主站接入站点", + "config": "运营配置" }, "sidebar": { "workspace": "工作台" diff --git a/src/lib/admin-permission-bundles.ts b/src/lib/admin-permission-bundles.ts new file mode 100644 index 0000000..41f3e97 --- /dev/null +++ b/src/lib/admin-permission-bundles.ts @@ -0,0 +1,41 @@ +import { PRD_INTEGRATION_MANAGE, PRD_INTEGRATION_VIEW } from "@/lib/admin-prd"; + +export type AdminPermissionBundleKey = "view" | "manage" | "audit" | "export" | "privilege"; + +export type AdminPageKey = "integration-sites"; + +/** + * “页面权限包”是把运营/管理员能理解的词汇(查看/管理/审核/导出/特权) + * 映射到系统真实用的 `prd.*` 权限 slug。 + * + * 目前只落地 integration-sites;其它页面按同样方式逐步接入。 + */ +export const ADMIN_PERMISSION_BUNDLES = { + "integration-sites": { + view: [PRD_INTEGRATION_VIEW] as const, + manage: [PRD_INTEGRATION_MANAGE] as const, + audit: [] as const, + // 导出接口的资源鉴权仍落在 view/manage,因此这里复用 view。 + export: [PRD_INTEGRATION_VIEW] as const, + privilege: [] as const, + }, +} satisfies Record>; + +export const ADMIN_PAGE_REQUIRED_ANY = { + "integration-sites": [ + ...ADMIN_PERMISSION_BUNDLES["integration-sites"].view, + ...ADMIN_PERMISSION_BUNDLES["integration-sites"].manage, + ] as const, +} satisfies Record; + +export function getAdminPageRequiredAny(page: AdminPageKey): readonly string[] { + return ADMIN_PAGE_REQUIRED_ANY[page]; +} + +export function getAdminPageBundle( + page: AdminPageKey, + bundle: AdminPermissionBundleKey, +): readonly string[] { + return ADMIN_PERMISSION_BUNDLES[page][bundle] ?? []; +} + diff --git a/src/lib/admin-prd.ts b/src/lib/admin-prd.ts index 3c1d52a..cf45841 100644 --- a/src/lib/admin-prd.ts +++ b/src/lib/admin-prd.ts @@ -10,6 +10,10 @@ export const PRD_PLAYER_FREEZE_MANAGE = "prd.player_freeze.manage" as const; export const PRD_CURRENCY_MANAGE = "prd.currency.manage" as const; +/** 接入站点(integration-sites) */ +export const PRD_INTEGRATION_VIEW = "prd.integration.view" as const; +export const PRD_INTEGRATION_MANAGE = "prd.integration.manage" as const; + export const PRD_WALLET_RECONCILE_MANAGE = "prd.wallet_reconcile.manage" as const; export const PRD_WALLET_RECONCILE_VIEW = "prd.wallet_reconcile.view" as const; export const PRD_WALLET_RECONCILE_VIEW_CS = "prd.wallet_reconcile.view_cs" as const; @@ -105,8 +109,25 @@ export const PRD_RULES_ODDS_ACCESS_ANY = [ PRD_REBATE_VIEW, ] as const; +/** 开奖页面入口 */ +export const PRD_DRAW_ACCESS_ANY = [ + PRD_DRAW_RESULT_VIEW, + PRD_DRAW_RESULT_MANAGE, + PRD_DRAW_REOPEN_MANAGE, +] as const; + /** 封顶配置页 */ export const PRD_RISK_CAP_ACCESS_ANY = [PRD_RISK_CAP_MANAGE, PRD_RISK_CAP_VIEW] as const; /** Jackpot 配置页 */ export const PRD_JACKPOT_ACCESS_ANY = [PRD_JACKPOT_MANAGE, PRD_JACKPOT_VIEW] as const; + +/** 派彩 / 结算页面入口 */ +export const PRD_PAYOUT_ACCESS_ANY = [ + PRD_PAYOUT_VIEW, + PRD_PAYOUT_REVIEW, + PRD_PAYOUT_MANAGE, +] as const; + +/** 接入站点配置页 */ +export const PRD_INTEGRATION_ACCESS_ANY = [PRD_INTEGRATION_VIEW, PRD_INTEGRATION_MANAGE] as const; diff --git a/src/modules/config/config-hub-screen.tsx b/src/modules/config/config-hub-screen.tsx index 2a42d21..23f0bbe 100644 --- a/src/modules/config/config-hub-screen.tsx +++ b/src/modules/config/config-hub-screen.tsx @@ -6,6 +6,7 @@ import { ChevronRight } from "lucide-react"; import { ModuleScaffold } from "@/components/admin/module-scaffold"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { PRD_INTEGRATION_ACCESS_ANY } from "@/lib/admin-prd"; import { useAdminProfile } from "@/stores/admin-session"; import { adminHasAnyPermission } from "@/lib/admin-permissions"; @@ -45,7 +46,7 @@ const HUB_CARDS: HubCard[] = [ href: "/admin/config/integration-sites", titleKey: "hub.integrationTitle", descKey: "hub.integrationDesc", - requiredAny: ["prd.integration.view", "prd.integration.manage"], + requiredAny: PRD_INTEGRATION_ACCESS_ANY, }, ]; diff --git a/src/modules/draws/draw-finance-console.tsx b/src/modules/draws/draw-finance-console.tsx index 018a1fb..14af6df 100644 --- a/src/modules/draws/draw-finance-console.tsx +++ b/src/modules/draws/draw-finance-console.tsx @@ -27,6 +27,7 @@ import type { AdminDrawFinanceSummaryData } from "@/types/api/admin-draw-finance import { toast } from "sonner"; import { useAdminCurrencyCatalog } from "@/hooks/use-admin-currency-catalog"; +import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter"; import { useConfirmAction } from "@/hooks/use-confirm-action"; import { useExportLabels } from "@/hooks/use-export-labels"; import { formatAdminMinorUnits } from "@/lib/money"; @@ -44,6 +45,7 @@ export function DrawFinanceConsole({ drawId }: { drawId: string }): React.ReactE PRD_PAYOUT_REVIEW, ]); const [data, setData] = useState(null); + const formatTs = useAdminDateTimeFormatter(); const exportLabels = useExportLabels("drawFinance", { drawNo: data?.draw_no ?? drawId }); const [err, setErr] = useState(null); const [loading, setLoading] = useState(true); @@ -219,8 +221,8 @@ export function DrawFinanceConsole({ drawId }: { drawId: string }): React.ReactE {formatMoney(b.total_jackpot_payout_amount)} - - {b.finished_at ?? "—"} + + {formatTs(b.finished_at)} ))} diff --git a/src/modules/draws/draw-results-console.tsx b/src/modules/draws/draw-results-console.tsx index 4604eb8..95af0c5 100644 --- a/src/modules/draws/draw-results-console.tsx +++ b/src/modules/draws/draw-results-console.tsx @@ -15,6 +15,7 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; +import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter"; import { cn } from "@/lib/utils"; import { adminHasAnyPermission } from "@/lib/admin-permissions"; import { useAdminProfile } from "@/stores/admin-session"; @@ -103,6 +104,7 @@ export function DrawResultsConsole({ drawId }: { drawId: string }) { function BatchTable({ batch }: { batch: AdminDrawBatchRow }) { const { t } = useTranslation("draws"); + const formatDt = useAdminDateTimeFormatter(); return ( @@ -112,7 +114,7 @@ function BatchTable({ batch }: { batch: AdminDrawBatchRow }) { source: batch.source_type === "manual" ? t("manualEntry") : t("rng"), })}{" "} · {t("rngSummary", { hash: batch.rng_seed_hash ?? "—" })} ·{" "} - {t("confirmedAt", { time: batch.confirmed_at ?? "—" })} + {t("confirmedAt", { time: formatDt(batch.confirmed_at) })}

diff --git a/src/modules/integration/integration-sites-console.tsx b/src/modules/integration/integration-sites-console.tsx index e191f77..1e6f550 100644 --- a/src/modules/integration/integration-sites-console.tsx +++ b/src/modules/integration/integration-sites-console.tsx @@ -36,11 +36,15 @@ import { } from "@/components/ui/table"; import { Textarea } from "@/components/ui/textarea"; import { adminHasAnyPermission } from "@/lib/admin-permissions"; +import { getAdminPageBundle } from "@/lib/admin-permission-bundles"; import { useAdminProfile } from "@/stores/admin-session"; import { LotteryApiBizError } from "@/types/api/errors"; import type { + AdminIntegrationSiteCreatePayload, AdminIntegrationSiteConnectivityResult, + AdminIntegrationSiteUpdatePayload, AdminIntegrationSiteRow, + AdminIntegrationSiteDetail, AdminIntegrationSiteSecrets, AdminIntegrationSiteWithSecrets, } from "@/types/api/admin-integration-site"; @@ -86,7 +90,7 @@ function textToOrigins(text: string): string[] { .filter(Boolean); } -function rowToForm(row: AdminIntegrationSiteRow & Partial): FormState { +function rowToForm(row: AdminIntegrationSiteDetail): FormState { return { code: row.code, name: row.name, @@ -103,7 +107,12 @@ function rowToForm(row: AdminIntegrationSiteRow & Partial): FormState }; } -function formToPayload(form: FormState, includeCode: boolean) { +function formToPayload(form: FormState, includeCode: true): AdminIntegrationSiteCreatePayload; +function formToPayload(form: FormState, includeCode: false): AdminIntegrationSiteUpdatePayload; +function formToPayload( + form: FormState, + includeCode: boolean, +): AdminIntegrationSiteCreatePayload | AdminIntegrationSiteUpdatePayload { const base = { name: form.name.trim(), currency_code: form.currency_code.trim() || "NPR", @@ -128,11 +137,10 @@ function formToPayload(form: FormState, includeCode: boolean) { export function IntegrationSitesConsole() { const { t } = useTranslation("config"); const profile = useAdminProfile(); - const canView = adminHasAnyPermission(profile?.permissions, [ - "prd.integration.view", - "prd.integration.manage", - ]); - const canManage = adminHasAnyPermission(profile?.permissions, ["prd.integration.manage"]); + const canManage = adminHasAnyPermission( + profile?.permissions, + getAdminPageBundle("integration-sites", "manage"), + ); const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); @@ -158,12 +166,6 @@ export function IntegrationSitesConsole() { const [exportBusyId, setExportBusyId] = useState(null); const load = useCallback(async () => { - if (!canView) { - setItems([]); - setLoading(false); - return; - } - setLoading(true); try { const data = await getAdminIntegrationSites(); @@ -175,7 +177,7 @@ export function IntegrationSitesConsole() { } finally { setLoading(false); } - }, [canView, t]); + }, [t]); useEffect(() => { queueMicrotask(() => { @@ -334,14 +336,6 @@ export function IntegrationSitesConsole() { } } - if (!canView) { - return ( - -

{t("integrationSites.noPermission")}

-
- ); - } - return ( <> {row.name}
+ tone={row.status === 1 ? "success" : "neutral"} + > + {row.status === 1 + ? t("integrationSites.statusEnabled") + : t("integrationSites.statusDisabled")} + {row.wallet_api_url ?? "—"} @@ -407,9 +400,16 @@ export function IntegrationSitesConsole() { > {t("integrationSites.exportParams")} - + {canManage ? ( + + ) : null} {canManage ? (