From 5b83fa12cae4ae7ea928624fc88ec107ef07b363 Mon Sep 17 00:00:00 2001 From: Ryan Kegel Date: Mon, 8 Dec 2025 16:08:19 -0500 Subject: [PATCH] round 5 --- db/db.py | 4 +- db/default.py | 23 ++++++ main.py | 2 + .../src/components/ChildrenListView.vue | 1 - web/vue-app/src/components/LoginButton.vue | 5 ++ .../src/components/child/ChildView.vue | 23 ++++++ .../src/components/child/ParentView.vue | 3 - .../src/components/child/RewardAssignView.vue | 68 ++++++++++++++++- .../src/components/child/TaskAssignView.vue | 75 +++++++++++++++++-- .../src/components/reward/RewardList.vue | 1 + .../src/components/reward/RewardView.vue | 5 +- web/vue-app/src/components/task/TaskList.vue | 3 +- web/vue-app/src/components/task/TaskView.vue | 3 +- web/vue-app/src/layout/ParentLayout.vue | 1 + 14 files changed, 195 insertions(+), 22 deletions(-) diff --git a/db/db.py b/db/db.py index f7f1b64..acde135 100644 --- a/db/db.py +++ b/db/db.py @@ -6,6 +6,7 @@ from tinydb import TinyDB DB_ENV = os.environ.get('DB_ENV', 'prod') base_dir = os.path.dirname(__file__) + class LockedTable: """ Thread-safe wrapper around a TinyDB table. All callable attribute access @@ -98,4 +99,5 @@ if DB_ENV == 'test': task_db.truncate() reward_db.truncate() image_db.truncate() - pending_reward_db.truncate() \ No newline at end of file + pending_reward_db.truncate() + diff --git a/db/default.py b/db/default.py index b75a6b6..4bafb2c 100644 --- a/db/default.py +++ b/db/default.py @@ -82,5 +82,28 @@ def populate_default_data(): "rewards": rewards } +def initializeImages(): + """Initialize the image database with default images if empty.""" + if len(image_db.all()) == 0: + image_defs = [ + ('computer-game', IMAGE_TYPE_ICON, '.png', True), + ('ice-cream', IMAGE_TYPE_ICON, '.png', True), + ('meal', IMAGE_TYPE_ICON, '.png', True), + ('playground', IMAGE_TYPE_ICON, '.png', True), + ('tablet', IMAGE_TYPE_ICON, '.png', True), + ('boy01', IMAGE_TYPE_PROFILE, '.png', True), + ('girl01', IMAGE_TYPE_PROFILE, '.png', True), + ('girl02', IMAGE_TYPE_PROFILE, '.png', True), + ('boy02', IMAGE_TYPE_PROFILE, '.png', True), + ('boy03', IMAGE_TYPE_PROFILE, '.png', True), + ('girl03', IMAGE_TYPE_PROFILE, '.png', True), + ('boy04', IMAGE_TYPE_PROFILE, '.png', True), + ('girl04', IMAGE_TYPE_PROFILE, '.png', True), + ] + for _id, _type, ext, perm in image_defs: + img = Image(type=_type, extension=ext, permanent=perm) + img.id = _id + image_db.insert(img.to_dict()) + if __name__ == "__main__": result = populate_default_data() diff --git a/main.py b/main.py index a16db46..bbb5dbc 100644 --- a/main.py +++ b/main.py @@ -7,6 +7,7 @@ from api.reward_api import reward_api from api.task_api import task_api from events.broadcaster import Broadcaster from events.sse import sse_response_for_user, send_to_user +from db.default import initializeImages app = Flask(__name__) #CORS(app, resources={r"/api/*": {"origins": ["http://localhost:3000", "http://localhost:5173"]}}) @@ -48,4 +49,5 @@ start_background_threads() if __name__ == '__main__': + initializeImages() app.run(debug=False, host='0.0.0.0', port=5000, threaded=True) \ No newline at end of file diff --git a/web/vue-app/src/components/ChildrenListView.vue b/web/vue-app/src/components/ChildrenListView.vue index 7fc8e32..b7ff86a 100644 --- a/web/vue-app/src/components/ChildrenListView.vue +++ b/web/vue-app/src/components/ChildrenListView.vue @@ -211,7 +211,6 @@ const openMenu = (childId: string | number, evt?: Event) => { activeMenuFor.value = childId } const closeMenu = () => { - console.log('Closing menu') activeMenuFor.value = null } diff --git a/web/vue-app/src/components/LoginButton.vue b/web/vue-app/src/components/LoginButton.vue index a928fac..769ba37 100644 --- a/web/vue-app/src/components/LoginButton.vue +++ b/web/vue-app/src/components/LoginButton.vue @@ -30,6 +30,11 @@ const submit = () => { return } + if (pin.value !== '1179') { + error.value = 'Incorrect PIN' + return + } + // Authenticate parent and navigate authenticateParent() close() diff --git a/web/vue-app/src/components/child/ChildView.vue b/web/vue-app/src/components/child/ChildView.vue index c774236..ba4adfb 100644 --- a/web/vue-app/src/components/child/ChildView.vue +++ b/web/vue-app/src/components/child/ChildView.vue @@ -246,6 +246,26 @@ async function fetchChildData(id: string | number) { } } +let inactivityTimer: ReturnType | null = null + +function resetInactivityTimer() { + if (inactivityTimer) clearTimeout(inactivityTimer) + inactivityTimer = setTimeout(() => { + router.push({ name: 'ChildrenListView' }) + }, 15000) // 15 seconds +} + +function setupInactivityListeners() { + const events = ['mousemove', 'mousedown', 'keydown', 'touchstart'] + events.forEach((evt) => window.addEventListener(evt, resetInactivityTimer)) +} + +function removeInactivityListeners() { + const events = ['mousemove', 'mousedown', 'keydown', 'touchstart'] + events.forEach((evt) => window.removeEventListener(evt, resetInactivityTimer)) + if (inactivityTimer) clearTimeout(inactivityTimer) +} + onMounted(async () => { try { eventBus.on('child_task_triggered', handleTaskTriggered) @@ -269,6 +289,8 @@ onMounted(async () => { }) } } + setupInactivityListeners() + resetInactivityTimer() } catch (err) { console.error('Error in onMounted:', err) } @@ -283,6 +305,7 @@ onUnmounted(() => { eventBus.off('reward_modified', handleRewardModified) eventBus.off('child_modified', handleChildModified) eventBus.off('child_reward_request', handleRewardRequest) + removeInactivityListeners() }) diff --git a/web/vue-app/src/components/child/ParentView.vue b/web/vue-app/src/components/child/ParentView.vue index 30bfc7d..c6c7c1e 100644 --- a/web/vue-app/src/components/child/ParentView.vue +++ b/web/vue-app/src/components/child/ParentView.vue @@ -53,7 +53,6 @@ function handleRewardTriggered(event: Event) { } function handleChildTaskSet(event: Event) { - console.log('handleChildTaskSet called') const payload = event.payload as ChildTasksSetEventPayload if (child.value && payload.child_id == child.value.id) { tasks.value = payload.task_ids @@ -235,7 +234,6 @@ const confirmTriggerTask = async () => { }) if (!resp.ok) return const data = await resp.json() - console.log('Trigger task response data:', child.value.id, data.id) if (child.value && child.value.id === data.id) child.value.points = data.points } catch (err) { console.error('Failed to trigger task:', err) @@ -246,7 +244,6 @@ const confirmTriggerTask = async () => { } const triggerReward = (reward: Reward, redeemable: boolean) => { - console.log('Handle trigger reward:', reward, redeemable) if (!redeemable) return selectedReward.value = reward showRewardConfirm.value = true diff --git a/web/vue-app/src/components/child/RewardAssignView.vue b/web/vue-app/src/components/child/RewardAssignView.vue index 2fbc516..4b752c1 100644 --- a/web/vue-app/src/components/child/RewardAssignView.vue +++ b/web/vue-app/src/components/child/RewardAssignView.vue @@ -1,10 +1,24 @@