Commit 90fa6fe6 by A_Words Committed by GitHub

fix(wallet): honor configured quota units for reward transfers (#5808)

* fix(wallet): honor configured quota units for reward transfers

* fix(i18n): localize quota preview descriptions

* fix(settings): normalize invalid quota input
parent a72e5082
......@@ -34,6 +34,7 @@ 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'
......@@ -64,6 +65,11 @@ const quotaSchema = z.object({
})
type QuotaFormValues = z.infer<typeof quotaSchema>
type QuotaInputValue = number | ''
function formatQuotaInputValue(value: QuotaInputValue): string {
return formatQuota(value === '' ? 0 : value)
}
type QuotaSettingsSectionProps = {
defaultValues: QuotaFormValues
......@@ -77,11 +83,10 @@ export function QuotaSettingsSection({
const { t } = useTranslation()
const updateOption = useUpdateOption()
const handleNumberChange =
(onChange: (value: number | string) => void) =>
(onChange: (value: QuotaInputValue) => void) =>
(event: ChangeEvent<HTMLInputElement>) => {
onChange(
event.target.value === '' ? '' : event.currentTarget.valueAsNumber
)
const value = event.currentTarget.valueAsNumber
onChange(Number.isNaN(value) ? '' : value)
}
const { form, handleSubmit, isDirty, isSubmitting } =
......@@ -141,7 +146,12 @@ export function QuotaSettingsSection({
/>
</FormControl>
<FormDescription>
{t('Initial quota given to new users')}
{t(
'Initial quota given to new users ({{formattedQuota}})',
{
formattedQuota: formatQuotaInputValue(field.value),
}
)}
</FormDescription>
<FormMessage />
</FormItem>
......@@ -189,7 +199,12 @@ export function QuotaSettingsSection({
/>
</FormControl>
<FormDescription>
{t('Quota given to users who invite others')}
{t(
'Quota given to users who invite others ({{formattedQuota}})',
{
formattedQuota: formatQuotaInputValue(field.value),
}
)}
</FormDescription>
<FormMessage />
</FormItem>
......@@ -213,7 +228,9 @@ export function QuotaSettingsSection({
/>
</FormControl>
<FormDescription>
{t('Quota given to invited users')}
{t('Quota given to invited users ({{formattedQuota}})', {
formattedQuota: formatQuotaInputValue(field.value),
})}
</FormDescription>
<FormMessage />
</FormItem>
......
......@@ -24,9 +24,15 @@ import { Dialog } from '@/components/dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { formatQuota } from '@/lib/format'
import { QUOTA_PER_DOLLAR } from '../../constants'
import {
formatQuota,
parseQuotaFromDollars,
quotaUnitsToDollars,
} from '@/lib/format'
import {
DEFAULT_CURRENCY_CONFIG,
useSystemConfigStore,
} from '@/stores/system-config-store'
interface TransferDialogProps {
open: boolean
......@@ -44,17 +50,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)
}
......@@ -80,7 +103,10 @@ export function TransferDialog({
>
{t('Cancel')}
</Button>
<Button onClick={handleConfirm} disabled={transferring}>
<Button
onClick={handleConfirm}
disabled={transferring || !canTransfer}
>
{transferring && <Loader2 className='mr-2 h-4 w-4 animate-spin' />}
{t('Transfer')}
</Button>
......@@ -109,13 +135,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'
/>
<p className='text-muted-foreground text-xs'>
{t('Minimum:')} {formatQuota(QUOTA_PER_DOLLAR)}
{t('Minimum:')} {formatQuota(minimumQuota)}
</p>
</div>
</div>
......
......@@ -56,11 +56,6 @@ export const PAYMENT_ICON_COLORS = {
} as const
/**
* Quota conversion rate: 500,000 units = $1
*/
export const QUOTA_PER_DOLLAR = 500000
/**
* Default discount rate (no discount)
*/
export const DEFAULT_DISCOUNT_RATE = 1.0
......
......@@ -31,14 +31,14 @@
"extrasCount": 0,
"untranslatedCount": 0
},
"zh": {
"file": "zh.json",
"zh-TW": {
"file": "zh-TW.json",
"missingCount": 0,
"extrasCount": 0,
"untranslatedCount": 0
},
"zh-TW": {
"file": "zh-TW.json",
"zh": {
"file": "zh.json",
"missingCount": 0,
"extrasCount": 0,
"untranslatedCount": 0
......
......@@ -2269,6 +2269,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",
......@@ -3509,7 +3510,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",
......
......@@ -2269,6 +2269,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",
......@@ -3509,7 +3510,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é",
......
......@@ -2269,6 +2269,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": "システム初期化",
......@@ -3509,7 +3510,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": "ユニットあたりのクォータ",
......
......@@ -2269,6 +2269,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": "Инициализация системы",
......@@ -3509,7 +3510,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": "Квота на единицу",
......
......@@ -2269,6 +2269,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",
......@@ -3509,7 +3510,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ị",
......
......@@ -2269,6 +2269,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": "初始化系统",
......@@ -3509,7 +3510,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": "每单位配额",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment