redeem link + completing task + play sound

This commit is contained in:
dohsimpson
2025-01-27 18:24:53 -05:00
parent c66e28162c
commit b62cf77ba8
9 changed files with 152 additions and 69 deletions

View File

@@ -61,7 +61,7 @@ export default function AddEditHabitModal({ onClose, onSave, habit }: AddEditHab
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
Name
Name *
</Label>
<div className='flex col-span-3 gap-2'>
<Input
@@ -112,13 +112,14 @@ export default function AddEditHabitModal({ onClose, onSave, habit }: AddEditHab
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="recurrence" className="text-right">
When
When *
</Label>
<div className="col-span-3 space-y-2">
<Input
id="recurrence"
value={ruleText}
onChange={(e) => setRuleText(e.target.value)}
required
// placeholder="e.g. 'every weekday' or 'every 2 weeks on Monday, Wednesday'"
/>
</div>

View File

@@ -13,32 +13,44 @@ import { WishlistItemType } from '@/lib/types'
interface AddEditWishlistItemModalProps {
isOpen: boolean
onClose: () => void
onSave: (item: Omit<WishlistItemType, 'id'>) => void
item?: WishlistItemType | null
setIsOpen: (isOpen: boolean) => void
editingItem: WishlistItemType | null
setEditingItem: (item: WishlistItemType | null) => void
addWishlistItem: (item: Omit<WishlistItemType, 'id'>) => void
editWishlistItem: (item: WishlistItemType) => void
}
export default function AddEditWishlistItemModal({ isOpen, onClose, onSave, item }: AddEditWishlistItemModalProps) {
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)
export default function AddEditWishlistItemModal({
isOpen,
setIsOpen,
editingItem,
setEditingItem,
addWishlistItem,
editWishlistItem
}: AddEditWishlistItemModalProps) {
const [name, setName] = useState(editingItem?.name || '')
const [description, setDescription] = useState(editingItem?.description || '')
const [coinCost, setCoinCost] = useState(editingItem?.coinCost || 1)
const [targetCompletions, setTargetCompletions] = useState<number | undefined>(editingItem?.targetCompletions)
const [link, setLink] = useState(editingItem?.link || '')
const [errors, setErrors] = useState<{ [key: string]: string }>({})
useEffect(() => {
if (item) {
setName(item.name)
setDescription(item.description)
setCoinCost(item.coinCost)
setTargetCompletions(item.targetCompletions)
if (editingItem) {
setName(editingItem.name)
setDescription(editingItem.description)
setCoinCost(editingItem.coinCost)
setTargetCompletions(editingItem.targetCompletions)
setLink(editingItem.link || '')
} else {
setName('')
setDescription('')
setCoinCost(1)
setTargetCompletions(undefined)
setLink('')
}
setErrors({})
}, [item])
}, [editingItem])
const validate = () => {
const newErrors: { [key: string]: string } = {}
@@ -51,32 +63,60 @@ export default function AddEditWishlistItemModal({ isOpen, onClose, onSave, item
if (targetCompletions !== undefined && targetCompletions < 1) {
newErrors.targetCompletions = 'Target completions must be at least 1'
}
if (link && !isValidUrl(link)) {
newErrors.link = 'Please enter a valid URL'
}
setErrors(newErrors)
return Object.keys(newErrors).length === 0
}
const handleSubmit = (e: React.FormEvent) => {
const isValidUrl = (url: string) => {
try {
new URL(url)
return true
} catch {
return false
}
}
const handleClose = () => {
setIsOpen(false)
setEditingItem(null)
}
const handleSave = (e: React.FormEvent) => {
e.preventDefault()
if (!validate()) return
onSave({
const itemData = {
name,
description,
coinCost,
targetCompletions: targetCompletions || undefined
})
targetCompletions: targetCompletions || undefined,
link: link.trim() || undefined
}
if (editingItem) {
editWishlistItem({ ...itemData, id: editingItem.id })
} else {
addWishlistItem(itemData)
}
setIsOpen(false)
setEditingItem(null)
}
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<Dialog open={isOpen} onOpenChange={handleClose}>
<DialogContent>
<DialogHeader>
<DialogTitle>{item ? 'Edit Reward' : 'Add New Reward'}</DialogTitle>
<DialogTitle>{editingItem ? 'Edit Reward' : 'Add New Reward'}</DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit}>
<form onSubmit={handleSave}>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
Name
Name *
</Label>
<div className="col-span-3 flex gap-2">
<Input
@@ -208,9 +248,29 @@ export default function AddEditWishlistItemModal({ isOpen, onClose, onSave, item
)}
</div>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="link" className="text-right">
Link
</Label>
<div className="col-span-3">
<Input
id="link"
type="url"
placeholder="https://..."
value={link}
onChange={(e) => setLink(e.target.value)}
className="col-span-3"
/>
{errors.link && (
<div className="text-sm text-red-500">
{errors.link}
</div>
)}
</div>
</div>
</div>
<DialogFooter>
<Button type="submit">{item ? 'Save Changes' : 'Add Reward'}</Button>
<Button type="submit">{editingItem ? 'Save Changes' : 'Add Reward'}</Button>
</DialogFooter>
</form>
</DialogContent>

View File

@@ -148,14 +148,6 @@ export default function PomodoroTimer() {
}
}, [state])
const playSound = useCallback(() => {
const audio = new Audio('/sounds/timer-end.wav')
audio.play().catch(error => {
console.error('Error playing sound:', error)
})
}, [])
const handleTimerEnd = async () => {
setState("stopped")
const currentTimerType = currentTimer.current.type
@@ -165,9 +157,6 @@ export default function PomodoroTimer() {
currentTimer.current.labels[Math.floor(Math.random() * currentTimer.current.labels.length)]
)
// Play sound
playSound()
// update habits only after focus sessions
if (selectedHabit && currentTimerType === 'focus') {
await completeHabit(selectedHabit)

View File

@@ -137,20 +137,11 @@ export default function WishlistManager() {
</div>
<AddEditWishlistItemModal
isOpen={isModalOpen}
onClose={() => {
setIsModalOpen(false)
setEditingItem(null)
}}
onSave={(item) => {
if (editingItem) {
editWishlistItem({ ...item, id: editingItem.id })
} else {
addWishlistItem(item)
}
setIsModalOpen(false)
setEditingItem(null)
}}
item={editingItem}
setIsOpen={setIsModalOpen}
editingItem={editingItem}
setEditingItem={setEditingItem}
addWishlistItem={addWishlistItem}
editWishlistItem={editWishlistItem}
/>
<ConfirmDialog
isOpen={deleteConfirmation.isOpen}