mirror of
https://github.com/ManInDark/HabitTrove.git
synced 2026-01-21 06:34:30 +01:00
Merge Tag v0.2.15.0
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## Version 0.2.13
|
||||
## Version 0.2.15
|
||||
|
||||
### Improved
|
||||
|
||||
* max coins set to 9999, to prevent js large number precision issue (#137)
|
||||
|
||||
## Version 0.2.14
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { settingsAtom, usersAtom } from '@/lib/atoms'
|
||||
import { useHelpers } from '@/lib/client-helpers'
|
||||
import { INITIAL_DUE, INITIAL_RECURRENCE_RULE, QUICK_DATES } from '@/lib/constants'
|
||||
import { INITIAL_DUE, INITIAL_RECURRENCE_RULE, MAX_COIN_LIMIT, QUICK_DATES } from '@/lib/constants'
|
||||
import { Habit } from '@/lib/types'
|
||||
import { convertHumanReadableFrequencyToMachineReadable, convertMachineReadableFrequencyToHumanReadable, d2t, serializeRRule } from '@/lib/utils'
|
||||
import data from '@emoji-mart/data'
|
||||
@@ -268,14 +268,18 @@ export default function AddEditHabitModal({ onClose, onSave, habit, isTask }: Ad
|
||||
id="coinReward"
|
||||
type="number"
|
||||
value={coinReward}
|
||||
onChange={(e) => setCoinReward(parseInt(e.target.value === "" ? "0" : e.target.value))}
|
||||
onChange={(e) => {
|
||||
const value = parseInt(e.target.value === "" ? "0" : e.target.value)
|
||||
setCoinReward(Math.min(value, MAX_COIN_LIMIT))
|
||||
}}
|
||||
min={0}
|
||||
max={MAX_COIN_LIMIT}
|
||||
required
|
||||
className="w-20 text-center border-0 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setCoinReward(prev => prev + 1)}
|
||||
onClick={() => setCoinReward(prev => Math.min(prev + 1, MAX_COIN_LIMIT))}
|
||||
className="px-3 py-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors"
|
||||
>
|
||||
+
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { usersAtom } from '@/lib/atoms'
|
||||
import { useHelpers } from '@/lib/client-helpers'
|
||||
import { MAX_COIN_LIMIT } from '@/lib/constants'
|
||||
import { WishlistItemType } from '@/lib/types'
|
||||
import data from '@emoji-mart/data'
|
||||
import Picker from '@emoji-mart/react'
|
||||
@@ -68,6 +69,8 @@ export default function AddEditWishlistItemModal({
|
||||
}
|
||||
if (coinCost < 1) {
|
||||
newErrors.coinCost = t('errorCoinCostMin')
|
||||
} else if (coinCost > MAX_COIN_LIMIT) {
|
||||
newErrors.coinCost = t('errorCoinCostMax', { max: MAX_COIN_LIMIT })
|
||||
}
|
||||
if (targetCompletions !== undefined && targetCompletions < 1) {
|
||||
newErrors.targetCompletions = t('errorTargetCompletionsMin')
|
||||
@@ -192,14 +195,18 @@ export default function AddEditWishlistItemModal({
|
||||
id="coinReward"
|
||||
type="number"
|
||||
value={coinCost}
|
||||
onChange={(e) => setCoinCost(parseInt(e.target.value === "" ? "0" : e.target.value))}
|
||||
onChange={(e) => {
|
||||
const value = parseInt(e.target.value === "" ? "0" : e.target.value)
|
||||
setCoinCost(Math.min(value, MAX_COIN_LIMIT))
|
||||
}}
|
||||
min={0}
|
||||
max={MAX_COIN_LIMIT}
|
||||
required
|
||||
className="w-20 text-center border-0 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setCoinCost(prev => prev + 1)}
|
||||
onClick={() => setCoinCost(prev => Math.min(prev + 1, MAX_COIN_LIMIT))}
|
||||
className="px-3 py-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors"
|
||||
>
|
||||
+
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Input } from '@/components/ui/input'
|
||||
import { useCoins } from '@/hooks/useCoins'
|
||||
import { settingsAtom, usersAtom } from '@/lib/atoms'
|
||||
import { useHelpers } from '@/lib/client-helpers'
|
||||
import { MAX_COIN_LIMIT } from '@/lib/constants'
|
||||
import { TransactionType } from '@/lib/types'
|
||||
import { d2s, t2d } from '@/lib/utils'
|
||||
import { useAtom } from 'jotai'
|
||||
@@ -139,7 +140,11 @@ export default function CoinsManager() {
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-10 w-10 text-lg"
|
||||
onClick={() => setAmount(prev => (Number(prev) - 1).toString())}
|
||||
onClick={() => setAmount(prev => {
|
||||
const current = Number(prev);
|
||||
const next = current - 1;
|
||||
return (Math.abs(next) > MAX_COIN_LIMIT ? (next < 0 ? -MAX_COIN_LIMIT : MAX_COIN_LIMIT) : next).toString();
|
||||
})}
|
||||
>
|
||||
-
|
||||
</Button>
|
||||
@@ -147,7 +152,22 @@ export default function CoinsManager() {
|
||||
<Input
|
||||
type="number"
|
||||
value={amount}
|
||||
onChange={(e) => setAmount(e.target.value)}
|
||||
onChange={(e) => {
|
||||
const rawValue = e.target.value;
|
||||
if (rawValue === '' || rawValue === '-') {
|
||||
setAmount(rawValue);
|
||||
return;
|
||||
}
|
||||
let numericValue = Number(rawValue); // Changed const to let
|
||||
if (isNaN(numericValue)) return; // Or handle error
|
||||
|
||||
if (Math.abs(numericValue) > MAX_COIN_LIMIT) {
|
||||
numericValue = numericValue < 0 ? -MAX_COIN_LIMIT : MAX_COIN_LIMIT;
|
||||
}
|
||||
setAmount(numericValue.toString());
|
||||
}}
|
||||
min={-MAX_COIN_LIMIT}
|
||||
max={MAX_COIN_LIMIT}
|
||||
className="text-center text-xl font-medium h-12"
|
||||
/>
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground">
|
||||
@@ -158,7 +178,11 @@ export default function CoinsManager() {
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-10 w-10 text-lg"
|
||||
onClick={() => setAmount(prev => (Number(prev) + 1).toString())}
|
||||
onClick={() => setAmount(prev => {
|
||||
const current = Number(prev);
|
||||
const next = current + 1;
|
||||
return (Math.abs(next) > MAX_COIN_LIMIT ? (next < 0 ? -MAX_COIN_LIMIT : MAX_COIN_LIMIT) : next).toString();
|
||||
})}
|
||||
>
|
||||
+
|
||||
</Button>
|
||||
|
||||
@@ -16,6 +16,7 @@ import { addCoins, removeCoins, saveCoinsData } from '@/app/actions/data'
|
||||
import { CoinsData, User } from '@/lib/types'
|
||||
import { toast } from '@/hooks/use-toast'
|
||||
import { useHelpers } from '@/lib/client-helpers'
|
||||
import { MAX_COIN_LIMIT } from '@/lib/constants'
|
||||
|
||||
function handlePermissionCheck(
|
||||
user: User | undefined,
|
||||
@@ -77,6 +78,13 @@ export function useCoins(options?: { selectedUser?: string }) {
|
||||
})
|
||||
return null
|
||||
}
|
||||
if (amount > MAX_COIN_LIMIT) {
|
||||
toast({
|
||||
title: t("invalidAmountTitle"),
|
||||
description: t("maxAmountExceededDescription", { max: MAX_COIN_LIMIT })
|
||||
})
|
||||
return null
|
||||
}
|
||||
|
||||
const data = await addCoins({
|
||||
amount,
|
||||
@@ -100,6 +108,13 @@ export function useCoins(options?: { selectedUser?: string }) {
|
||||
})
|
||||
return null
|
||||
}
|
||||
if (numAmount > MAX_COIN_LIMIT) {
|
||||
toast({
|
||||
title: t("invalidAmountTitle"),
|
||||
description: t("maxAmountExceededDescription", { max: MAX_COIN_LIMIT })
|
||||
})
|
||||
return null
|
||||
}
|
||||
|
||||
const data = await removeCoins({
|
||||
amount: numAmount,
|
||||
|
||||
@@ -30,3 +30,5 @@ export const QUICK_DATES = [
|
||||
{ label: 'Saturday', value: 'this saturday' },
|
||||
{ label: 'Sunday', value: 'this sunday' },
|
||||
] as const
|
||||
|
||||
export const MAX_COIN_LIMIT = 9999
|
||||
@@ -406,16 +406,17 @@
|
||||
"notEnoughCoinsTitle": "Nicht genug Münzen",
|
||||
"notEnoughCoinsDescription": "Sie benötigen {coinsNeeded} Münzen mehr, um diese Belohnung einzulösen."
|
||||
},
|
||||
"Warning": {
|
||||
"areYouSure": "Sind Sie sicher?",
|
||||
"cancel": "Abbrechen"
|
||||
},
|
||||
"useCoins": {
|
||||
"invalidAmountTitle": "Ungültiger Betrag",
|
||||
"invalidAmountDescription": "Bitte geben Sie eine gültige positive Zahl ein",
|
||||
"successTitle": "Erfolg",
|
||||
"addedCoinsDescription": "{amount} Münzen hinzugefügt",
|
||||
"removedCoinsDescription": "{amount} Münzen entfernt",
|
||||
"transactionNotFoundDescription": "Transaktion nicht gefunden"
|
||||
},
|
||||
"Warning": {
|
||||
"areYouSure": "Sind Sie sicher?",
|
||||
"cancel": "Abbrechen"
|
||||
"transactionNotFoundDescription": "Transaktion nicht gefunden",
|
||||
"maxAmountExceededDescription": "Der Betrag darf {max} nicht überschreiten.",
|
||||
"transactionNotFoundDescription": "Transaktion nicht gefunden",
|
||||
"maxAmountExceededDescription": "Der Betrag darf {max} nicht überschreiten."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,9 +410,10 @@
|
||||
"invalidAmountTitle": "Invalid amount",
|
||||
"invalidAmountDescription": "Please enter a valid positive number",
|
||||
"successTitle": "Success",
|
||||
"addedCoinsDescription": "Added {amount} coins",
|
||||
"removedCoinsDescription": "Removed {amount} coins",
|
||||
"transactionNotFoundDescription": "Transaction not found"
|
||||
"transactionNotFoundDescription": "Transaction not found",
|
||||
"maxAmountExceededDescription": "The amount cannot exceed {max}.",
|
||||
"transactionNotFoundDescription": "Transaction not found",
|
||||
"maxAmountExceededDescription": "The amount cannot exceed {max}."
|
||||
},
|
||||
"Warning": {
|
||||
"areYouSure": "Are you sure?",
|
||||
|
||||
@@ -406,16 +406,17 @@
|
||||
"notEnoughCoinsTitle": "No hay suficientes monedas",
|
||||
"notEnoughCoinsDescription": "Necesitas {coinsNeeded} monedas más para canjear esta recompensa."
|
||||
},
|
||||
"Warning": {
|
||||
"areYouSure": "¿Estás seguro?",
|
||||
"cancel": "Cancelar"
|
||||
},
|
||||
"useCoins": {
|
||||
"invalidAmountTitle": "Cantidad inválida",
|
||||
"invalidAmountDescription": "Por favor ingresa un número positivo válido",
|
||||
"successTitle": "Éxito",
|
||||
"addedCoinsDescription": "Añadidas {amount} monedas",
|
||||
"removedCoinsDescription": "Quitadas {amount} monedas",
|
||||
"transactionNotFoundDescription": "Transacción no encontrada"
|
||||
},
|
||||
"Warning": {
|
||||
"areYouSure": "¿Estás seguro?",
|
||||
"cancel": "Cancelar"
|
||||
"transactionNotFoundDescription": "Transacción no encontrada",
|
||||
"maxAmountExceededDescription": "La cantidad no puede exceder {max}.",
|
||||
"transactionNotFoundDescription": "Transacción no encontrada",
|
||||
"maxAmountExceededDescription": "La cantidad no puede exceder {max}."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,16 +406,17 @@
|
||||
"notEnoughCoinsTitle": "Pas assez de pièces",
|
||||
"notEnoughCoinsDescription": "Il vous manque {coinsNeeded} pièces pour échanger cette récompense."
|
||||
},
|
||||
"Warning": {
|
||||
"areYouSure": "Êtes-vous sûr ?",
|
||||
"cancel": "Annuler"
|
||||
},
|
||||
"useCoins": {
|
||||
"invalidAmountTitle": "Montant invalide",
|
||||
"invalidAmountDescription": "Veuillez entrer un nombre positif valide",
|
||||
"successTitle": "Succès",
|
||||
"addedCoinsDescription": "Ajouté {amount} pièces",
|
||||
"removedCoinsDescription": "Retiré {amount} pièces",
|
||||
"transactionNotFoundDescription": "Transaction non trouvée"
|
||||
},
|
||||
"Warning": {
|
||||
"areYouSure": "Êtes-vous sûr ?",
|
||||
"cancel": "Annuler"
|
||||
"transactionNotFoundDescription": "Transaction non trouvée",
|
||||
"maxAmountExceededDescription": "Le montant ne peut pas dépasser {max}.",
|
||||
"transactionNotFoundDescription": "Transaction non trouvée",
|
||||
"maxAmountExceededDescription": "Le montant ne peut pas dépasser {max}."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,16 +406,17 @@
|
||||
"notEnoughCoinsTitle": "コインが不足しています",
|
||||
"notEnoughCoinsDescription": "この報酬を使用するにはあと{coinsNeeded}コイン必要です。"
|
||||
},
|
||||
"Warning": {
|
||||
"areYouSure": "本当によろしいですか?",
|
||||
"cancel": "キャンセル"
|
||||
},
|
||||
"useCoins": {
|
||||
"invalidAmountTitle": "無効な値です",
|
||||
"invalidAmountDescription": "有効な正の数を入力してください",
|
||||
"successTitle": "成功しました",
|
||||
"addedCoinsDescription": "{amount}コインを追加しました",
|
||||
"removedCoinsDescription": "{amount}コインを削除しました",
|
||||
"transactionNotFoundDescription": "取引が見つかりません"
|
||||
},
|
||||
"Warning": {
|
||||
"areYouSure": "本当によろしいですか?",
|
||||
"cancel": "キャンセル"
|
||||
"transactionNotFoundDescription": "取引が見つかりません",
|
||||
"maxAmountExceededDescription": "金額は{max}を超えることはできません。",
|
||||
"transactionNotFoundDescription": "取引が見つかりません",
|
||||
"maxAmountExceededDescription": "金額は{max}を超えることはできません。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,16 +406,17 @@
|
||||
"notEnoughCoinsTitle": "Недостаточно монет",
|
||||
"notEnoughCoinsDescription": "Вам нужно еще {coinsNeeded} монет, чтобы получить эту награду."
|
||||
},
|
||||
"Warning": {
|
||||
"areYouSure": "Вы уверены?",
|
||||
"cancel": "Отмена"
|
||||
},
|
||||
"useCoins": {
|
||||
"invalidAmountTitle": "Неверная сумма",
|
||||
"invalidAmountDescription": "Пожалуйста, введите положительное число",
|
||||
"successTitle": "Успех",
|
||||
"addedCoinsDescription": "Добавлено {amount} монет",
|
||||
"removedCoinsDescription": "Удалено {amount} монет",
|
||||
"transactionNotFoundDescription": "Транзакция не найдена"
|
||||
},
|
||||
"Warning": {
|
||||
"areYouSure": "Вы уверены?",
|
||||
"cancel": "Отмена"
|
||||
"transactionNotFoundDescription": "Транзакция не найдена",
|
||||
"maxAmountExceededDescription": "Сумма не может превышать {max}.",
|
||||
"transactionNotFoundDescription": "Транзакция не найдена",
|
||||
"maxAmountExceededDescription": "Сумма не может превышать {max}."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,16 +406,17 @@
|
||||
"notEnoughCoinsTitle": "金币不足",
|
||||
"notEnoughCoinsDescription": "您还需要{coinsNeeded}金币才能兑换此奖励。"
|
||||
},
|
||||
"Warning": {
|
||||
"areYouSure": "您确定吗?",
|
||||
"cancel": "取消"
|
||||
},
|
||||
"useCoins": {
|
||||
"invalidAmountTitle": "无效金额",
|
||||
"invalidAmountDescription": "请输入有效的正数",
|
||||
"successTitle": "成功",
|
||||
"addedCoinsDescription": "添加了{amount}金币",
|
||||
"removedCoinsDescription": "移除了{amount}金币",
|
||||
"transactionNotFoundDescription": "未找到交易记录"
|
||||
},
|
||||
"Warning": {
|
||||
"areYouSure": "您确定吗?",
|
||||
"cancel": "取消"
|
||||
"transactionNotFoundDescription": "未找到交易记录",
|
||||
"maxAmountExceededDescription": "金额不能超过 {max}。",
|
||||
"transactionNotFoundDescription": "未找到交易记录",
|
||||
"maxAmountExceededDescription": "金额不能超过 {max}。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "habittrove",
|
||||
"version": "0.2.14",
|
||||
"version": "0.2.15",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
|
||||
Reference in New Issue
Block a user