feat(i18n): add batch group switch text to English, Nepali, and Chinese locales
- Updated the English, Nepali, and Chinese locale files to include a new translation for "Toggle batch switch for {{group}}".
- Enhanced internationalization support for the admin interface by adding relevant strings for improved user experience.
This commit is contained in:
@@ -57,6 +57,15 @@ function oddsMultiplierLabel(oddsValue: number): string {
|
||||
return (oddsValue / 10000).toFixed(4);
|
||||
}
|
||||
|
||||
function parseOddsMultiplierInput(raw: string): number {
|
||||
const n = Number.parseFloat(raw);
|
||||
if (!Number.isFinite(n) || n < 0) {
|
||||
return 0;
|
||||
}
|
||||
const scaled = Math.round(n * 10000);
|
||||
return Number.isSafeInteger(scaled) ? scaled : 0;
|
||||
}
|
||||
|
||||
function filterTypes(tab: CatTab, types: AdminPlayTypeRow[]): AdminPlayTypeRow[] {
|
||||
if (tab === "all") {
|
||||
return types;
|
||||
@@ -515,19 +524,19 @@ export function OddsConfigDocScreen({ embedded = false }: OddsConfigDocScreenPro
|
||||
{canEditDraft ? (
|
||||
<Input
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
inputMode="decimal"
|
||||
className="h-9 max-w-[200px] font-mono tabular-nums"
|
||||
disabled={saving}
|
||||
value={row.odds_value}
|
||||
value={oddsMultiplierLabel(row.odds_value)}
|
||||
onChange={(e) =>
|
||||
updateOddsForScope(scope, {
|
||||
odds_value: Number.parseInt(e.target.value, 10) || 0,
|
||||
odds_value: parseOddsMultiplierInput(e.target.value),
|
||||
})
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<ConfigReadonlyValue mono className="max-w-[200px]">
|
||||
{row.odds_value}
|
||||
{oddsMultiplierLabel(row.odds_value)}
|
||||
</ConfigReadonlyValue>
|
||||
)}
|
||||
{!embedded ? (
|
||||
@@ -610,9 +619,11 @@ export function OddsConfigDocScreen({ embedded = false }: OddsConfigDocScreenPro
|
||||
<div key={row.scope} className="grid grid-cols-3 px-3 py-2 text-sm">
|
||||
<span>{row.label}</span>
|
||||
<span className="text-right font-mono tabular-nums">
|
||||
{row.oldValue === null ? "—" : row.oldValue}
|
||||
{row.oldValue === null ? "—" : oddsMultiplierLabel(row.oldValue)}
|
||||
</span>
|
||||
<span className="text-right font-mono tabular-nums">
|
||||
{row.newValue === null ? "—" : oddsMultiplierLabel(row.newValue)}
|
||||
</span>
|
||||
<span className="text-right font-mono tabular-nums">{row.newValue ?? "—"}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@ import { ConfigChipGroup } from "@/modules/config/config-chip-group";
|
||||
import { ConfigContextBanner, ConfigContextEmphasis } from "@/modules/config/config-context-banner";
|
||||
import { ConfigDocPage, ConfigDocToolbar } from "@/modules/config/config-doc-page";
|
||||
import { ConfigSection } from "@/modules/config/config-section";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -29,7 +29,6 @@ import {
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -46,6 +45,7 @@ import { ConfigVersionSwitcher } from "@/modules/config/config-version-switcher"
|
||||
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
|
||||
import { useConfirmAction } from "@/hooks/use-confirm-action";
|
||||
import { adminHasAnyPermission } from "@/lib/admin-permissions";
|
||||
import { formatAdminMinorDecimal, parseAdminMajorToMinor } from "@/lib/money";
|
||||
import { PRD_PLAY_SWITCH_MANAGE } from "@/lib/admin-prd";
|
||||
import { useAdminProfile } from "@/stores/admin-session";
|
||||
import { LotteryApiBizError } from "@/types/api/errors";
|
||||
@@ -151,9 +151,6 @@ export function PlayConfigDocScreen() {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const detailRequestSeq = useRef(0);
|
||||
|
||||
const [nameDialogOpen, setNameDialogOpen] = useState(false);
|
||||
const [namePlayCode, setNamePlayCode] = useState<string | null>(null);
|
||||
const [nameDraft, setNameDraft] = useState("");
|
||||
const [ruleDialogOpen, setRuleDialogOpen] = useState(false);
|
||||
const [rulePlayCode, setRulePlayCode] = useState<string | null>(null);
|
||||
const [ruleDraftZh, setRuleDraftZh] = useState("");
|
||||
@@ -258,6 +255,7 @@ export function PlayConfigDocScreen() {
|
||||
const isSelectedDetail = detail !== null && String(detail.id) === selectedId;
|
||||
const selectedStatus = isSelectedDetail ? detail.status : selectedVersionSummary?.status;
|
||||
const isDraft = selectedStatus === "draft";
|
||||
const amountCurrencyCode = "NPR";
|
||||
|
||||
const orderedRows = useMemo(
|
||||
() =>
|
||||
@@ -373,30 +371,6 @@ export function PlayConfigDocScreen() {
|
||||
}
|
||||
}
|
||||
|
||||
function openNameEditor(play_code: string) {
|
||||
const item = draftRows.find((row) => row.play_code === play_code);
|
||||
setNamePlayCode(play_code);
|
||||
setNameDraft(item?.display_name ?? item?.play_code ?? "");
|
||||
setNameDialogOpen(true);
|
||||
}
|
||||
|
||||
function saveNameDraft() {
|
||||
if (!namePlayCode) {
|
||||
return;
|
||||
}
|
||||
const name = nameDraft.trim();
|
||||
if (!name) {
|
||||
toast.error(t("play.validation.displayNameRequired", { ns: "config" }));
|
||||
return;
|
||||
}
|
||||
updateConfigRow(namePlayCode, {
|
||||
display_name: name,
|
||||
});
|
||||
setNameDialogOpen(false);
|
||||
setNamePlayCode(null);
|
||||
toast.message(t("play.nameDialog.savedLocal", { ns: "config" }));
|
||||
}
|
||||
|
||||
function openRuleEditor(play_code: string) {
|
||||
const item = draftRows.find((row) => row.play_code === play_code);
|
||||
setRulePlayCode(play_code);
|
||||
@@ -501,52 +475,53 @@ export function PlayConfigDocScreen() {
|
||||
description={!isDraft ? t("play.readOnlyDraftHint", { ns: "config" }) : undefined}
|
||||
>
|
||||
<ConfigChipGroup>
|
||||
{batchSwitchStates.map((group) => (
|
||||
<div
|
||||
key={group.key}
|
||||
className="flex items-center gap-3 rounded-xl border border-border/60 bg-card px-4 py-3"
|
||||
>
|
||||
<div className="min-w-[100px]">
|
||||
<p className="text-sm font-medium text-foreground">{group.label}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{group.total > 0
|
||||
? t("play.batchEnabledCount", {
|
||||
ns: "config",
|
||||
enabledCount: group.enabledCount,
|
||||
total: group.total,
|
||||
})
|
||||
: t("play.noPlayTypes", { ns: "config" })}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant={group.allEnabled ? "secondary" : "outline"}
|
||||
disabled={!isDraft || saving || group.total === 0}
|
||||
onClick={() => {
|
||||
const enable = !group.allEnabled;
|
||||
const action = enable
|
||||
? t("play.batchSwitchEnable", { ns: "config" })
|
||||
: t("play.batchSwitchDisable", { ns: "config" });
|
||||
requestConfirm({
|
||||
title: t("play.batchSwitchConfirmTitle", { ns: "config", action }),
|
||||
description: t("play.batchSwitchConfirmDescription", {
|
||||
ns: "config",
|
||||
action,
|
||||
group: group.label,
|
||||
count: group.total,
|
||||
}),
|
||||
confirmVariant: enable ? "default" : "destructive",
|
||||
onConfirm: () => applyBatchSwitch(group, enable),
|
||||
});
|
||||
}}
|
||||
{batchSwitchStates.map((group) => {
|
||||
const groupOn = group.enabledCount > 0;
|
||||
return (
|
||||
<div
|
||||
key={group.key}
|
||||
className="flex items-center justify-between gap-4 rounded-xl border border-border/60 bg-card px-4 py-3"
|
||||
>
|
||||
{group.allEnabled
|
||||
? t("play.actions.disable", { ns: "config" })
|
||||
: t("play.actions.enable", { ns: "config" })}
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-medium text-foreground">{group.label}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{group.total > 0
|
||||
? t("play.batchEnabledCount", {
|
||||
ns: "config",
|
||||
enabledCount: group.enabledCount,
|
||||
total: group.total,
|
||||
})
|
||||
: t("play.noPlayTypes", { ns: "config" })}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={groupOn}
|
||||
disabled={!isDraft || saving || group.total === 0}
|
||||
aria-label={t("play.aria.batchGroupSwitch", {
|
||||
ns: "config",
|
||||
group: group.label,
|
||||
})}
|
||||
onCheckedChange={(checked) => {
|
||||
const enable = checked;
|
||||
const action = enable
|
||||
? t("play.batchSwitchEnable", { ns: "config" })
|
||||
: t("play.batchSwitchDisable", { ns: "config" });
|
||||
requestConfirm({
|
||||
title: t("play.batchSwitchConfirmTitle", { ns: "config", action }),
|
||||
description: t("play.batchSwitchConfirmDescription", {
|
||||
ns: "config",
|
||||
action,
|
||||
group: group.label,
|
||||
count: group.total,
|
||||
}),
|
||||
confirmVariant: enable ? "default" : "destructive",
|
||||
onConfirm: () => applyBatchSwitch(group, enable),
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ConfigChipGroup>
|
||||
</ConfigSection>
|
||||
) : null}
|
||||
@@ -562,7 +537,7 @@ export function PlayConfigDocScreen() {
|
||||
<TableHead className="text-center">{t("play.table.playCode", { ns: "config" })}</TableHead>
|
||||
<TableHead className="w-[100px] text-center">{t("play.table.category", { ns: "config" })}</TableHead>
|
||||
<TableHead className="w-[88px] text-center">{t("play.table.status", { ns: "config" })}</TableHead>
|
||||
<TableHead className="min-w-[120px] text-center">{t("play.table.displayName", { ns: "config" })}</TableHead>
|
||||
<TableHead className="w-32 text-center">{t("play.table.displayName", { ns: "config" })}</TableHead>
|
||||
<TableHead className="w-[120px] text-center">{t("play.table.order", { ns: "config" })}</TableHead>
|
||||
<TableHead className="w-[110px] text-center">{t("play.table.minBet", { ns: "config" })}</TableHead>
|
||||
<TableHead className="w-[110px] text-center">{t("play.table.maxBet", { ns: "config" })}</TableHead>
|
||||
@@ -575,12 +550,16 @@ export function PlayConfigDocScreen() {
|
||||
<TableCell className="text-center font-mono text-sm">{row.play_code}</TableCell>
|
||||
<TableCell className="text-center text-muted-foreground text-sm">{row.category ?? "—"}</TableCell>
|
||||
<TableCell className="text-center">
|
||||
{isDraft ? (
|
||||
<Checkbox
|
||||
<div className="flex justify-center">
|
||||
<Switch
|
||||
checked={row.is_enabled}
|
||||
disabled={saving}
|
||||
onCheckedChange={(v) => {
|
||||
const enabled = v === true;
|
||||
disabled={!isDraft || saving}
|
||||
aria-label={t("play.aria.enablePlay", { ns: "config", playCode: row.play_code })}
|
||||
onCheckedChange={(checked) => {
|
||||
if (!isDraft) {
|
||||
return;
|
||||
}
|
||||
const enabled = checked;
|
||||
const action = enabled
|
||||
? t("play.toggleEnable", { ns: "config" })
|
||||
: t("play.toggleDisable", { ns: "config" });
|
||||
@@ -598,35 +577,27 @@ export function PlayConfigDocScreen() {
|
||||
},
|
||||
});
|
||||
}}
|
||||
aria-label={t("play.aria.enablePlay", { ns: "config", playCode: row.play_code })}
|
||||
/>
|
||||
) : (
|
||||
<div className="flex justify-center">
|
||||
<AdminStatusBadge status={row.is_enabled ? "enabled" : "disabled"}>
|
||||
{row.is_enabled
|
||||
? t("play.states.enabled", { ns: "config" })
|
||||
: t("play.states.disabled", { ns: "config" })}
|
||||
</AdminStatusBadge>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<TableCell className="w-32 text-center">
|
||||
{isDraft ? (
|
||||
<div className="flex flex-col items-center gap-1.5">
|
||||
<p className="max-w-[10rem] truncate text-sm font-medium">
|
||||
{row.display_name ?? row.play_code}
|
||||
</p>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-7 text-xs"
|
||||
disabled={saving}
|
||||
onClick={() => openNameEditor(row.play_code)}
|
||||
>
|
||||
{t("play.actions.editDisplayName", { ns: "config" })}
|
||||
</Button>
|
||||
</div>
|
||||
<Input
|
||||
type="text"
|
||||
className="mx-auto h-8 w-28 text-center text-sm"
|
||||
disabled={saving}
|
||||
value={row.display_name ?? ""}
|
||||
placeholder={row.play_code}
|
||||
onChange={(e) =>
|
||||
updateConfigRow(row.play_code, { display_name: e.target.value })
|
||||
}
|
||||
onBlur={(e) => {
|
||||
const trimmed = e.target.value.trim();
|
||||
updateConfigRow(row.play_code, {
|
||||
display_name: trimmed || row.play_code,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ConfigReadonlyValue className="justify-center">
|
||||
{renderDisplayNameReadonly(row)}
|
||||
@@ -658,19 +629,20 @@ export function PlayConfigDocScreen() {
|
||||
{isDraft ? (
|
||||
<Input
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
inputMode="decimal"
|
||||
className="h-8 text-center font-mono tabular-nums"
|
||||
disabled={saving}
|
||||
value={row.min_bet_amount}
|
||||
value={formatAdminMinorDecimal(row.min_bet_amount, amountCurrencyCode)}
|
||||
onChange={(e) =>
|
||||
updateConfigRow(row.play_code, {
|
||||
min_bet_amount: Number.parseInt(e.target.value, 10) || 0,
|
||||
min_bet_amount:
|
||||
parseAdminMajorToMinor(e.target.value, amountCurrencyCode) ?? 0,
|
||||
})
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<ConfigReadonlyValue mono className="justify-center">
|
||||
{row.min_bet_amount}
|
||||
{formatAdminMinorDecimal(row.min_bet_amount, amountCurrencyCode)}
|
||||
</ConfigReadonlyValue>
|
||||
)}
|
||||
</TableCell>
|
||||
@@ -678,19 +650,20 @@ export function PlayConfigDocScreen() {
|
||||
{isDraft ? (
|
||||
<Input
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
inputMode="decimal"
|
||||
className="h-8 text-center font-mono tabular-nums"
|
||||
disabled={saving}
|
||||
value={row.max_bet_amount}
|
||||
value={formatAdminMinorDecimal(row.max_bet_amount, amountCurrencyCode)}
|
||||
onChange={(e) =>
|
||||
updateConfigRow(row.play_code, {
|
||||
max_bet_amount: Number.parseInt(e.target.value, 10) || 0,
|
||||
max_bet_amount:
|
||||
parseAdminMajorToMinor(e.target.value, amountCurrencyCode) ?? 0,
|
||||
})
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<ConfigReadonlyValue mono className="justify-center">
|
||||
{row.max_bet_amount}
|
||||
{formatAdminMinorDecimal(row.max_bet_amount, amountCurrencyCode)}
|
||||
</ConfigReadonlyValue>
|
||||
)}
|
||||
</TableCell>
|
||||
@@ -716,33 +689,6 @@ export function PlayConfigDocScreen() {
|
||||
</Table>
|
||||
)}
|
||||
|
||||
<Dialog open={nameDialogOpen} onOpenChange={setNameDialogOpen}>
|
||||
<DialogContent showCloseButton className="sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("play.nameDialog.title", { ns: "config" })}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{t("play.nameDialog.description", { ns: "config", playCode: namePlayCode ?? "—" })}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor="play-display-name">{t("play.table.displayName", { ns: "config" })}</Label>
|
||||
<Input
|
||||
id="play-display-name"
|
||||
value={nameDraft}
|
||||
onChange={(e) => setNameDraft(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button type="button" variant="outline" onClick={() => setNameDialogOpen(false)}>
|
||||
{t("actions.cancel", { ns: "adminUsers" })}
|
||||
</Button>
|
||||
<Button type="button" onClick={saveNameDraft}>
|
||||
{t("play.nameDialog.apply", { ns: "config" })}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<Dialog open={ruleDialogOpen} onOpenChange={setRuleDialogOpen}>
|
||||
<DialogContent showCloseButton className="sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from "@/api/admin-config";
|
||||
import { ConfigContextBanner, ConfigContextEmphasis } from "@/modules/config/config-context-banner";
|
||||
import { ConfigDocPage, ConfigDocToolbar } from "@/modules/config/config-doc-page";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { ConfigReadonlyValue } from "@/modules/config/config-readonly-value";
|
||||
@@ -40,9 +40,9 @@ import { PRIZE_SCOPE_ORDER } from "@/modules/config/doc/prize-scopes";
|
||||
function rateToPercentUi(rateStr: string): string {
|
||||
const n = Number.parseFloat(rateStr);
|
||||
if (!Number.isFinite(n)) {
|
||||
return "0";
|
||||
return "0.00";
|
||||
}
|
||||
return String(Math.round(n * 10000) / 100);
|
||||
return (Math.round(n * 10000) / 100).toFixed(2);
|
||||
}
|
||||
|
||||
function inferPercentFrom(dim: 2 | 3 | 4, rows: OddsItemRow[], typeList: AdminPlayTypeRow[]): string {
|
||||
@@ -391,19 +391,11 @@ export function RebateConfigDocScreen({ embedded = false }: RebateConfigDocScree
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3 px-1">
|
||||
<Checkbox
|
||||
id="win-enjoy"
|
||||
checked
|
||||
aria-disabled
|
||||
disabled
|
||||
aria-label={t("rebate.winEnjoy.label", { ns: "config" })}
|
||||
/>
|
||||
<div className="grid gap-1">
|
||||
<Label htmlFor="win-enjoy" className="font-medium leading-snug">
|
||||
{t("rebate.winEnjoy.label", { ns: "config" })}
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-3 rounded-xl border border-border/60 px-4 py-3">
|
||||
<Label htmlFor="win-enjoy" className="font-medium leading-snug">
|
||||
{t("rebate.winEnjoy.label", { ns: "config" })}
|
||||
</Label>
|
||||
<Switch id="win-enjoy" checked disabled aria-label={t("rebate.winEnjoy.label", { ns: "config" })} />
|
||||
</div>
|
||||
|
||||
{!embedded ? (
|
||||
|
||||
@@ -41,6 +41,7 @@ import { ConfigVersionActions } from "@/modules/config/config-version-actions";
|
||||
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
|
||||
import { useConfirmAction } from "@/hooks/use-confirm-action";
|
||||
import { adminHasAnyPermission } from "@/lib/admin-permissions";
|
||||
import { formatAdminMinorDecimal, parseAdminMajorToMinor } from "@/lib/money";
|
||||
import { PRD_RISK_CAP_MANAGE, PRD_RISK_CAP_VIEW } from "@/lib/admin-prd";
|
||||
import { useAdminProfile } from "@/stores/admin-session";
|
||||
import { LotteryApiBizError } from "@/types/api/errors";
|
||||
@@ -95,6 +96,7 @@ export function RiskCapDocScreen() {
|
||||
const [syncOpen, setSyncOpen] = useState(false);
|
||||
|
||||
const [occSearch, setOccSearch] = useState("");
|
||||
const amountCurrencyCode = "NPR";
|
||||
|
||||
const refreshList = useCallback(async () => {
|
||||
setLoadingList(true);
|
||||
@@ -123,7 +125,7 @@ export function RiskCapDocScreen() {
|
||||
setDefaultCapStr("");
|
||||
return;
|
||||
}
|
||||
setDefaultCapStr(String(defaultRow.cap_amount));
|
||||
setDefaultCapStr(formatAdminMinorDecimal(defaultRow.cap_amount, amountCurrencyCode));
|
||||
}
|
||||
|
||||
const loadDetail = useCallback(async (id: number) => {
|
||||
@@ -299,7 +301,7 @@ export function RiskCapDocScreen() {
|
||||
}
|
||||
|
||||
function applyDefaultCap() {
|
||||
const n = Number.parseInt(defaultCapStr, 10);
|
||||
const n = parseAdminMajorToMinor(defaultCapStr, amountCurrencyCode);
|
||||
if (!Number.isFinite(n) || n <= 0) {
|
||||
toast.error(t("riskCap.validation.enterValidCapAmount", { ns: "config" }));
|
||||
return;
|
||||
@@ -408,7 +410,7 @@ export function RiskCapDocScreen() {
|
||||
/>
|
||||
) : (
|
||||
<ConfigReadonlyValue mono className="w-[220px]">
|
||||
{defaultCapStr || "—"}
|
||||
{defaultCapStr || formatAdminMinorDecimal(0, amountCurrencyCode)}
|
||||
</ConfigReadonlyValue>
|
||||
)}
|
||||
</div>
|
||||
@@ -474,19 +476,22 @@ export function RiskCapDocScreen() {
|
||||
<TableCell>
|
||||
{canEditDraft ? (
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
className="h-8 font-mono tabular-nums"
|
||||
disabled={saving}
|
||||
value={r.cap_amount}
|
||||
value={formatAdminMinorDecimal(r.cap_amount, amountCurrencyCode)}
|
||||
onChange={(e) =>
|
||||
updateRow(idx, {
|
||||
cap_amount: Number.parseInt(e.target.value, 10) || 0,
|
||||
cap_amount:
|
||||
parseAdminMajorToMinor(e.target.value, amountCurrencyCode) ?? 0,
|
||||
})
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<ConfigReadonlyValue mono>{r.cap_amount}</ConfigReadonlyValue>
|
||||
<ConfigReadonlyValue mono>
|
||||
{formatAdminMinorDecimal(r.cap_amount, amountCurrencyCode)}
|
||||
</ConfigReadonlyValue>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className="text-right text-muted-foreground tabular-nums text-sm">—</TableCell>
|
||||
|
||||
Reference in New Issue
Block a user