mirror of
https://github.com/ManInDark/HabitTrove.git
synced 2026-01-20 22:24:28 +01:00
support archiving habit and wishlist + wishlist redeem count (#49)
This commit is contained in:
@@ -5,7 +5,8 @@ import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import { SmilePlus } from 'lucide-react'
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { SmilePlus, Info } from 'lucide-react'
|
||||
import data from '@emoji-mart/data'
|
||||
import Picker from '@emoji-mart/react'
|
||||
import { WishlistItemType } from '@/lib/types'
|
||||
@@ -18,25 +19,51 @@ interface AddEditWishlistItemModalProps {
|
||||
}
|
||||
|
||||
export default function AddEditWishlistItemModal({ isOpen, onClose, onSave, item }: AddEditWishlistItemModalProps) {
|
||||
const [name, setName] = useState('')
|
||||
const [description, setDescription] = useState('')
|
||||
const [coinCost, setCoinCost] = useState(1)
|
||||
const [name, setName] = useState(item?.name || '')
|
||||
const [description, setDescription] = useState(item?.description || '')
|
||||
const [coinCost, setCoinCost] = useState(item?.coinCost || 1)
|
||||
const [targetCompletions, setTargetCompletions] = useState<number | undefined>(item?.targetCompletions)
|
||||
const [errors, setErrors] = useState<{ [key: string]: string }>({})
|
||||
|
||||
useEffect(() => {
|
||||
if (item) {
|
||||
setName(item.name)
|
||||
setDescription(item.description)
|
||||
setCoinCost(item.coinCost)
|
||||
setTargetCompletions(item.targetCompletions)
|
||||
} else {
|
||||
setName('')
|
||||
setDescription('')
|
||||
setCoinCost(1)
|
||||
setTargetCompletions(undefined)
|
||||
}
|
||||
setErrors({})
|
||||
}, [item])
|
||||
|
||||
const validate = () => {
|
||||
const newErrors: { [key: string]: string } = {}
|
||||
if (!name.trim()) {
|
||||
newErrors.name = 'Name is required'
|
||||
}
|
||||
if (coinCost < 1) {
|
||||
newErrors.coinCost = 'Coin cost must be at least 1'
|
||||
}
|
||||
if (targetCompletions !== undefined && targetCompletions < 1) {
|
||||
newErrors.targetCompletions = 'Target completions must be at least 1'
|
||||
}
|
||||
setErrors(newErrors)
|
||||
return Object.keys(newErrors).length === 0
|
||||
}
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
onSave({ name, description, coinCost })
|
||||
if (!validate()) return
|
||||
onSave({
|
||||
name,
|
||||
description,
|
||||
coinCost,
|
||||
targetCompletions: targetCompletions || undefined
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -96,18 +123,90 @@ export default function AddEditWishlistItemModal({ isOpen, onClose, onSave, item
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="coinCost" className="text-right">
|
||||
Coin Cost
|
||||
</Label>
|
||||
<Input
|
||||
id="coinCost"
|
||||
type="number"
|
||||
value={coinCost}
|
||||
onChange={(e) => setCoinCost(parseInt(e.target.value === "" ? "0" : e.target.value))}
|
||||
className="col-span-3"
|
||||
min={1}
|
||||
required
|
||||
/>
|
||||
<div className="flex items-center gap-2 justify-end">
|
||||
<Label htmlFor="coinReward">
|
||||
Cost
|
||||
</Label>
|
||||
</div>
|
||||
<div className="col-span-3">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center border rounded-lg overflow-hidden">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setCoinCost(prev => Math.max(0, prev - 1))}
|
||||
className="px-3 py-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors"
|
||||
>
|
||||
-
|
||||
</button>
|
||||
<Input
|
||||
id="coinReward"
|
||||
type="number"
|
||||
value={coinCost}
|
||||
onChange={(e) => setCoinCost(parseInt(e.target.value === "" ? "0" : e.target.value))}
|
||||
min={0}
|
||||
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)}
|
||||
className="px-3 py-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
coins
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<div className="flex items-center gap-2 justify-end">
|
||||
<Label htmlFor="targetCompletions">
|
||||
Redeemable
|
||||
</Label>
|
||||
</div>
|
||||
<div className="col-span-3">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center border rounded-lg overflow-hidden">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setTargetCompletions(prev => prev !== undefined && prev > 1 ? prev - 1 : undefined)}
|
||||
className="px-3 py-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors"
|
||||
>
|
||||
-
|
||||
</button>
|
||||
<Input
|
||||
id="targetCompletions"
|
||||
type="number"
|
||||
value={targetCompletions || ''}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value
|
||||
setTargetCompletions(value && value !== "0" ? parseInt(value) : undefined)
|
||||
}}
|
||||
min={0}
|
||||
placeholder="∞"
|
||||
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={() => setTargetCompletions(prev => Math.min(10, (prev || 0) + 1))}
|
||||
className="px-3 py-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 transition-colors"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
times
|
||||
</span>
|
||||
</div>
|
||||
{errors.targetCompletions && (
|
||||
<div className="text-sm text-red-500">
|
||||
{errors.targetCompletions}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
|
||||
Reference in New Issue
Block a user