added defaults for now.

reworked tasks and pending rewards
This commit is contained in:
2025-12-15 15:08:27 -05:00
parent c375c34ed2
commit 3b3d14e454
7 changed files with 105 additions and 4 deletions

View File

@@ -512,7 +512,7 @@ def reward_status(id):
#check to see if this reward id and child id is in the pending rewards db if so set its redeeming flag to true
pending_query = Query()
pending = pending_reward_db.get((pending_query.child_id == child.id) & (pending_query.reward_id == reward.id))
status = RewardStatus(reward.id, reward.name, points_needed, reward.cost, pending, reward.image_id)
status = RewardStatus(reward.id, reward.name, points_needed, reward.cost, pending is not None, reward.image_id)
statuses.append(status.to_dict())
statuses.sort(key=lambda s: s['cost'])

View File

@@ -82,6 +82,42 @@ def populate_default_data():
"rewards": rewards
}
def createDefaultTasks():
"""Create default tasks if none exist."""
if len(task_db.all()) == 0:
default_tasks = [
Task(name="Take out trash", points=2, is_good=True, image_id="trash-can"),
Task(name="Make your bed", points=2, is_good=True, image_id="make-the-bed"),
Task(name="Sweep and clean kitchen", points=2, is_good=True, image_id="vacuum"),
Task(name="Do homework early", points=2, is_good=True, image_id="homework"),
Task(name="Be good for the day", points=2, is_good=True, image_id="good"),
Task(name="Clean your mess", points=2, is_good=True, image_id="broom"),
Task(name="Fighting", points=2, is_good=False, image_id="fighting"),
Task(name="Yelling at parents", points=2, is_good=False, image_id="yelling"),
Task(name="Lying", points=2, is_good=False, image_id="lying"),
Task(name="Not doing what told", points=2, is_good=False, image_id="ignore"),
Task(name="Not flushing toilet", points=2, is_good=False, image_id="toilet"),
]
for task in default_tasks:
task_db.insert(task.to_dict())
def createDefaultRewards():
"""Create default rewards if none exist."""
if len(reward_db.all()) == 0:
default_rewards = [
Reward(name="Choose meal", description="Choose dinner or lunch", cost=3, image_id="meal"),
Reward(name="$1", description="Money is always nice", cost=8, image_id='money'),
Reward(name="$5", description="Even more money", cost=12, image_id='money'),
Reward(name="Tablet 1 hour", description="Play your games", cost=5, image_id='tablet'),
Reward(name="Computer with dad", description="Let's play a game together", cost=5, image_id='games-with-dad'),
Reward(name="Computer 1 hour", description="Minecraft or Terraria?", cost=5, image_id='computer-game'),
Reward(name="TV 1 hour", description="Too much is bad for you.", cost=5, image_id='tv'),
Reward(name="Candy from store", description="Yum!", cost=5, image_id='candy'),
]
for reward in default_rewards:
reward_db.insert(reward.to_dict())
def initializeImages():
"""Initialize the image database with default images if empty."""
if len(image_db.all()) == 0:
@@ -91,6 +127,7 @@ def initializeImages():
('boy03', IMAGE_TYPE_PROFILE, '.png', True),
('boy04', IMAGE_TYPE_PROFILE, '.png', True),
('broom', IMAGE_TYPE_ICON, '.png', True),
('candy', IMAGE_TYPE_ICON, '.png', True),
('computer-game', IMAGE_TYPE_ICON, '.png', True),
('fighting', IMAGE_TYPE_ICON, '.png', True),
('games-with-dad', IMAGE_TYPE_ICON, '.png', True),

View File

@@ -10,7 +10,7 @@ from api.task_api import task_api
from config.version import get_full_version
from events.broadcaster import Broadcaster
from events.sse import sse_response_for_user, send_to_user
from db.default import initializeImages
from db.default import initializeImages, createDefaultTasks, createDefaultRewards
# Configure logging once at application startup
logging.basicConfig(
@@ -63,6 +63,8 @@ def start_background_threads():
# TODO: implement users
os.makedirs(get_user_image_dir("user123"), exist_ok=True)
initializeImages()
createDefaultTasks()
createDefaultRewards()
start_background_threads()

BIN
resources/images/candy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -67,7 +67,6 @@ function handleChildRewardSet(event: Event) {
function handleRewardRequest(event: Event) {
const payload = event.payload as ChildRewardRequestEventPayload
console.log('Received child_reward_request event:', payload)
const childId = payload.child_id
const rewardId = payload.reward_id
if (child.value && childId == child.value.id) {

View File

@@ -11,6 +11,7 @@ import type {
Child,
Event,
Reward,
RewardStatus,
ChildTaskTriggeredEventPayload,
ChildRewardTriggeredEventPayload,
ChildRewardRequestEventPayload,
@@ -36,6 +37,7 @@ const selectedReward = ref<Reward | null>(null)
const childRewardListRef = ref()
const childChoreListRef = ref()
const childHabitListRef = ref()
const showPendingRewardDialog = ref(false)
function handleTaskTriggered(event: Event) {
const payload = event.payload as ChildTaskTriggeredEventPayload
@@ -221,9 +223,50 @@ onUnmounted(() => {
const triggerTask = (task: Task) => {
selectedTask.value = task
const pendingRewardIds = childRewardListRef.value?.getPendingRewards()
if (pendingRewardIds && pendingRewardIds.length > 0) {
showPendingRewardDialog.value = true
return
}
showConfirm.value = true
}
async function cancelRewardById(rewardId: string) {
if (!child.value?.id) {
return
}
try {
await fetch(`/api/child/${child.value.id}/cancel-request-reward`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ reward_id: rewardId }),
})
} catch (err) {
console.error(`Failed to cancel reward ID ${rewardId}:`, err)
}
}
async function cancelPendingReward() {
if (!child.value?.id) {
showPendingRewardDialog.value = false
return
}
try {
const pendingRewardIds = childRewardListRef.value?.getPendingRewards()
await Promise.all(pendingRewardIds?.map((id: string) => cancelRewardById(id)) || [])
childRewardListRef.value?.refresh()
} catch (err) {
console.error('Failed to cancel pending reward:', err)
} finally {
showPendingRewardDialog.value = false
// After cancelling, proceed to trigger the task if one was selected
console.log('Proceeding to trigger task after cancelling pending rewards.', selectedTask.value)
if (selectedTask.value) {
showConfirm.value = true
}
}
}
const confirmTriggerTask = async () => {
if (!child.value?.id || !selectedTask.value) return
try {
@@ -333,6 +376,22 @@ const childId = computed(() => child.value?.id ?? null)
Assign Rewards
</button>
</div>
<!-- Pending Reward Dialog -->
<div v-if="showPendingRewardDialog" class="modal-backdrop">
<div class="modal">
<div class="dialog-message" style="margin-bottom: 1.2rem">
There is a pending reward request. The reward must be cancelled before triggering a new
task.<br />
Would you like to cancel the pending reward?
</div>
<div class="actions">
<button @click="cancelPendingReward">Yes, Cancel Reward</button>
<button @click="showPendingRewardDialog = false">No</button>
</div>
</div>
</div>
<div v-if="showConfirm && selectedTask" class="modal-backdrop">
<div class="modal">
<div class="task-info">

View File

@@ -127,13 +127,17 @@ watch(
},
)
function getPendingRewards(): string[] {
return rewards.value.filter((r) => r.redeeming).map((r) => r.id)
}
// revoke created object URLs when component unmounts to avoid memory leaks
onBeforeUnmount(() => {
revokeAllImageUrls()
})
// expose refresh method for parent component
defineExpose({ refresh: () => fetchRewards(props.childId) })
defineExpose({ refresh: () => fetchRewards(props.childId), getPendingRewards })
const isAnyPending = computed(() => rewards.value.some((r) => r.redeeming))
</script>