Merge Tag v0.2.15.0

This commit is contained in:
2025-05-25 17:46:20 +02:00
14 changed files with 121 additions and 56 deletions

View File

@@ -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"
>
+

View File

@@ -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"
>
+

View File

@@ -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>