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 { ...@@ -34,6 +34,7 @@ import {
} from '@/components/ui/form' } from '@/components/ui/form'
import { Input } from '@/components/ui/input' import { Input } from '@/components/ui/input'
import { Switch } from '@/components/ui/switch' import { Switch } from '@/components/ui/switch'
import { formatQuota } from '@/lib/format'
import { FormDirtyIndicator } from '../components/form-dirty-indicator' import { FormDirtyIndicator } from '../components/form-dirty-indicator'
import { FormNavigationGuard } from '../components/form-navigation-guard' import { FormNavigationGuard } from '../components/form-navigation-guard'
...@@ -64,6 +65,11 @@ const quotaSchema = z.object({ ...@@ -64,6 +65,11 @@ const quotaSchema = z.object({
}) })
type QuotaFormValues = z.infer<typeof quotaSchema> type QuotaFormValues = z.infer<typeof quotaSchema>
type QuotaInputValue = number | ''
function formatQuotaInputValue(value: QuotaInputValue): string {
return formatQuota(value === '' ? 0 : value)
}
type QuotaSettingsSectionProps = { type QuotaSettingsSectionProps = {
defaultValues: QuotaFormValues defaultValues: QuotaFormValues
...@@ -77,11 +83,10 @@ export function QuotaSettingsSection({ ...@@ -77,11 +83,10 @@ export function QuotaSettingsSection({
const { t } = useTranslation() const { t } = useTranslation()
const updateOption = useUpdateOption() const updateOption = useUpdateOption()
const handleNumberChange = const handleNumberChange =
(onChange: (value: number | string) => void) => (onChange: (value: QuotaInputValue) => void) =>
(event: ChangeEvent<HTMLInputElement>) => { (event: ChangeEvent<HTMLInputElement>) => {
onChange( const value = event.currentTarget.valueAsNumber
event.target.value === '' ? '' : event.currentTarget.valueAsNumber onChange(Number.isNaN(value) ? '' : value)
)
} }
const { form, handleSubmit, isDirty, isSubmitting } = const { form, handleSubmit, isDirty, isSubmitting } =
...@@ -141,7 +146,12 @@ export function QuotaSettingsSection({ ...@@ -141,7 +146,12 @@ export function QuotaSettingsSection({
/> />
</FormControl> </FormControl>
<FormDescription> <FormDescription>
{t('Initial quota given to new users')} {t(
'Initial quota given to new users ({{formattedQuota}})',
{
formattedQuota: formatQuotaInputValue(field.value),
}
)}
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
...@@ -189,7 +199,12 @@ export function QuotaSettingsSection({ ...@@ -189,7 +199,12 @@ export function QuotaSettingsSection({
/> />
</FormControl> </FormControl>
<FormDescription> <FormDescription>
{t('Quota given to users who invite others')} {t(
'Quota given to users who invite others ({{formattedQuota}})',
{
formattedQuota: formatQuotaInputValue(field.value),
}
)}
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
...@@ -213,7 +228,9 @@ export function QuotaSettingsSection({ ...@@ -213,7 +228,9 @@ export function QuotaSettingsSection({
/> />
</FormControl> </FormControl>
<FormDescription> <FormDescription>
{t('Quota given to invited users')} {t('Quota given to invited users ({{formattedQuota}})', {
formattedQuota: formatQuotaInputValue(field.value),
})}
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
......
...@@ -24,9 +24,15 @@ import { Dialog } from '@/components/dialog' ...@@ -24,9 +24,15 @@ import { Dialog } from '@/components/dialog'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input' import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label' import { Label } from '@/components/ui/label'
import { formatQuota } from '@/lib/format' import {
formatQuota,
import { QUOTA_PER_DOLLAR } from '../../constants' parseQuotaFromDollars,
quotaUnitsToDollars,
} from '@/lib/format'
import {
DEFAULT_CURRENCY_CONFIG,
useSystemConfigStore,
} from '@/stores/system-config-store'
interface TransferDialogProps { interface TransferDialogProps {
open: boolean open: boolean
...@@ -44,17 +50,34 @@ export function TransferDialog({ ...@@ -44,17 +50,34 @@ export function TransferDialog({
transferring, transferring,
}: TransferDialogProps) { }: TransferDialogProps) {
const { t } = useTranslation() 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(() => { useEffect(() => {
if (open) { if (open) {
// eslint-disable-next-line react-hooks/set-state-in-effect // eslint-disable-next-line react-hooks/set-state-in-effect
setAmount(QUOTA_PER_DOLLAR) setAmount(minimumAmount)
} }
}, [open]) }, [minimumAmount, open])
const handleConfirm = async () => { const handleConfirm = async () => {
const success = await onConfirm(amount) if (!canTransfer) return
const success = await onConfirm(transferQuota)
if (success) { if (success) {
onOpenChange(false) onOpenChange(false)
} }
...@@ -80,7 +103,10 @@ export function TransferDialog({ ...@@ -80,7 +103,10 @@ export function TransferDialog({
> >
{t('Cancel')} {t('Cancel')}
</Button> </Button>
<Button onClick={handleConfirm} disabled={transferring}> <Button
onClick={handleConfirm}
disabled={transferring || !canTransfer}
>
{transferring && <Loader2 className='mr-2 h-4 w-4 animate-spin' />} {transferring && <Loader2 className='mr-2 h-4 w-4 animate-spin' />}
{t('Transfer')} {t('Transfer')}
</Button> </Button>
...@@ -109,13 +135,13 @@ export function TransferDialog({ ...@@ -109,13 +135,13 @@ export function TransferDialog({
type='number' type='number'
value={amount} value={amount}
onChange={(e) => setAmount(Number(e.target.value))} onChange={(e) => setAmount(Number(e.target.value))}
min={QUOTA_PER_DOLLAR} min={minimumAmount}
max={availableQuota} max={maximumAmount}
step={QUOTA_PER_DOLLAR} step={minimumAmount}
className='font-mono text-lg' className='font-mono text-lg'
/> />
<p className='text-muted-foreground text-xs'> <p className='text-muted-foreground text-xs'>
{t('Minimum:')} {formatQuota(QUOTA_PER_DOLLAR)} {t('Minimum:')} {formatQuota(minimumQuota)}
</p> </p>
</div> </div>
</div> </div>
......
...@@ -56,11 +56,6 @@ export const PAYMENT_ICON_COLORS = { ...@@ -56,11 +56,6 @@ export const PAYMENT_ICON_COLORS = {
} as const } as const
/** /**
* Quota conversion rate: 500,000 units = $1
*/
export const QUOTA_PER_DOLLAR = 500000
/**
* Default discount rate (no discount) * Default discount rate (no discount)
*/ */
export const DEFAULT_DISCOUNT_RATE = 1.0 export const DEFAULT_DISCOUNT_RATE = 1.0
......
...@@ -31,14 +31,14 @@ ...@@ -31,14 +31,14 @@
"extrasCount": 0, "extrasCount": 0,
"untranslatedCount": 0 "untranslatedCount": 0
}, },
"zh": { "zh-TW": {
"file": "zh.json", "file": "zh-TW.json",
"missingCount": 0, "missingCount": 0,
"extrasCount": 0, "extrasCount": 0,
"untranslatedCount": 0 "untranslatedCount": 0
}, },
"zh-TW": { "zh": {
"file": "zh-TW.json", "file": "zh.json",
"missingCount": 0, "missingCount": 0,
"extrasCount": 0, "extrasCount": 0,
"untranslatedCount": 0 "untranslatedCount": 0
......
...@@ -2269,6 +2269,7 @@ ...@@ -2269,6 +2269,7 @@
"Increased user quota by {{quota}}": "Increased user quota by {{quota}}", "Increased user quota by {{quota}}": "Increased user quota by {{quota}}",
"Index": "Index", "Index": "Index",
"Initial quota given to new users": "Initial quota given to new users", "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.", "Initialization failed, please try again.": "Initialization failed, please try again.",
"Initialize": "Initialize", "Initialize": "Initialize",
"Initialize system": "Initialize system", "Initialize system": "Initialize system",
...@@ -3509,7 +3510,9 @@ ...@@ -3509,7 +3510,9 @@
"Quota consumed before charging users": "Quota consumed before charging users", "Quota consumed before charging users": "Quota consumed before charging users",
"Quota Distribution": "Quota Distribution", "Quota Distribution": "Quota Distribution",
"Quota given to invited users": "Quota given to invited users", "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": "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 a positive number": "Quota must be a positive number",
"Quota must be zero or greater": "Quota must be zero or greater", "Quota must be zero or greater": "Quota must be zero or greater",
"Quota Per Unit": "Quota Per Unit", "Quota Per Unit": "Quota Per Unit",
......
...@@ -2269,6 +2269,7 @@ ...@@ -2269,6 +2269,7 @@
"Increased user quota by {{quota}}": "Quota de l'utilisateur augmenté de {{quota}}", "Increased user quota by {{quota}}": "Quota de l'utilisateur augmenté de {{quota}}",
"Index": "Index", "Index": "Index",
"Initial quota given to new users": "Quota initial donné aux nouveaux utilisateurs", "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.", "Initialization failed, please try again.": "L'initialisation a échoué, veuillez réessayer.",
"Initialize": "Initialiser", "Initialize": "Initialiser",
"Initialize system": "Initialiser le système", "Initialize system": "Initialiser le système",
...@@ -3509,7 +3510,9 @@ ...@@ -3509,7 +3510,9 @@
"Quota consumed before charging users": "Quota consommé avant de facturer les utilisateurs", "Quota consumed before charging users": "Quota consommé avant de facturer les utilisateurs",
"Quota Distribution": "Distribution des quotas", "Quota Distribution": "Distribution des quotas",
"Quota given to invited users": "Quota attribué aux utilisateurs invités", "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": "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 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 must be zero or greater": "Le quota ne peut pas être négatif",
"Quota Per Unit": "Quota par unité", "Quota Per Unit": "Quota par unité",
......
...@@ -2269,6 +2269,7 @@ ...@@ -2269,6 +2269,7 @@
"Increased user quota by {{quota}}": "ユーザーのクォータを {{quota}} 増やしました", "Increased user quota by {{quota}}": "ユーザーのクォータを {{quota}} 増やしました",
"Index": "インデックス", "Index": "インデックス",
"Initial quota given to new users": "新規ユーザーに付与される初期クォータ", "Initial quota given to new users": "新規ユーザーに付与される初期クォータ",
"Initial quota given to new users ({{formattedQuota}})": "新規ユーザーに付与される初期クォータ({{formattedQuota}})",
"Initialization failed, please try again.": "初期化に失敗しました。もう一度お試しください。", "Initialization failed, please try again.": "初期化に失敗しました。もう一度お試しください。",
"Initialize": "初期化", "Initialize": "初期化",
"Initialize system": "システム初期化", "Initialize system": "システム初期化",
...@@ -3509,7 +3510,9 @@ ...@@ -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}})": "招待されたユーザーに付与されるクォータ({{formattedQuota}})",
"Quota given to users who invite others": "他のユーザーを招待したユーザーに付与されるクォータ", "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 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 @@ ...@@ -2269,6 +2269,7 @@
"Increased user quota by {{quota}}": "Квота пользователя увеличена на {{quota}}", "Increased user quota by {{quota}}": "Квота пользователя увеличена на {{quota}}",
"Index": "Индекс", "Index": "Индекс",
"Initial quota given to new users": "Начальная квота, предоставляемая новым пользователям", "Initial quota given to new users": "Начальная квота, предоставляемая новым пользователям",
"Initial quota given to new users ({{formattedQuota}})": "Начальная квота, предоставляемая новым пользователям ({{formattedQuota}})",
"Initialization failed, please try again.": "Инициализация не удалась, попробуйте ещё раз.", "Initialization failed, please try again.": "Инициализация не удалась, попробуйте ещё раз.",
"Initialize": "Инициализировать", "Initialize": "Инициализировать",
"Initialize system": "Инициализация системы", "Initialize system": "Инициализация системы",
...@@ -3509,7 +3510,9 @@ ...@@ -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}})": "Квота, предоставляемая приглашенным пользователям ({{formattedQuota}})",
"Quota given to users who invite others": "Квота, предоставляемая пользователям, которые приглашают других", "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 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 @@ ...@@ -2269,6 +2269,7 @@
"Increased user quota by {{quota}}": "Đã tăng hạn mức người dùng thêm {{quota}}", "Increased user quota by {{quota}}": "Đã tăng hạn mức người dùng thêm {{quota}}",
"Index": "Chỉ mục", "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": "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.", "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": "Khởi tạo",
"Initialize system": "Khởi tạo hệ thống", "Initialize system": "Khởi tạo hệ thống",
...@@ -3509,7 +3510,9 @@ ...@@ -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 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 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": "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": "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 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 must be zero or greater": "Hạn mức không được âm",
"Quota Per Unit": "Định mức mỗi đơn vị", "Quota Per Unit": "Định mức mỗi đơn vị",
......
...@@ -2269,6 +2269,7 @@ ...@@ -2269,6 +2269,7 @@
"Increased user quota by {{quota}}": "增加用户额度 {{quota}}", "Increased user quota by {{quota}}": "增加用户额度 {{quota}}",
"Index": "索引", "Index": "索引",
"Initial quota given to new users": "授予新用户的初始配额", "Initial quota given to new users": "授予新用户的初始配额",
"Initial quota given to new users ({{formattedQuota}})": "授予新用户的初始配额({{formattedQuota}})",
"Initialization failed, please try again.": "初始化失败,请重试。", "Initialization failed, please try again.": "初始化失败,请重试。",
"Initialize": "初始化", "Initialize": "初始化",
"Initialize system": "初始化系统", "Initialize system": "初始化系统",
...@@ -3509,7 +3510,9 @@ ...@@ -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}})": "授予被邀请用户的配额({{formattedQuota}})",
"Quota given to users who invite others": "授予邀请其他用户的配额", "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 a positive number": "配额必须是正数",
"Quota must be zero or greater": "额度不能为负数", "Quota must be zero or greater": "额度不能为负数",
"Quota Per Unit": "每单位配额", "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