From 71a15cda1ebbe21441d18244fe87390b739d174f Mon Sep 17 00:00:00 2001 From: A-Words Date: Mon, 29 Jun 2026 21:43:18 +0800 Subject: [PATCH 1/3] fix(wallet): honor configured quota units for reward transfers --- .../general/quota-settings-section.tsx | 10 ++-- .../components/dialogs/transfer-dialog.tsx | 49 ++++++++++++++----- web/default/src/features/wallet/constants.ts | 5 -- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/web/default/src/features/system-settings/general/quota-settings-section.tsx b/web/default/src/features/system-settings/general/quota-settings-section.tsx index a8a837004082..6bd24767c0b3 100644 --- a/web/default/src/features/system-settings/general/quota-settings-section.tsx +++ b/web/default/src/features/system-settings/general/quota-settings-section.tsx @@ -21,6 +21,7 @@ import * as z from 'zod' import type { Resolver } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { useTranslation } from 'react-i18next' +import { formatQuota } from '@/lib/format' import { Alert, AlertDescription } from '@/components/ui/alert' import { Form, @@ -139,7 +140,8 @@ export function QuotaSettingsSection({ /> - {t('Initial quota given to new users')} + {t('Initial quota given to new users')} ( + {formatQuota(field.value ?? 0)}) @@ -187,7 +189,8 @@ export function QuotaSettingsSection({ /> - {t('Quota given to users who invite others')} + {t('Quota given to users who invite others')} ( + {formatQuota(field.value ?? 0)}) @@ -211,7 +214,8 @@ export function QuotaSettingsSection({ /> - {t('Quota given to invited users')} + {t('Quota given to invited users')} ( + {formatQuota(field.value ?? 0)}) diff --git a/web/default/src/features/wallet/components/dialogs/transfer-dialog.tsx b/web/default/src/features/wallet/components/dialogs/transfer-dialog.tsx index 5ea9b418c2d9..6888cc74f299 100644 --- a/web/default/src/features/wallet/components/dialogs/transfer-dialog.tsx +++ b/web/default/src/features/wallet/components/dialogs/transfer-dialog.tsx @@ -19,12 +19,19 @@ For commercial licensing, please contact support@quantumnous.com import { useState, useEffect } from 'react' import { Loader2 } from 'lucide-react' import { useTranslation } from 'react-i18next' -import { formatQuota } from '@/lib/format' +import { + formatQuota, + parseQuotaFromDollars, + quotaUnitsToDollars, +} from '@/lib/format' +import { + DEFAULT_CURRENCY_CONFIG, + useSystemConfigStore, +} from '@/stores/system-config-store' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Dialog } from '@/components/dialog' -import { QUOTA_PER_DOLLAR } from '../../constants' interface TransferDialogProps { open: boolean @@ -42,17 +49,34 @@ export function TransferDialog({ transferring, }: TransferDialogProps) { const { t } = useTranslation() - const [amount, setAmount] = useState(QUOTA_PER_DOLLAR) + const currencyConfig = useSystemConfigStore( + (state) => state.config.currency + ) + const minimumQuota = Math.ceil( + currencyConfig.quotaPerUnit > 0 + ? currencyConfig.quotaPerUnit + : DEFAULT_CURRENCY_CONFIG.quotaPerUnit + ) + const minimumAmount = quotaUnitsToDollars(minimumQuota) + const maximumAmount = quotaUnitsToDollars(availableQuota) + const [amount, setAmount] = useState(minimumAmount) + const transferQuota = parseQuotaFromDollars(amount) + const canTransfer = + Number.isFinite(amount) && + transferQuota >= minimumQuota && + transferQuota <= availableQuota useEffect(() => { if (open) { // eslint-disable-next-line react-hooks/set-state-in-effect - setAmount(QUOTA_PER_DOLLAR) + setAmount(minimumAmount) } - }, [open]) + }, [minimumAmount, open]) const handleConfirm = async () => { - const success = await onConfirm(amount) + if (!canTransfer) return + + const success = await onConfirm(transferQuota) if (success) { onOpenChange(false) } @@ -78,7 +102,10 @@ export function TransferDialog({ > {t('Cancel')} - @@ -107,13 +134,13 @@ export function TransferDialog({ type='number' value={amount} onChange={(e) => setAmount(Number(e.target.value))} - min={QUOTA_PER_DOLLAR} - max={availableQuota} - step={QUOTA_PER_DOLLAR} + min={minimumAmount} + max={maximumAmount} + step={minimumAmount} className='font-mono text-lg' />

- {t('Minimum:')} {formatQuota(QUOTA_PER_DOLLAR)} + {t('Minimum:')} {formatQuota(minimumQuota)}

diff --git a/web/default/src/features/wallet/constants.ts b/web/default/src/features/wallet/constants.ts index 3f4127884f00..50bb45eb5b41 100644 --- a/web/default/src/features/wallet/constants.ts +++ b/web/default/src/features/wallet/constants.ts @@ -55,11 +55,6 @@ export const PAYMENT_ICON_COLORS = { [PAYMENT_TYPES.WAFFO_PANCAKE]: '#F97316', } as const -/** - * Quota conversion rate: 500,000 units = $1 - */ -export const QUOTA_PER_DOLLAR = 500000 - /** * Default discount rate (no discount) */ From a33469b7820b317dceea18d62485ee523c391a33 Mon Sep 17 00:00:00 2001 From: A-Words Date: Tue, 30 Jun 2026 01:53:55 +0800 Subject: [PATCH 2/3] fix(i18n): localize quota preview descriptions --- .../general/quota-settings-section.tsx | 36 +++++++++++++------ web/default/src/i18n/locales/en.json | 3 ++ web/default/src/i18n/locales/fr.json | 3 ++ web/default/src/i18n/locales/ja.json | 3 ++ web/default/src/i18n/locales/ru.json | 3 ++ web/default/src/i18n/locales/vi.json | 3 ++ web/default/src/i18n/locales/zh.json | 3 ++ 7 files changed, 44 insertions(+), 10 deletions(-) diff --git a/web/default/src/features/system-settings/general/quota-settings-section.tsx b/web/default/src/features/system-settings/general/quota-settings-section.tsx index 6bd24767c0b3..391996d4b2e6 100644 --- a/web/default/src/features/system-settings/general/quota-settings-section.tsx +++ b/web/default/src/features/system-settings/general/quota-settings-section.tsx @@ -16,12 +16,12 @@ along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ +import { zodResolver } from '@hookform/resolvers/zod' import type { ChangeEvent } from 'react' -import * as z from 'zod' import type { Resolver } from 'react-hook-form' -import { zodResolver } from '@hookform/resolvers/zod' import { useTranslation } from 'react-i18next' -import { formatQuota } from '@/lib/format' +import * as z from 'zod' + import { Alert, AlertDescription } from '@/components/ui/alert' import { Form, @@ -34,6 +34,8 @@ import { } from '@/components/ui/form' import { Input } from '@/components/ui/input' import { Switch } from '@/components/ui/switch' +import { formatQuota } from '@/lib/format' + import { FormDirtyIndicator } from '../components/form-dirty-indicator' import { FormNavigationGuard } from '../components/form-navigation-guard' import { @@ -63,6 +65,11 @@ const quotaSchema = z.object({ }) type QuotaFormValues = z.infer +type QuotaInputValue = number | '' + +function formatQuotaInputValue(value: QuotaInputValue): string { + return formatQuota(value === '' ? 0 : value) +} type QuotaSettingsSectionProps = { defaultValues: QuotaFormValues @@ -76,7 +83,7 @@ export function QuotaSettingsSection({ const { t } = useTranslation() const updateOption = useUpdateOption() const handleNumberChange = - (onChange: (value: number | string) => void) => + (onChange: (value: QuotaInputValue) => void) => (event: ChangeEvent) => { onChange( event.target.value === '' ? '' : event.currentTarget.valueAsNumber @@ -140,8 +147,12 @@ export function QuotaSettingsSection({ /> - {t('Initial quota given to new users')} ( - {formatQuota(field.value ?? 0)}) + {t( + 'Initial quota given to new users ({{formattedQuota}})', + { + formattedQuota: formatQuotaInputValue(field.value), + } + )} @@ -189,8 +200,12 @@ export function QuotaSettingsSection({ /> - {t('Quota given to users who invite others')} ( - {formatQuota(field.value ?? 0)}) + {t( + 'Quota given to users who invite others ({{formattedQuota}})', + { + formattedQuota: formatQuotaInputValue(field.value), + } + )} @@ -214,8 +229,9 @@ export function QuotaSettingsSection({ /> - {t('Quota given to invited users')} ( - {formatQuota(field.value ?? 0)}) + {t('Quota given to invited users ({{formattedQuota}})', { + formattedQuota: formatQuotaInputValue(field.value), + })} diff --git a/web/default/src/i18n/locales/en.json b/web/default/src/i18n/locales/en.json index 569dd49992c6..8bd16f95d7c3 100644 --- a/web/default/src/i18n/locales/en.json +++ b/web/default/src/i18n/locales/en.json @@ -2217,6 +2217,7 @@ "Increased user quota by {{quota}}": "Increased user quota by {{quota}}", "Index": "Index", "Initial quota given to new users": "Initial quota given to new users", + "Initial quota given to new users ({{formattedQuota}})": "Initial quota given to new users ({{formattedQuota}})", "Initialization failed, please try again.": "Initialization failed, please try again.", "Initialize": "Initialize", "Initialize system": "Initialize system", @@ -3438,7 +3439,9 @@ "Quota consumed before charging users": "Quota consumed before charging users", "Quota Distribution": "Quota Distribution", "Quota given to invited users": "Quota given to invited users", + "Quota given to invited users ({{formattedQuota}})": "Quota given to invited users ({{formattedQuota}})", "Quota given to users who invite others": "Quota given to users who invite others", + "Quota given to users who invite others ({{formattedQuota}})": "Quota given to users who invite others ({{formattedQuota}})", "Quota must be a positive number": "Quota must be a positive number", "Quota must be zero or greater": "Quota must be zero or greater", "Quota Per Unit": "Quota Per Unit", diff --git a/web/default/src/i18n/locales/fr.json b/web/default/src/i18n/locales/fr.json index 8ff9c68a2950..28aac94970f6 100644 --- a/web/default/src/i18n/locales/fr.json +++ b/web/default/src/i18n/locales/fr.json @@ -2217,6 +2217,7 @@ "Increased user quota by {{quota}}": "Quota de l'utilisateur augmenté de {{quota}}", "Index": "Index", "Initial quota given to new users": "Quota initial donné aux nouveaux utilisateurs", + "Initial quota given to new users ({{formattedQuota}})": "Quota initial donné aux nouveaux utilisateurs ({{formattedQuota}})", "Initialization failed, please try again.": "L'initialisation a échoué, veuillez réessayer.", "Initialize": "Initialiser", "Initialize system": "Initialiser le système", @@ -3438,7 +3439,9 @@ "Quota consumed before charging users": "Quota consommé avant de facturer les utilisateurs", "Quota Distribution": "Distribution des quotas", "Quota given to invited users": "Quota attribué aux utilisateurs invités", + "Quota given to invited users ({{formattedQuota}})": "Quota attribué aux utilisateurs invités ({{formattedQuota}})", "Quota given to users who invite others": "Quota attribué aux utilisateurs qui invitent d'autres personnes", + "Quota given to users who invite others ({{formattedQuota}})": "Quota attribué aux utilisateurs qui invitent d'autres personnes ({{formattedQuota}})", "Quota must be a positive number": "Le quota doit être un nombre positif", "Quota must be zero or greater": "Le quota ne peut pas être négatif", "Quota Per Unit": "Quota par unité", diff --git a/web/default/src/i18n/locales/ja.json b/web/default/src/i18n/locales/ja.json index 1bbcf897fdac..4350e838f5da 100644 --- a/web/default/src/i18n/locales/ja.json +++ b/web/default/src/i18n/locales/ja.json @@ -2217,6 +2217,7 @@ "Increased user quota by {{quota}}": "ユーザーのクォータを {{quota}} 増やしました", "Index": "インデックス", "Initial quota given to new users": "新規ユーザーに付与される初期クォータ", + "Initial quota given to new users ({{formattedQuota}})": "新規ユーザーに付与される初期クォータ({{formattedQuota}})", "Initialization failed, please try again.": "初期化に失敗しました。もう一度お試しください。", "Initialize": "初期化", "Initialize system": "システム初期化", @@ -3438,7 +3439,9 @@ "Quota consumed before charging users": "ユーザーに請求する前に消費されるクォータ", "Quota Distribution": "クォータの分配", "Quota given to invited users": "招待されたユーザーに付与されるクォータ", + "Quota given to invited users ({{formattedQuota}})": "招待されたユーザーに付与されるクォータ({{formattedQuota}})", "Quota given to users who invite others": "他のユーザーを招待したユーザーに付与されるクォータ", + "Quota given to users who invite others ({{formattedQuota}})": "他のユーザーを招待したユーザーに付与されるクォータ({{formattedQuota}})", "Quota must be a positive number": "クォータは正の数である必要があります", "Quota must be zero or greater": "クォータは負の値にできません", "Quota Per Unit": "ユニットあたりのクォータ", diff --git a/web/default/src/i18n/locales/ru.json b/web/default/src/i18n/locales/ru.json index a9044cb6fa09..3872ddf25d13 100644 --- a/web/default/src/i18n/locales/ru.json +++ b/web/default/src/i18n/locales/ru.json @@ -2217,6 +2217,7 @@ "Increased user quota by {{quota}}": "Квота пользователя увеличена на {{quota}}", "Index": "Индекс", "Initial quota given to new users": "Начальная квота, предоставляемая новым пользователям", + "Initial quota given to new users ({{formattedQuota}})": "Начальная квота, предоставляемая новым пользователям ({{formattedQuota}})", "Initialization failed, please try again.": "Инициализация не удалась, попробуйте ещё раз.", "Initialize": "Инициализировать", "Initialize system": "Инициализация системы", @@ -3438,7 +3439,9 @@ "Quota consumed before charging users": "Квота, потребляемая до взимания платы с пользователей", "Quota Distribution": "Распределение квоты", "Quota given to invited users": "Квота, предоставляемая приглашенным пользователям", + "Quota given to invited users ({{formattedQuota}})": "Квота, предоставляемая приглашенным пользователям ({{formattedQuota}})", "Quota given to users who invite others": "Квота, предоставляемая пользователям, которые приглашают других", + "Quota given to users who invite others ({{formattedQuota}})": "Квота, предоставляемая пользователям, которые приглашают других ({{formattedQuota}})", "Quota must be a positive number": "Квота должна быть положительным числом", "Quota must be zero or greater": "Квота не может быть отрицательной", "Quota Per Unit": "Квота на единицу", diff --git a/web/default/src/i18n/locales/vi.json b/web/default/src/i18n/locales/vi.json index 0fb823789d4f..9a9250bcd8c8 100644 --- a/web/default/src/i18n/locales/vi.json +++ b/web/default/src/i18n/locales/vi.json @@ -2217,6 +2217,7 @@ "Increased user quota by {{quota}}": "Đã tăng hạn mức người dùng thêm {{quota}}", "Index": "Chỉ mục", "Initial quota given to new users": "Hạn mức ban đầu cấp cho người dùng mới", + "Initial quota given to new users ({{formattedQuota}})": "Hạn mức ban đầu cấp cho người dùng mới ({{formattedQuota}})", "Initialization failed, please try again.": "Khởi tạo thất bại, vui lòng thử lại.", "Initialize": "Khởi tạo", "Initialize system": "Khởi tạo hệ thống", @@ -3438,7 +3439,9 @@ "Quota consumed before charging users": "Hạn mức tiêu thụ trước khi tính phí người dùng", "Quota Distribution": "Phân bổ hạn ngạch", "Quota given to invited users": "Hạn mức được cấp cho người dùng được mời", + "Quota given to invited users ({{formattedQuota}})": "Hạn mức cấp cho người dùng được mời ({{formattedQuota}})", "Quota given to users who invite others": "Limit for users inviting others", + "Quota given to users who invite others ({{formattedQuota}})": "Hạn mức cấp cho người dùng mời người khác ({{formattedQuota}})", "Quota must be a positive number": "Hạn mức phải là một số dương", "Quota must be zero or greater": "Hạn mức không được âm", "Quota Per Unit": "Định mức mỗi đơn vị", diff --git a/web/default/src/i18n/locales/zh.json b/web/default/src/i18n/locales/zh.json index e78ac5b441b6..28e24f0d03ab 100644 --- a/web/default/src/i18n/locales/zh.json +++ b/web/default/src/i18n/locales/zh.json @@ -2217,6 +2217,7 @@ "Increased user quota by {{quota}}": "增加用户额度 {{quota}}", "Index": "索引", "Initial quota given to new users": "授予新用户的初始配额", + "Initial quota given to new users ({{formattedQuota}})": "授予新用户的初始配额({{formattedQuota}})", "Initialization failed, please try again.": "初始化失败,请重试。", "Initialize": "初始化", "Initialize system": "初始化系统", @@ -3438,7 +3439,9 @@ "Quota consumed before charging users": "向用户收费前消耗的配额", "Quota Distribution": "消耗分布", "Quota given to invited users": "授予被邀请用户的配额", + "Quota given to invited users ({{formattedQuota}})": "授予被邀请用户的配额({{formattedQuota}})", "Quota given to users who invite others": "授予邀请其他用户的配额", + "Quota given to users who invite others ({{formattedQuota}})": "授予邀请其他用户的配额({{formattedQuota}})", "Quota must be a positive number": "配额必须是正数", "Quota must be zero or greater": "额度不能为负数", "Quota Per Unit": "每单位配额", From de07a020f16dab85ecf5ec50046deeae907b1f5e Mon Sep 17 00:00:00 2001 From: A-Words Date: Tue, 30 Jun 2026 02:16:33 +0800 Subject: [PATCH 3/3] fix(settings): normalize invalid quota input --- .../system-settings/general/quota-settings-section.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/web/default/src/features/system-settings/general/quota-settings-section.tsx b/web/default/src/features/system-settings/general/quota-settings-section.tsx index 391996d4b2e6..b72838ace6ed 100644 --- a/web/default/src/features/system-settings/general/quota-settings-section.tsx +++ b/web/default/src/features/system-settings/general/quota-settings-section.tsx @@ -85,9 +85,8 @@ export function QuotaSettingsSection({ const handleNumberChange = (onChange: (value: QuotaInputValue) => void) => (event: ChangeEvent) => { - onChange( - event.target.value === '' ? '' : event.currentTarget.valueAsNumber - ) + const value = event.currentTarget.valueAsNumber + onChange(Number.isNaN(value) ? '' : value) } const { form, handleSubmit, isDirty, isSubmitting } =