added defaults for now.
reworked tasks and pending rewards
This commit is contained in:
@@ -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
|
#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_query = Query()
|
||||||
pending = pending_reward_db.get((pending_query.child_id == child.id) & (pending_query.reward_id == reward.id))
|
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.append(status.to_dict())
|
||||||
|
|
||||||
statuses.sort(key=lambda s: s['cost'])
|
statuses.sort(key=lambda s: s['cost'])
|
||||||
|
|||||||
@@ -82,6 +82,42 @@ def populate_default_data():
|
|||||||
"rewards": rewards
|
"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():
|
def initializeImages():
|
||||||
"""Initialize the image database with default images if empty."""
|
"""Initialize the image database with default images if empty."""
|
||||||
if len(image_db.all()) == 0:
|
if len(image_db.all()) == 0:
|
||||||
@@ -91,6 +127,7 @@ def initializeImages():
|
|||||||
('boy03', IMAGE_TYPE_PROFILE, '.png', True),
|
('boy03', IMAGE_TYPE_PROFILE, '.png', True),
|
||||||
('boy04', IMAGE_TYPE_PROFILE, '.png', True),
|
('boy04', IMAGE_TYPE_PROFILE, '.png', True),
|
||||||
('broom', IMAGE_TYPE_ICON, '.png', True),
|
('broom', IMAGE_TYPE_ICON, '.png', True),
|
||||||
|
('candy', IMAGE_TYPE_ICON, '.png', True),
|
||||||
('computer-game', IMAGE_TYPE_ICON, '.png', True),
|
('computer-game', IMAGE_TYPE_ICON, '.png', True),
|
||||||
('fighting', IMAGE_TYPE_ICON, '.png', True),
|
('fighting', IMAGE_TYPE_ICON, '.png', True),
|
||||||
('games-with-dad', IMAGE_TYPE_ICON, '.png', True),
|
('games-with-dad', IMAGE_TYPE_ICON, '.png', True),
|
||||||
|
|||||||
4
main.py
4
main.py
@@ -10,7 +10,7 @@ from api.task_api import task_api
|
|||||||
from config.version import get_full_version
|
from config.version import get_full_version
|
||||||
from events.broadcaster import Broadcaster
|
from events.broadcaster import Broadcaster
|
||||||
from events.sse import sse_response_for_user, send_to_user
|
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
|
# Configure logging once at application startup
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@@ -63,6 +63,8 @@ def start_background_threads():
|
|||||||
# TODO: implement users
|
# TODO: implement users
|
||||||
os.makedirs(get_user_image_dir("user123"), exist_ok=True)
|
os.makedirs(get_user_image_dir("user123"), exist_ok=True)
|
||||||
initializeImages()
|
initializeImages()
|
||||||
|
createDefaultTasks()
|
||||||
|
createDefaultRewards()
|
||||||
start_background_threads()
|
start_background_threads()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
resources/images/candy.png
Normal file
BIN
resources/images/candy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@@ -67,7 +67,6 @@ function handleChildRewardSet(event: Event) {
|
|||||||
|
|
||||||
function handleRewardRequest(event: Event) {
|
function handleRewardRequest(event: Event) {
|
||||||
const payload = event.payload as ChildRewardRequestEventPayload
|
const payload = event.payload as ChildRewardRequestEventPayload
|
||||||
console.log('Received child_reward_request event:', payload)
|
|
||||||
const childId = payload.child_id
|
const childId = payload.child_id
|
||||||
const rewardId = payload.reward_id
|
const rewardId = payload.reward_id
|
||||||
if (child.value && childId == child.value.id) {
|
if (child.value && childId == child.value.id) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import type {
|
|||||||
Child,
|
Child,
|
||||||
Event,
|
Event,
|
||||||
Reward,
|
Reward,
|
||||||
|
RewardStatus,
|
||||||
ChildTaskTriggeredEventPayload,
|
ChildTaskTriggeredEventPayload,
|
||||||
ChildRewardTriggeredEventPayload,
|
ChildRewardTriggeredEventPayload,
|
||||||
ChildRewardRequestEventPayload,
|
ChildRewardRequestEventPayload,
|
||||||
@@ -36,6 +37,7 @@ const selectedReward = ref<Reward | null>(null)
|
|||||||
const childRewardListRef = ref()
|
const childRewardListRef = ref()
|
||||||
const childChoreListRef = ref()
|
const childChoreListRef = ref()
|
||||||
const childHabitListRef = ref()
|
const childHabitListRef = ref()
|
||||||
|
const showPendingRewardDialog = ref(false)
|
||||||
|
|
||||||
function handleTaskTriggered(event: Event) {
|
function handleTaskTriggered(event: Event) {
|
||||||
const payload = event.payload as ChildTaskTriggeredEventPayload
|
const payload = event.payload as ChildTaskTriggeredEventPayload
|
||||||
@@ -221,9 +223,50 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
const triggerTask = (task: Task) => {
|
const triggerTask = (task: Task) => {
|
||||||
selectedTask.value = task
|
selectedTask.value = task
|
||||||
|
const pendingRewardIds = childRewardListRef.value?.getPendingRewards()
|
||||||
|
if (pendingRewardIds && pendingRewardIds.length > 0) {
|
||||||
|
showPendingRewardDialog.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
showConfirm.value = true
|
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 () => {
|
const confirmTriggerTask = async () => {
|
||||||
if (!child.value?.id || !selectedTask.value) return
|
if (!child.value?.id || !selectedTask.value) return
|
||||||
try {
|
try {
|
||||||
@@ -333,6 +376,22 @@ const childId = computed(() => child.value?.id ?? null)
|
|||||||
Assign Rewards
|
Assign Rewards
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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 v-if="showConfirm && selectedTask" class="modal-backdrop">
|
||||||
<div class="modal">
|
<div class="modal">
|
||||||
<div class="task-info">
|
<div class="task-info">
|
||||||
|
|||||||
@@ -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
|
// revoke created object URLs when component unmounts to avoid memory leaks
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
revokeAllImageUrls()
|
revokeAllImageUrls()
|
||||||
})
|
})
|
||||||
|
|
||||||
// expose refresh method for parent component
|
// 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))
|
const isAnyPending = computed(() => rewards.value.some((r) => r.redeeming))
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user