feat(admin, i18n): enhance reports, draws, config, and player workflows

This commit is contained in:
2026-06-08 17:41:55 +08:00
parent af982bb9f7
commit 7e65c53732
55 changed files with 1986 additions and 804 deletions

View File

@@ -15,9 +15,9 @@
"risk-cap": "Payout caps"
},
"rulesPlaysTitle": "Play rules",
"rulesOddsTitle": "Odds & rebate",
"rulesOddsDescription": "Odds matrix and rebate rates on one page, sharing the same odds version line.",
"rulesOddsDescriptionShort": "Pick a play on the left, edit odds and rebate on the right, then save and publish.",
"rulesOddsTitle": "Odds & base rebate",
"rulesOddsDescription": "Odds matrix and base rebate are maintained on one page, sharing the same odds version line.",
"rulesOddsDescriptionShort": "Pick a play on the left, edit odds and base rebate on the right. Agent/player rebate is added on top of this base, then save and publish.",
"riskCapTitle": "Risk cap rules"
},
"hub": {
@@ -25,8 +25,8 @@
"description": "Jump to play rules, odds & rebate, jackpot, and risk cap by domain. The sidebar provides direct links; this page is an overview.",
"playsTitle": "Play rules",
"playsDesc": "Play switches, limits, and rule copy",
"oddsTitle": "Odds & rebate",
"oddsDesc": "Odds matrix and rebate rates in one version stream",
"oddsTitle": "Odds & base rebate",
"oddsDesc": "Odds matrix and base rebate in one version stream",
"jackpotTitle": "Jackpot",
"jackpotDesc": "Pool parameters and ledger records",
"riskCapTitle": "Risk cap rules",
@@ -351,6 +351,20 @@
"readOnlyDraftHint": "Current version is read-only. Create a draft first.",
"batchEnabledCount": "{{enabledCount}}/{{total}} enabled",
"noPlayTypes": "No play types",
"filters": {
"sectionTitle": "Filter plays",
"sectionDescription": "Narrow the list first, then use batch switches or row-level edits.",
"keyword": "Search plays",
"keywordPlaceholder": "Filter by play code, display name, or category",
"category": "Category",
"status": "Status",
"allCategories": "All categories",
"allStatuses": "All statuses",
"uncategorized": "Uncategorized",
"reset": "Clear filters",
"empty": "No matching play types",
"groupCount": "{{count}} plays"
},
"actions": {
"enable": "Enable",
"disable": "Disable",
@@ -362,6 +376,13 @@
"en": "English",
"ne": "Nepali"
},
"categories": {
"standard": "Standard",
"attribute": "Attribute",
"position": "Position",
"box": "Box",
"jackpot": "Jackpot"
},
"table": {
"playCode": "Play code",
"category": "Category",
@@ -413,7 +434,7 @@
},
"currentSelection": "Selection: {{category}} / {{play}}",
"playSelectPlaceholder": "Select play type",
"readOnlyBanner": "This version is read-only. Create a draft to edit odds and rebate.",
"readOnlyBanner": "This version is read-only. Create a draft to edit odds and base rebate.",
"table": {
"prizeScope": "Prize scope",
"multiplier": "Odds multiplier"
@@ -452,11 +473,11 @@
"loadingDetails": "Loading details…",
"multiplier": "Multiplier x{{value}} · {{currency}}",
"missingScopeRow": "Missing {{scope}} row. Check seed or version data.",
"rebateRate": "Rebate rate (%)",
"rebateRateHint": "Writes rebate_rate to all prize scopes under this play type.",
"rebateRate": "Base rebate rate (%)",
"rebateRateHint": "This is the platform base rebate. It writes rebate_rate to all prize scopes under this play type; agent/player rebate is added on top of it.",
"placeholders": {
"multiplier": "Enter odds multiplier",
"rebateRate": "Enter rebate rate"
"rebateRate": "Enter base rebate rate"
},
"publishFailed": "Publish failed",
"createDraftSuccess": "Created draft v{{version}}",
@@ -481,33 +502,33 @@
}
},
"rebate": {
"sectionHint": "Rebate rates are stored in the odds version; select or create an odds draft in the section above first.",
"sectionHint": "This section configures the base rebate, which is stored in the odds version; select or create an odds draft in the section above first.",
"lazyLoadHint": "Scroll down to the rebate section to load",
"embeddedVersionHint": "Rebate shares the odds version line—switch versions in the Odds section above.",
"embeddedVersionHint": "Base rebate shares the odds version line—switch versions in the Odds section above.",
"sheetDescription": "Rebate is stored in the odds draft version and shares the same version set as odds.",
"publishLabel": "Publish",
"publishSuccess": "Published odds version with rebate",
"publishFailed": "Publish failed",
"publishDialog": {
"title": "Publish rebate/odds version?",
"description": "After publish, rebate calculation applies to new tickets.",
"title": "Publish base rebate/odds version?",
"description": "After publish, the base rebate applies to new tickets. Agent/player extra rebate is still added on top.",
"confirm": "Confirm publish"
},
"createDraftSuccess": "Created draft v{{version}}",
"createDraftFailed": "Failed to create draft",
"deleteFailed": "Delete failed",
"editingVersion": "Editing version v{{version}} · {{status}}",
"readOnlyHint": "Create a draft before editing rebate.",
"dimensionRatesMixedHint": "Rebate rates within the same dimension (2D/3D/4D) are not identical: the three percentage inputs show the first play (alphabetically) that has the primary prize scope; use the table as the source of truth. Bulk inputs will overwrite all plays in that dimension to one rate.",
"readOnlyHint": "Create a draft before editing base rebate.",
"dimensionRatesMixedHint": "Base rebate within the same dimension (2D/3D/4D) is not identical: the three percentage inputs show the first play (alphabetically) that has the primary prize scope; use the table as the source of truth. Bulk inputs will overwrite all plays in that dimension to one rate.",
"fields": {
"d2": "2D rebate rate (%)",
"d3": "3D rebate rate (%)",
"d4": "4D rebate rate (%)"
"d2": "2D base rebate rate (%)",
"d3": "3D base rebate rate (%)",
"d4": "4D base rebate rate (%)"
},
"placeholders": {
"d2": "Enter 2D rebate",
"d3": "Enter 3D rebate",
"d4": "Enter 4D rebate"
"d2": "Enter 2D base rebate",
"d3": "Enter 3D base rebate",
"d4": "Enter 4D base rebate"
},
"winEnjoy": {
"label": "Deduct rebate on winning payouts",
@@ -527,6 +548,8 @@
"validation": {
"requireAtLeastOne": "At least one cap row is required",
"defaultGreaterThanZero": "Default cap amount must be greater than 0",
"defaultCannotBindDraw": "Default cap cannot be bound to a specific draw",
"specialGreaterThanZero": "Special cap amount must be greater than 0: {{number}}",
"numberMustBe4Digits": "Number must be 4 digits: {{number}}",
"enterValidCapAmount": "Enter a valid cap amount"
},
@@ -547,14 +570,37 @@
"defaultCap": {
"title": "Default cap",
"description": "Numbers without a special cap use this default cap template.",
"fieldLabel": "Cap amount (minor unit)"
"fieldLabel": "Cap amount (major unit)"
},
"specialCaps": {
"title": "Special caps"
"title": "Special caps",
"description": "No draw selected means a global number cap. Selecting a draw makes it a draw-specific cap."
},
"scope": {
"global": "Global number",
"drawId": "Draw ID: {{id}}"
},
"groups": {
"globalTitle": "Global number caps",
"globalDescription": "Long-running caps that are not tied to a specific draw. Use these for normal number-level selling limits.",
"globalEmpty": "No global number caps yet.",
"drawTitle": "Draw-specific caps",
"drawDescription": "Only applies to the selected draw. Use these for temporary tightening or relaxing of a number cap.",
"drawEmpty": "No draw-specific caps yet.",
"count": "{{count}} rows"
},
"summary": {
"defaultCap": "Default cap",
"defaultHint": "Any number without a special rule falls back to this value.",
"globalCaps": "Global number caps",
"globalHint": "Long-running rules that do not change with a single draw.",
"drawCaps": "Draw-specific caps",
"drawHint": "Temporary rules that only apply to selected draws."
},
"loadingDetails": "Loading details…",
"noDetailRows": "No detail rows.",
"table": {
"scope": "Scope",
"number": "Number",
"capAmount": "Cap amount",
"used": "Used",

View File

@@ -30,9 +30,9 @@
"queryDraw": "Search draw",
"reset": "Reset",
"fuzzyDrawNo": "Fuzzy draw no.",
"viewDetails": "View details",
"viewDetails": "View draw details",
"editDraw": {
"action": "Edit",
"action": "Edit draw",
"title": "Edit draw",
"description": "Draw {{drawNo}} · edit times in {{tz}}",
"drawNoPlaceholder": "Enter draw number, for example 20260526-008",
@@ -42,7 +42,7 @@
"failed": "Update failed"
},
"deleteDraw": {
"action": "Delete",
"action": "Delete draw",
"title": "Delete draw",
"description": "Delete draw {{drawNo}}? Only for pending draws with no bets. This cannot be undone.",
"success": "Draw deleted",
@@ -57,13 +57,19 @@
"invalidDrawId": "Invalid draw ID",
"loadFailed": "Failed to load. Check login and API configuration.",
"drawDetail": "Draw details",
"backToList": "Back to draw list",
"detailSubtitle": "{{date}} · Round {{seq}}",
"overviewTitle": "Draw overview",
"overviewBetTotal": "Total bet",
"overviewPayoutTotal": "Total payout",
"overviewProfitLoss": "Profit/Loss",
"scheduleTitle": "Schedule",
"resultBatchesTitle": "Result batches",
"batchSummaryTotal": "{{count}} batch(es)",
"batchSummaryPending": "{{count}} pending",
"batchSummaryPublished": "{{count}} published",
"noResultBatchesYet": "No result batches yet.",
"reviewQueueHint": "After results are generated, continue in Review & publish.",
"goToReviewTab": "Review & publish",
"businessDate": "Business date",
"sequenceNo": "Sequence no.",

View File

@@ -3,17 +3,21 @@
"configTitle": "Jackpot pool configuration",
"pageDescription": "Maintain per-currency pool parameters; contribution and payout logs are below.",
"poolsSectionDescription": "Contribution rate, burst threshold, switch, and manual burst.",
"rulesTitle": "Rules",
"rulesJoin": "Only successfully placed lines that meet the minimum participation bet amount contribute to the pool.",
"rulesBurst": "The pool releases when the burst threshold is reached, the forced burst gap is met, or a configured play combo triggers it.",
"rulesManual": "Manual burst is a super-admin fallback only. You can enter either the numeric draw ID or the draw number.",
"recordsSectionTitle": "Contribution & payout logs",
"recordsSectionDescription": "Filter payout and contribution entries (read-only).",
"loadFailed": "Failed to load",
"saveSuccess": "Saved",
"saveFailed": "Save failed",
"invalidDrawId": "Enter a valid draw number",
"invalidDrawId": "Enter a valid draw ID or draw number",
"manualBurstSuccess": "Jackpot burst triggered manually",
"manualBurstFailed": "Manual burst failed",
"noPoolData": "No pool data",
"displayBalance": "Display balance {{amount}}",
"currentAmount": "Current pool balance (minor unit)",
"currentAmount": "Current pool balance (major unit)",
"balanceAdjustmentTitle": "Balance adjustment",
"balanceAdjustmentHint": "A reason is required; each change is recorded in the adjustment ledger. Balance cannot be edited via Save.",
"adjustmentDirection": "Direction",
@@ -33,13 +37,13 @@
"recentAdjustments": "Recent adjustments",
"contributionRate": "Contribution rate (%)",
"contributionRatePlaceholder": "e.g. 2 = 2%",
"triggerThreshold": "Burst threshold (minor unit)",
"triggerThreshold": "Burst threshold (major unit)",
"triggerThresholdPlaceholder": "Enter burst threshold",
"payoutRate": "Burst payout rate (%)",
"payoutRatePlaceholder": "e.g. 5 = 5%",
"forceTriggerGap": "Force burst gap (settled draws)",
"forceTriggerGapPlaceholder": "Enter forced burst gap in draws",
"minBetAmount": "Minimum bet amount (minor unit)",
"minBetAmount": "Minimum participation bet amount (major unit)",
"minBetAmountPlaceholder": "Enter minimum bet amount",
"comboTriggerPlays": "Combo trigger plays (comma separated)",
"comboTriggerPlaysPlaceholder": "Enter play codes separated by commas, for example straight,ibox",
@@ -48,8 +52,8 @@
"enabled": "Enabled",
"saving": "Saving…",
"save": "Save",
"manualBurstDrawId": "Draw ID for manual burst",
"manualBurstHint": "Super admin only. Requires a settled draw with first-prize winners. Pool release follows the configured payout rate.",
"manualBurstDrawId": "Draw for manual burst (ID or number)",
"manualBurstHint": "Super admin only. You can enter either the numeric draw ID or the draw number. Requires a settled draw with first-prize winners. Pool release follows the configured payout rate.",
"manualBurstConfirmTitle": "Confirm manual jackpot burst?",
"manualBurstConfirmDescription": "Jackpot will be split among first-prize winners for draw {{drawId}} using the payout rate. Pool balance will be reduced. This cannot be undone automatically.",
"processing": "Processing…",

View File

@@ -7,6 +7,7 @@
"detailSubtitle": "{{site}} · {{sitePlayerId}} · ID {{playerId}}",
"tabOverview": "Overview",
"tabTickets": "Tickets",
"ticketTableHint": "This table shows the player's recent tickets. Use the row action to jump into the main ticket list for full context and troubleshooting.",
"tabWalletTxns": "Wallet transactions",
"tabCreditLedger": "Credit ledger",
"tabTransferOrders": "Transfer orders",

View File

@@ -29,7 +29,7 @@
"createSummaryPlayer": "A manual reconcile will run for player {{player}} from {{from}} to {{to}}.",
"createSummaryPending": "Choose a complete reconcile date range before creating a job.",
"jobsTitle": "Reconcile jobs",
"jobsDesc": "Use the action on the right to open paginated item details.",
"jobsDesc": "Use the action on the right to open discrepancy details and paginated results.",
"refresh": "Refresh",
"jobNo": "Job no.",
"type": "Type",
@@ -42,10 +42,18 @@
"createdAt": "Created at",
"operate": "Action",
"view": "View",
"detailsTitle": "Job details",
"viewDetails": "View discrepancy details",
"detailsTitle": "Discrepancy details",
"sideARef": "Lottery ref",
"sideBRef": "Main site ref",
"differenceAmount": "Difference (cent)",
"itemResult": "Check result",
"diagnosis": "Issue summary",
"suggestedAction": "Suggested action",
"processingStatus": "Processing status",
"quickAccess": "Quick access",
"openTransferOrder": "Open transfer order",
"openWalletTxn": "Open wallet ledger",
"detectedAt": "Detected at",
"noDetails": "No details",
"playerSearch": "Player (optional)",
@@ -63,5 +71,33 @@
"statusFailed": "Failed",
"itemMismatch": "Mismatch",
"itemMatched": "Matched",
"itemPendingCheck": "Pending check"
"itemPendingCheck": "Pending check",
"itemStaleProcessing": "Stale processing",
"itemPendingReconcile": "Pending manual reconcile",
"itemMissingWalletTxn": "Missing wallet ledger",
"itemUnexpectedWalletTxn": "Unexpected wallet ledger",
"itemMissingRefund": "Missing refund ledger",
"itemMissingReversal": "Missing reversal ledger",
"itemResolved": "Resolved",
"itemUnresolved": "Unresolved",
"diagnosisStaleProcessing": "The transfer order has stayed in processing for too long and the system has no final success or failure result.",
"diagnosisPendingReconcile": "The transfer order is marked for manual reconciliation and needs a human-confirmed final result.",
"diagnosisMissingWalletTxn": "The transfer order status moved forward, but the matching lottery wallet ledger entry is missing.",
"diagnosisUnexpectedWalletTxn": "The lottery side contains extra wallet ledger entries that do not match the current transfer status.",
"diagnosisMissingRefund": "The transfer-out failed, but the expected refund ledger entry was not found.",
"diagnosisMissingReversal": "The transfer order was reversed, but the matching reversal ledger entry is missing on the lottery side.",
"diagnosisMatched": "This record is already balanced and needs no further action.",
"diagnosisPendingCheck": "This record still needs manual verification.",
"actionStaleProcessing": "Check whether the main site already debited successfully, then decide whether the lottery side needs a reversal or a compensating entry.",
"actionPendingReconcile": "Open the transfer order first, confirm the main-site outcome, then decide whether to credit, reverse, or close the case.",
"actionMissingWalletTxn": "Open both the transfer order and wallet ledger to confirm whether a compensating wallet entry should be added.",
"actionUnexpectedWalletTxn": "Check for duplicate posting or an incorrect compensation entry, and reverse it if needed.",
"actionMissingRefund": "Confirm whether the main site already refunded the player, then add the lottery-side refund entry or reverse the order.",
"actionMissingReversal": "Confirm the reversal result externally, then add the matching lottery-side reversal ledger entry.",
"actionMatched": "No action needed.",
"actionPendingCheck": "Continue verification with the transfer order and wallet ledger.",
"actionResolved": "This exception has already been handled. Current transfer-order status: {{status}}. Open the transfer order if you want to verify the result.",
"transferStatusSuccess": "Successful",
"transferStatusReversed": "Reversed",
"transferStatusManual": "Case closed"
}

View File

@@ -35,6 +35,7 @@
"loadFailed": "Failed to load export jobs",
"downloadSuccess": "Downloaded {{jobNo}}",
"downloadFailed": "Download failed",
"currentReportHint": "Only export tasks for the currently selected report are shown here.",
"columns": {
"jobNo": "Job no.",
"report": "Report",
@@ -191,6 +192,8 @@
"stats": {
"records": "Records",
"currentPage": "This page",
"notQueried": "Not queried",
"notSet": "Not set",
"drawNo": "Draw no.",
"currency": "Currency",
"exportRows": "Export rows",
@@ -225,12 +228,10 @@
"status": "Status",
"createdAt": "Created at"
},
"legacyTitle": "Legacy wallet reports",
"categories": {
"all": "All",
"profit": "Profit",
"wallet": "Funds",
"legacy": "Legacy",
"risk": "Risk",
"audit": "Audit"
},

View File

@@ -25,6 +25,7 @@
"actualDeduct": "Actual deduct",
"status": "Status",
"actions": "Actions",
"viewTicketInList": "View this ticket",
"failReason": "Fail reason",
"winAmount": "Win amount",
"placedAt": "Placed at",