From e9f43434266536c2b0b867e82ddb63a56bee43b6 Mon Sep 17 00:00:00 2001 From: Ryan Kegel Date: Tue, 24 Mar 2026 15:31:57 -0400 Subject: [PATCH] added fixes for bug plan 1.0.6RC01 --- .../specs/{ => archive}/feat-landing-page.md | 0 .../feat-schedule-enable-disable.md | 0 .../specs/bugs-1.0.6-01/bugs-1.0.6-set01.md | 91 ++++++ .github/specs/bugs-1.0.6-01/image-1.png | Bin 0 -> 40699 bytes .github/specs/bugs-1.0.6-01/image.png | Bin 0 -> 50720 bytes backend/api/child_api.py | 35 ++- backend/api/chore_schedule_api.py | 17 +- backend/api/reward_api.py | 18 +- backend/api/task_api.py | 21 +- backend/config/version.py | 2 +- backend/tests/test_chore_confirmation.py | 94 +++++- backend/tests/test_chore_schedule_api.py | 36 ++- backend/tests/test_reward_api.py | 60 +++- backend/tests/test_task_api.py | 60 +++- frontend/vue-app/e2e/.auth/user-cc.json | 10 +- frontend/vue-app/e2e/.auth/user-delete.json | 10 +- frontend/vue-app/e2e/.auth/user.json | 10 +- .../e2e/plans/bugs-1.0.6-set01.plan.md | 281 ++++++++++++++++++ .../src/components/auth/ParentPinSetup.vue | 6 +- .../src/components/child/ChildView.vue | 65 ++++ .../src/components/child/ChoreAssignView.vue | 3 +- .../components/child/KindnessAssignView.vue | 3 +- .../src/components/child/ParentView.vue | 82 ++++- .../components/child/PenaltyAssignView.vue | 3 +- .../src/components/child/RewardAssignView.vue | 3 +- .../notification/NotificationView.vue | 32 +- .../src/components/shared/ScrollingList.vue | 13 + 27 files changed, 890 insertions(+), 65 deletions(-) rename .github/specs/{ => archive}/feat-landing-page.md (100%) rename .github/specs/{ => archive}/feat-schedule-enable-disable.md (100%) create mode 100644 .github/specs/bugs-1.0.6-01/bugs-1.0.6-set01.md create mode 100644 .github/specs/bugs-1.0.6-01/image-1.png create mode 100644 .github/specs/bugs-1.0.6-01/image.png create mode 100644 frontend/vue-app/e2e/plans/bugs-1.0.6-set01.plan.md diff --git a/.github/specs/feat-landing-page.md b/.github/specs/archive/feat-landing-page.md similarity index 100% rename from .github/specs/feat-landing-page.md rename to .github/specs/archive/feat-landing-page.md diff --git a/.github/specs/feat-schedule-enable-disable.md b/.github/specs/archive/feat-schedule-enable-disable.md similarity index 100% rename from .github/specs/feat-schedule-enable-disable.md rename to .github/specs/archive/feat-schedule-enable-disable.md diff --git a/.github/specs/bugs-1.0.6-01/bugs-1.0.6-set01.md b/.github/specs/bugs-1.0.6-01/bugs-1.0.6-set01.md new file mode 100644 index 0000000..4549d7b --- /dev/null +++ b/.github/specs/bugs-1.0.6-01/bugs-1.0.6-set01.md @@ -0,0 +1,91 @@ +# Bug List for 1.0.6 Set 1 + +## Uppercase text for parent verification ✅ + +When entering a parent verification code (when creating a parent PIN) all entered characters should be uppercase. Convert the characters to uppercase in the UI. + +**Fix:** Added `text-transform: uppercase` CSS and an `@input` handler that converts the value to uppercase on the verification code input in `ParentPinSetup.vue`. + +## Assigning tasks should have the name of the child being assigned. ✅ + +![alt text](image.png) + +Instead of just saying "Assign Chores", it should say Assign Chores for [Name] or Assign Chores [Name] + +**Fix:** All four assign views (`ChoreAssignView`, `RewardAssignView`, `KindnessAssignView`, `PenaltyAssignView`) now read the child's name from the route query parameter and display it in the heading (e.g. "Assign Chores for Emma"). The `ParentView` passes `query: { name: child.name }` when navigating to each assign route. + +## Child completing a general chore (not scheduled) ✅ + +When a child completes a general chore and it is approved, the parent sees "COMPLETED", but the child can click and have it approved again. The chore on the parent side should not say completed, it should just go back to normal state. General chores, by default, can be completed multiple times, so it should not say "COMPLETED" on the parent side. + +**Fix:** Backend `child_api.py` now distinguishes between general and scheduled chores during trigger and approval. General chores no longer create an "approved" `PendingConfirmation` record — the existing pending record is simply removed on approval. Only scheduled chores persist an approved confirmation (which drives the COMPLETED stamp). This allows general chores to return to normal state and be triggered again. + +## Scheduled chore and too late ✅ + +When extending "Make Your Bed" (a scheduled chore that is too late,) the "TOO LATE" banner does not go away even though it was extended. Activating it again tells me that the chore was already extended. I'm expecting that when the chore is extended the too late banner disappears. + +**Fix:** `ParentView.vue` `doExtendTime()` now explicitly calls `childChoreListRef.value?.refresh()` immediately after the extension API call, before resetting expiry timers. This forces the chore list to re-fetch and re-render, removing the TOO LATE banner without waiting for an SSE race condition. + +## Notification runoff ✅ + +Notifications for completed or pending tasks can sometimes be too long for the card can run off the card. +![alt text](image-1.png) +in the red square, the icon for the chore has run off the card. We need a better way to fit the information. Make the card heights for only notifications dynamic? + +**Fix:** Updated `NotificationView.vue` CSS to use `overflow: hidden` and `text-overflow: ellipsis` on notification card content, preventing icons and text from overflowing card boundaries. Card heights are now flexible to accommodate varying content lengths. + +## Point editing events ✅ + +When changing points in parent mode, the child mode does not see the point update until refreshed. There should be a functional SSE that updates the points in the child view (or other parent mode tabs) + +**Fix:** Added SSE event handlers in `ChildView.vue` for `child_override_set` and `child_override_deleted` events. When a parent changes point overrides, the child view now listens for these events and refreshes the affected task lists (chores, kindness, penalties) in real time. Handlers are registered in `onMounted` and cleaned up in `onUnmounted`. + +## Child tasks list editing in view ✅ + +When a child's task is edited (points changed or scheduled, etc) and saved. The view resets. The problem is that when the view resets, if the edited task was off screen and had to be scrolled to, the user has to do this again. The task that was just edited should be automatically scrolled to so that it is in view both vertically and horizontally. + +**Fix:** `ScrollingList.vue` now exposes a `scrollToItem(itemId)` method that scrolls an item into view both vertically (via `scrollIntoView`) and horizontally (via `centerItem`). `ParentView.vue` tracks the last edited item ID when saving an override, and after the list refreshes, calls `scrollToItem()` on the appropriate list ref to bring the edited card back into view. + +## Task sorting in child task view ✅ + +When viewing the child's chores, kindnesses, penalties, rewards. The items should be sorted differently. + +### Parent mode order + +1. Pending tasks (chores and rewards) +2. General tasks +3. Too late (chores) when it applies +4. Completed (chores) when it applies +5. Chores that are not scheduled for today. + +### Child mode order + +- Chores + +1. Scheduled for today chores (with earliest deadline first when it applies) +2. General Chores +3. Pending chores + +- Rewards + +1. Pending Rewards +2. Other rewards sorted by how many more points are required. + +**Fix:** Added a `sortFn` prop to `ScrollingList.vue` that applies a custom sort after filtering. `ParentView.vue` defines `parentChoreSort()` (pending → general → expired → completed → unscheduled) and `parentRewardSort()` (pending first). `ChildView.vue` defines `childChoreSort()` (scheduled today with earliest deadline first → general → pending) and `childRewardSort()` (pending first, then ascending by points needed). These are wired as `:sort-fn` on their respective ScrollingList instances. + +### Notification clicking ✅ + +When a parent clicks on a notification, the parent view should show for the child and child's task list. However, the task that was listed in the notification should be scrolled to both vertically and horizontally. + +**Fix:** `NotificationView.vue` `handleNotificationClick()` now passes `query: { scrollTo: item.entity_id, entityType: item.entity_type }` when navigating to the parent child view. `ParentView.vue` reads these query parameters on mount and, after data loads, calls `scrollToItem()` on the correct list ref (chores, rewards, kindness, or penalties) with a short delay to ensure the list is rendered. + +### Pending chore / reward changes ✅ + +If a chore or reward is changed while pending (schedule change or point change), the pending status should be reset back to normal state. Completed chores however should stay in the completed state. + +**Fix:** Three backend API endpoints now clear pending status on modification: + +- `task_api.py` `edit_task()`: When points or type changes, removes all pending chore confirmations for that task and sends `OPERATION_RESET` SSE events. +- `chore_schedule_api.py` `set_chore_schedule()`: After upserting a schedule, removes any pending chore confirmations for that child+task and sends reset events. +- `reward_api.py` `edit_reward()`: When cost changes, removes all pending reward requests for that reward and sends `CANCELLED` SSE events. + Completed chores (approved PendingConfirmation on scheduled chores) are not affected by these changes. diff --git a/.github/specs/bugs-1.0.6-01/image-1.png b/.github/specs/bugs-1.0.6-01/image-1.png new file mode 100644 index 0000000000000000000000000000000000000000..12939fd070e1b51539346a13f012505cfd044981 GIT binary patch literal 40699 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 literal 0 HcmV?d00001 diff --git a/.github/specs/bugs-1.0.6-01/image.png b/.github/specs/bugs-1.0.6-01/image.png new file mode 100644 index 0000000000000000000000000000000000000000..c08bdbcd7c3f20dcc65d15b989586e6051d02226 GIT binary patch literal 50720 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 literal 0 HcmV?d00001 diff --git a/backend/api/child_api.py b/backend/api/child_api.py index 79b778e..dd89141 100644 --- a/backend/api/child_api.py +++ b/backend/api/child_api.py @@ -451,6 +451,7 @@ def trigger_child_task(id): child_db.update({'points': child.points}, ChildQuery.id == id) # For chores, create an approved PendingConfirmation so child view shows COMPLETED + # Only persist for scheduled chores; general chores reset to normal after trigger if task.type == 'chore': PendingQuery = Query() # Remove any existing pending confirmation for this chore @@ -458,12 +459,14 @@ def trigger_child_task(id): (PendingQuery.child_id == id) & (PendingQuery.entity_id == task_id) & (PendingQuery.entity_type == 'chore') & (PendingQuery.user_id == user_id) ) - confirmation = PendingConfirmation( - child_id=id, entity_id=task_id, entity_type='chore', - user_id=user_id, status='approved', - approved_at=datetime.now(timezone.utc).isoformat() - ) - pending_confirmations_db.insert(confirmation.to_dict()) + schedule = get_schedule(id, task_id) + if schedule: + confirmation = PendingConfirmation( + child_id=id, entity_id=task_id, entity_type='chore', + user_id=user_id, status='approved', + approved_at=datetime.now(timezone.utc).isoformat() + ) + pending_confirmations_db.insert(confirmation.to_dict()) send_event_for_current_user(Event(EventType.CHILD_CHORE_CONFIRMATION.value, ChildChoreConfirmation(id, task_id, ChildChoreConfirmation.OPERATION_APPROVED))) @@ -1198,12 +1201,20 @@ def approve_chore(id): child_db.update({'points': child.points}, ChildQuery.id == id) # Update confirmation to approved - now_str = datetime.now(timezone.utc).isoformat() - pending_confirmations_db.update( - {'status': 'approved', 'approved_at': now_str}, - (PendingQuery.child_id == id) & (PendingQuery.entity_id == task_id) & - (PendingQuery.entity_type == 'chore') & (PendingQuery.user_id == user_id) - ) + # For general (non-scheduled) chores, remove the confirmation so chore resets to normal + schedule = get_schedule(id, task_id) + if schedule: + now_str = datetime.now(timezone.utc).isoformat() + pending_confirmations_db.update( + {'status': 'approved', 'approved_at': now_str}, + (PendingQuery.child_id == id) & (PendingQuery.entity_id == task_id) & + (PendingQuery.entity_type == 'chore') & (PendingQuery.user_id == user_id) + ) + else: + pending_confirmations_db.remove( + (PendingQuery.child_id == id) & (PendingQuery.entity_id == task_id) & + (PendingQuery.entity_type == 'chore') & (PendingQuery.user_id == user_id) + ) tracking_metadata = { 'task_name': task.name, diff --git a/backend/api/chore_schedule_api.py b/backend/api/chore_schedule_api.py index c27fcd4..26949ff 100644 --- a/backend/api/chore_schedule_api.py +++ b/backend/api/chore_schedule_api.py @@ -2,7 +2,7 @@ from flask import Blueprint, request, jsonify from tinydb import Query from api.utils import get_validated_user_id, send_event_for_current_user from api.error_codes import ErrorCodes -from db.db import child_db +from db.db import child_db, pending_confirmations_db from db.chore_schedules import get_schedule, upsert_schedule, delete_schedule from db.task_extensions import get_extension, add_extension, delete_extension_for_child_task from models.chore_schedule import ChoreSchedule @@ -11,6 +11,7 @@ from events.types.event import Event from events.types.event_types import EventType from events.types.chore_schedule_modified import ChoreScheduleModified from events.types.chore_time_extended import ChoreTimeExtended +from events.types.child_chore_confirmation import ChildChoreConfirmation import logging chore_schedule_api = Blueprint('chore_schedule_api', __name__) @@ -102,6 +103,20 @@ def set_chore_schedule(child_id, task_id): delete_extension_for_child_task(child_id, task_id) upsert_schedule(schedule) + # Reset pending chore confirmations when schedule changes (completed chores stay) + PendingQuery = Query() + pending_chores = pending_confirmations_db.search( + (PendingQuery.child_id == child_id) & (PendingQuery.entity_id == task_id) & + (PendingQuery.entity_type == 'chore') & (PendingQuery.status == 'pending') + ) + for pc in pending_chores: + pending_confirmations_db.remove( + (PendingQuery.child_id == child_id) & (PendingQuery.entity_id == task_id) & + (PendingQuery.entity_type == 'chore') & (PendingQuery.status == 'pending') + ) + send_event_for_current_user(Event(EventType.CHILD_CHORE_CONFIRMATION.value, + ChildChoreConfirmation(child_id, task_id, ChildChoreConfirmation.OPERATION_RESET))) + send_event_for_current_user(Event( EventType.CHORE_SCHEDULE_MODIFIED.value, ChoreScheduleModified(child_id, task_id, ChoreScheduleModified.OPERATION_SET) diff --git a/backend/api/reward_api.py b/backend/api/reward_api.py index 85d6e0e..457fc69 100644 --- a/backend/api/reward_api.py +++ b/backend/api/reward_api.py @@ -3,11 +3,12 @@ from tinydb import Query from api.utils import send_event_for_current_user, get_validated_user_id from events.types.child_rewards_set import ChildRewardsSet -from db.db import reward_db, child_db +from db.db import reward_db, child_db, pending_confirmations_db from db.child_overrides import delete_overrides_for_entity from events.types.event import Event from events.types.event_types import EventType from events.types.reward_modified import RewardModified +from events.types.child_reward_request import ChildRewardRequest from models.reward import Reward reward_api = Blueprint('reward_api', __name__) @@ -155,6 +156,21 @@ def edit_reward(id): if not is_dirty: return jsonify({'error': 'No valid fields to update'}), 400 + # Reset pending reward requests when cost changes + if 'cost' in data: + PendingQuery = Query() + pending_rewards = pending_confirmations_db.search( + (PendingQuery.entity_id == id) & (PendingQuery.entity_type == 'reward') & + (PendingQuery.status == 'pending') + ) + for pr in pending_rewards: + pending_confirmations_db.remove( + (PendingQuery.child_id == pr['child_id']) & (PendingQuery.entity_id == id) & + (PendingQuery.entity_type == 'reward') & (PendingQuery.status == 'pending') + ) + send_event_for_current_user(Event(EventType.CHILD_REWARD_REQUEST.value, + ChildRewardRequest(pr['child_id'], id, ChildRewardRequest.REQUEST_CANCELLED))) + if reward.user_id is None: # public reward new_reward = Reward(name=reward.name, description=reward.description, cost=reward.cost, image_id=reward.image_id, user_id=user_id) reward_db.insert(new_reward.to_dict()) diff --git a/backend/api/task_api.py b/backend/api/task_api.py index 3aac1e0..8a2f50a 100644 --- a/backend/api/task_api.py +++ b/backend/api/task_api.py @@ -3,11 +3,12 @@ from tinydb import Query from api.utils import send_event_for_current_user, get_validated_user_id from events.types.child_tasks_set import ChildTasksSet -from db.db import task_db, child_db +from db.db import task_db, child_db, pending_confirmations_db from db.child_overrides import delete_overrides_for_entity from events.types.event import Event from events.types.event_types import EventType from events.types.task_modified import TaskModified +from events.types.child_chore_confirmation import ChildChoreConfirmation from models.task import Task task_api = Blueprint('task_api', __name__) @@ -176,7 +177,23 @@ def edit_task(id): if not is_dirty: return jsonify({'error': 'No valid fields to update'}), 400 - + + # Reset pending chore confirmations when task is modified (points, type, etc.) + # Completed (approved) chores stay in completed state + if 'points' in data or 'type' in data: + PendingQuery = Query() + pending_chores = pending_confirmations_db.search( + (PendingQuery.entity_id == id) & (PendingQuery.entity_type == 'chore') & + (PendingQuery.status == 'pending') + ) + for pc in pending_chores: + pending_confirmations_db.remove( + (PendingQuery.child_id == pc['child_id']) & (PendingQuery.entity_id == id) & + (PendingQuery.entity_type == 'chore') & (PendingQuery.status == 'pending') + ) + send_event_for_current_user(Event(EventType.CHILD_CHORE_CONFIRMATION.value, + ChildChoreConfirmation(pc['child_id'], id, ChildChoreConfirmation.OPERATION_RESET))) + if task.user_id is None: # public task new_task = Task(name=task.name, points=task.points, type=task.type, image_id=task.image_id, user_id=user_id) task_db.insert(new_task.to_dict()) diff --git a/backend/config/version.py b/backend/config/version.py index 571f67b..b25a512 100644 --- a/backend/config/version.py +++ b/backend/config/version.py @@ -2,7 +2,7 @@ # file: config/version.py import os -BASE_VERSION = "1.0.6RC01" # update manually when releasing features +BASE_VERSION = "1.0.6RC02" # update manually when releasing features def get_full_version() -> str: """ diff --git a/backend/tests/test_chore_confirmation.py b/backend/tests/test_chore_confirmation.py index 9eede70..2065916 100644 --- a/backend/tests/test_chore_confirmation.py +++ b/backend/tests/test_chore_confirmation.py @@ -7,11 +7,12 @@ from datetime import date as date_type from flask import Flask from api.child_api import child_api from api.auth_api import auth_api -from db.db import child_db, task_db, reward_db, users_db, pending_confirmations_db, tracking_events_db +from db.db import child_db, task_db, reward_db, users_db, pending_confirmations_db, tracking_events_db, chore_schedules_db from tinydb import Query from models.child import Child from models.pending_confirmation import PendingConfirmation from models.tracking_event import TrackingEvent +from models.chore_schedule import ChoreSchedule TEST_EMAIL = "testuser@example.com" @@ -214,13 +215,12 @@ def test_parent_approve_chore_success(client): data = resp.get_json() assert data['points'] == points_before + 10 - # Verify confirmation is now approved + # General chore: PendingConfirmation should be removed after approval PQ = Query() conf = pending_confirmations_db.get( (PQ.child_id == child_id) & (PQ.entity_id == task_id) & (PQ.entity_type == 'chore') ) - assert conf['status'] == 'approved' - assert conf['approved_at'] is not None + assert conf is None def test_parent_approve_chore_not_pending(client): @@ -294,7 +294,8 @@ def test_parent_reject_chore_creates_tracking_event(client): def test_parent_reset_chore_success(client): child_id, task_id = setup_child_and_chore(chore_points=10) - # Confirm and approve first + _add_schedule_for(child_id, task_id) + # Confirm and approve first (scheduled chore keeps PendingConfirmation) client.post(f'/child/{child_id}/confirm-chore', json={'task_id': task_id}) client.post(f'/child/{child_id}/approve-chore', json={'task_id': task_id}) # Now reset @@ -320,6 +321,7 @@ def test_parent_reset_chore_not_completed(client): def test_parent_reset_chore_creates_tracking_event(client): child_id, task_id = setup_child_and_chore(chore_points=10) + _add_schedule_for(child_id, task_id) client.post(f'/child/{child_id}/confirm-chore', json={'task_id': task_id}) client.post(f'/child/{child_id}/approve-chore', json={'task_id': task_id}) tracking_events_db.truncate() @@ -330,8 +332,9 @@ def test_parent_reset_chore_creates_tracking_event(client): def test_parent_reset_then_child_confirm_again(client): - """Full cycle: confirm → approve → reset → confirm → approve.""" + """Full cycle: confirm → approve → reset → confirm → approve (scheduled chore).""" child_id, task_id = setup_child_and_chore(chore_points=10) + _add_schedule_for(child_id, task_id) child_db.update({'points': 0}, Query().id == child_id) # First cycle @@ -359,20 +362,19 @@ def test_parent_reset_then_child_confirm_again(client): # --------------------------------------------------------------------------- def test_parent_trigger_chore_directly_creates_approved_confirmation(client): + """General chore trigger should not persist PendingConfirmation.""" child_id, task_id = setup_child_and_chore(chore_points=10) child_db.update({'points': 0}, Query().id == child_id) resp = client.post(f'/child/{child_id}/trigger-task', json={'task_id': task_id}) assert resp.status_code == 200 assert resp.get_json()['points'] == 10 - # Verify an approved PendingConfirmation exists + # General chore: no PendingConfirmation should remain PQ = Query() conf = pending_confirmations_db.get( (PQ.child_id == child_id) & (PQ.entity_id == task_id) & (PQ.entity_type == 'chore') ) - assert conf is not None - assert conf['status'] == 'approved' - assert conf['approved_at'] is not None + assert conf is None # --------------------------------------------------------------------------- @@ -479,3 +481,75 @@ def test_list_pending_confirmations_filters_by_user(client): resp = client.get('/pending-confirmations') assert resp.status_code == 200 assert resp.get_json()['count'] == 0 + + +# --------------------------------------------------------------------------- +# Bug #3: General vs Scheduled chore approval +# --------------------------------------------------------------------------- + +def _add_schedule_for(child_id, task_id): + """Insert a simple days-mode schedule so the chore is treated as scheduled.""" + chore_schedules_db.truncate() + sched = ChoreSchedule( + child_id=child_id, task_id=task_id, mode='days', + day_configs=[{'day': d, 'hour': 8, 'minute': 0} for d in range(7)] + ) + chore_schedules_db.insert(sched.to_dict()) + + +def test_approve_general_chore_removes_pending(client): + """Approving a general (non-scheduled) chore should remove PendingConfirmation.""" + child_id, task_id = setup_child_and_chore(chore_points=5) + chore_schedules_db.truncate() + client.post(f'/child/{child_id}/confirm-chore', json={'task_id': task_id}) + resp = client.post(f'/child/{child_id}/approve-chore', json={'task_id': task_id}) + assert resp.status_code == 200 + PQ = Query() + conf = pending_confirmations_db.get( + (PQ.child_id == child_id) & (PQ.entity_id == task_id) & (PQ.entity_type == 'chore') + ) + assert conf is None + + +def test_approve_scheduled_chore_keeps_approved_pending(client): + """Approving a scheduled chore should keep PendingConfirmation as approved.""" + child_id, task_id = setup_child_and_chore(chore_points=5) + _add_schedule_for(child_id, task_id) + client.post(f'/child/{child_id}/confirm-chore', json={'task_id': task_id}) + resp = client.post(f'/child/{child_id}/approve-chore', json={'task_id': task_id}) + assert resp.status_code == 200 + PQ = Query() + conf = pending_confirmations_db.get( + (PQ.child_id == child_id) & (PQ.entity_id == task_id) & (PQ.entity_type == 'chore') + ) + assert conf is not None + assert conf['status'] == 'approved' + assert conf['approved_at'] is not None + + +def test_trigger_scheduled_chore_creates_approved_confirmation(client): + """Parent trigger on scheduled chore should create approved PendingConfirmation.""" + child_id, task_id = setup_child_and_chore(chore_points=10) + _add_schedule_for(child_id, task_id) + child_db.update({'points': 0}, Query().id == child_id) + resp = client.post(f'/child/{child_id}/trigger-task', json={'task_id': task_id}) + assert resp.status_code == 200 + assert resp.get_json()['points'] == 10 + PQ = Query() + conf = pending_confirmations_db.get( + (PQ.child_id == child_id) & (PQ.entity_id == task_id) & (PQ.entity_type == 'chore') + ) + assert conf is not None + assert conf['status'] == 'approved' + + +def test_approve_general_chore_can_be_confirmed_again(client): + """After approving a general chore, child can confirm it again.""" + child_id, task_id = setup_child_and_chore(chore_points=5) + chore_schedules_db.truncate() + # First cycle + client.post(f'/child/{child_id}/confirm-chore', json={'task_id': task_id}) + client.post(f'/child/{child_id}/approve-chore', json={'task_id': task_id}) + # Should be able to confirm again + resp = client.post(f'/child/{child_id}/confirm-chore', json={'task_id': task_id}) + assert resp.status_code == 200 diff --git a/backend/tests/test_chore_schedule_api.py b/backend/tests/test_chore_schedule_api.py index a83c581..29f1157 100644 --- a/backend/tests/test_chore_schedule_api.py +++ b/backend/tests/test_chore_schedule_api.py @@ -6,8 +6,9 @@ from werkzeug.security import generate_password_hash from flask import Flask from api.chore_schedule_api import chore_schedule_api from api.auth_api import auth_api -from db.db import users_db, child_db, chore_schedules_db, task_extensions_db +from db.db import users_db, child_db, chore_schedules_db, task_extensions_db, pending_confirmations_db, task_db from tinydb import Query +from models.pending_confirmation import PendingConfirmation TEST_EMAIL = "sched_test@example.com" @@ -412,3 +413,36 @@ def test_chore_schedule_to_dict_includes_enabled(): d = s.to_dict() assert "enabled" in d assert d["enabled"] is False + + +# --------------------------------------------------------------------------- +# Bug #10: Setting schedule resets pending confirmations +# --------------------------------------------------------------------------- + +def test_set_schedule_resets_pending_confirmation(client): + """Setting/updating a chore schedule should remove pending chore confirmations.""" + chore_schedules_db.truncate() + pending_confirmations_db.truncate() + task_db.truncate() + + task_db.insert({ + 'id': TEST_TASK_ID, 'name': 'Test Chore', 'points': 5, + 'type': 'chore', 'user_id': TEST_USER_ID + }) + pending_confirmations_db.insert(PendingConfirmation( + child_id=TEST_CHILD_ID, entity_id=TEST_TASK_ID, + entity_type='chore', user_id=TEST_USER_ID + ).to_dict()) + + payload = { + "mode": "days", + "day_configs": [{"day": 1, "hour": 8, "minute": 0}], + } + resp = client.put(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule', json=payload) + assert resp.status_code == 200 + + PQ = Query() + conf = pending_confirmations_db.get( + (PQ.child_id == TEST_CHILD_ID) & (PQ.entity_id == TEST_TASK_ID) & (PQ.entity_type == 'chore') + ) + assert conf is None diff --git a/backend/tests/test_reward_api.py b/backend/tests/test_reward_api.py index b2d13e1..5230388 100644 --- a/backend/tests/test_reward_api.py +++ b/backend/tests/test_reward_api.py @@ -6,9 +6,10 @@ from werkzeug.security import generate_password_hash from flask import Flask from api.reward_api import reward_api from api.auth_api import auth_api -from db.db import reward_db, child_db, users_db +from db.db import reward_db, child_db, users_db, pending_confirmations_db from tinydb import Query from models.reward import Reward +from models.pending_confirmation import PendingConfirmation import jwt @@ -126,4 +127,59 @@ def test_delete_assigned_reward_removes_from_child(client): resp2 = client.delete('/reward/r_user_owned') assert resp2.status_code == 200 child2 = child_db.search(ChildQuery.id == 'child_for_user_reward')[0] - assert 'r_user_owned' not in child2.get('rewards', []) \ No newline at end of file + assert 'r_user_owned' not in child2.get('rewards', []) + + +# --------------------------------------------------------------------------- +# Bug #10: Editing reward cost resets pending reward requests +# --------------------------------------------------------------------------- + +def test_edit_reward_cost_resets_pending_requests(client): + """Changing a reward's cost should remove all pending reward requests for that reward.""" + reward_db.truncate() + child_db.truncate() + pending_confirmations_db.truncate() + + reward_db.insert({'id': 'r_pending', 'name': 'Prize', 'cost': 50, 'user_id': 'testuserid'}) + child_db.insert({ + 'id': 'c1', 'name': 'Alice', 'age': 8, 'points': 100, + 'tasks': [], 'rewards': ['r_pending'], 'user_id': 'testuserid' + }) + pending_confirmations_db.insert(PendingConfirmation( + child_id='c1', entity_id='r_pending', entity_type='reward', user_id='testuserid' + ).to_dict()) + + resp = client.put('/reward/r_pending/edit', json={'cost': 75}) + assert resp.status_code == 200 + + PQ = Query() + conf = pending_confirmations_db.get( + (PQ.child_id == 'c1') & (PQ.entity_id == 'r_pending') & (PQ.entity_type == 'reward') + ) + assert conf is None + + +def test_edit_reward_name_only_keeps_pending(client): + """Changing only the reward name should not reset pending requests.""" + reward_db.truncate() + child_db.truncate() + pending_confirmations_db.truncate() + + reward_db.insert({'id': 'r_name', 'name': 'Old Prize', 'cost': 50, 'user_id': 'testuserid'}) + child_db.insert({ + 'id': 'c2', 'name': 'Bob', 'age': 7, 'points': 100, + 'tasks': [], 'rewards': ['r_name'], 'user_id': 'testuserid' + }) + pending_confirmations_db.insert(PendingConfirmation( + child_id='c2', entity_id='r_name', entity_type='reward', user_id='testuserid' + ).to_dict()) + + resp = client.put('/reward/r_name/edit', json={'name': 'New Prize'}) + assert resp.status_code == 200 + + PQ = Query() + conf = pending_confirmations_db.get( + (PQ.child_id == 'c2') & (PQ.entity_id == 'r_name') & (PQ.entity_type == 'reward') + ) + assert conf is not None + assert conf['status'] == 'pending' \ No newline at end of file diff --git a/backend/tests/test_task_api.py b/backend/tests/test_task_api.py index 58ce5ac..48a2d2c 100644 --- a/backend/tests/test_task_api.py +++ b/backend/tests/test_task_api.py @@ -6,8 +6,9 @@ from werkzeug.security import generate_password_hash from flask import Flask from api.task_api import task_api from api.auth_api import auth_api -from db.db import task_db, child_db, users_db +from db.db import task_db, child_db, users_db, pending_confirmations_db from tinydb import Query +from models.pending_confirmation import PendingConfirmation import jwt @@ -146,4 +147,59 @@ def test_delete_assigned_task_removes_from_child(client): assert resp.status_code == 200 # verify the task id is no longer in the child's tasks child = child_db.search(ChildQuery.id == 'child_for_task_delete')[0] - assert 't_delete_assigned' not in child.get('tasks', []) \ No newline at end of file + assert 't_delete_assigned' not in child.get('tasks', []) + + +# --------------------------------------------------------------------------- +# Bug #10: Editing task points/type resets pending confirmations +# --------------------------------------------------------------------------- + +def test_edit_task_points_resets_pending_confirmations(client): + """Changing a task's points should remove all pending chore confirmations for that task.""" + task_db.truncate() + child_db.truncate() + pending_confirmations_db.truncate() + + task_db.insert({'id': 't_pending', 'name': 'Sweep', 'points': 5, 'type': 'chore', 'user_id': 'testuserid'}) + child_db.insert({ + 'id': 'c1', 'name': 'Alice', 'age': 8, 'points': 0, + 'tasks': ['t_pending'], 'rewards': [], 'user_id': 'testuserid' + }) + pending_confirmations_db.insert(PendingConfirmation( + child_id='c1', entity_id='t_pending', entity_type='chore', user_id='testuserid' + ).to_dict()) + + resp = client.put('/task/t_pending/edit', json={'points': 10}) + assert resp.status_code == 200 + + PQ = Query() + conf = pending_confirmations_db.get( + (PQ.child_id == 'c1') & (PQ.entity_id == 't_pending') & (PQ.entity_type == 'chore') + ) + assert conf is None + + +def test_edit_task_name_only_keeps_pending(client): + """Changing only the task name should not reset pending confirmations.""" + task_db.truncate() + child_db.truncate() + pending_confirmations_db.truncate() + + task_db.insert({'id': 't_name', 'name': 'Old Name', 'points': 5, 'type': 'chore', 'user_id': 'testuserid'}) + child_db.insert({ + 'id': 'c2', 'name': 'Bob', 'age': 7, 'points': 0, + 'tasks': ['t_name'], 'rewards': [], 'user_id': 'testuserid' + }) + pending_confirmations_db.insert(PendingConfirmation( + child_id='c2', entity_id='t_name', entity_type='chore', user_id='testuserid' + ).to_dict()) + + resp = client.put('/task/t_name/edit', json={'name': 'New Name'}) + assert resp.status_code == 200 + + PQ = Query() + conf = pending_confirmations_db.get( + (PQ.child_id == 'c2') & (PQ.entity_id == 't_name') & (PQ.entity_type == 'chore') + ) + assert conf is not None + assert conf['status'] == 'pending' \ No newline at end of file diff --git a/frontend/vue-app/e2e/.auth/user-cc.json b/frontend/vue-app/e2e/.auth/user-cc.json index 6ea0d25..dde098e 100644 --- a/frontend/vue-app/e2e/.auth/user-cc.json +++ b/frontend/vue-app/e2e/.auth/user-cc.json @@ -2,17 +2,17 @@ "cookies": [ { "name": "refresh_token", - "value": "icI1wOmbV5D-snH1lnz75Z-IGK-DuzK-RR_rKzjvvMs", + "value": "55lJfL_-PofI1V-NQ2E3Jlo4f_IE1aXqOF-wX54ZVSY", "domain": "localhost", "path": "/api/auth", - "expires": 1781792623.050125, + "expires": 1782155883.275106, "httpOnly": true, "secure": true, "sameSite": "Strict" }, { "name": "access_token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZS1jY0B0ZXN0LmNvbSIsInVzZXJfaWQiOiI0NmNmMmU0Ni1kOGFjLTRlYTItOTExYS1iZjJlMjVjNGI1ZDAiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzQwMTc1MjN9.KNGwsOe7xEIo-tDbuSP38p3zB1vEB0KZk3mGEtgHs5s", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZS1jY0B0ZXN0LmNvbSIsInVzZXJfaWQiOiIzZGQ0YTI1Yy04ZDI1LTQwNGEtODU0NS1mYjM2ZGRkYTU5NmYiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzQzODA3ODN9.b0ACVC63OobLzraiOcn4QDXipHXUVMW-Wn42XJFDOn0", "domain": "localhost", "path": "/", "expires": -1, @@ -27,11 +27,11 @@ "localStorage": [ { "name": "authSyncEvent", - "value": "{\"type\":\"logout\",\"at\":1774016622749}" + "value": "{\"type\":\"logout\",\"at\":1774379882977}" }, { "name": "parentAuth", - "value": "{\"expiresAt\":1774189423292}" + "value": "{\"expiresAt\":1774552683610}" } ] } diff --git a/frontend/vue-app/e2e/.auth/user-delete.json b/frontend/vue-app/e2e/.auth/user-delete.json index e0d488f..baf2c5c 100644 --- a/frontend/vue-app/e2e/.auth/user-delete.json +++ b/frontend/vue-app/e2e/.auth/user-delete.json @@ -2,17 +2,17 @@ "cookies": [ { "name": "refresh_token", - "value": "9eQaO12-M1GpCKeqJOmFb07Sms32J1j62DcEb5-neqs", + "value": "us7D-X0NRlXld6bpCl4epHaruPIWbkI895jFn3o84LY", "domain": "localhost", "path": "/api/auth", - "expires": 1781792623.010225, + "expires": 1782155883.187584, "httpOnly": true, "secure": true, "sameSite": "Strict" }, { "name": "access_token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZS1kZWxldGVAdGVzdC5jb20iLCJ1c2VyX2lkIjoiMTFlYjUwNTgtZmYxYi00YjgzLWExMTEtMTJjZjFkZjI4ZjA5IiwidG9rZW5fdmVyc2lvbiI6MCwiZXhwIjoxNzc0MDE3NTIyfQ.VCdPbgWlX4Mdr4NSzcyIybyNNbTSLvZE0YQGmQ-owKw", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZS1kZWxldGVAdGVzdC5jb20iLCJ1c2VyX2lkIjoiNmU0MmNhNGQtMGNkNC00MmUzLWFkOGMtMWYyOTZmMzg5NTE5IiwidG9rZW5fdmVyc2lvbiI6MCwiZXhwIjoxNzc0MzgwNzgzfQ.AthhvOE2qsaJo6x37QbGXbT9oIP2pnFks3Pj14MjmcI", "domain": "localhost", "path": "/", "expires": -1, @@ -27,11 +27,11 @@ "localStorage": [ { "name": "authSyncEvent", - "value": "{\"type\":\"logout\",\"at\":1774016622727}" + "value": "{\"type\":\"logout\",\"at\":1774379882932}" }, { "name": "parentAuth", - "value": "{\"expiresAt\":1774189423292}" + "value": "{\"expiresAt\":1774552683575}" } ] } diff --git a/frontend/vue-app/e2e/.auth/user.json b/frontend/vue-app/e2e/.auth/user.json index 5972665..504b916 100644 --- a/frontend/vue-app/e2e/.auth/user.json +++ b/frontend/vue-app/e2e/.auth/user.json @@ -2,17 +2,17 @@ "cookies": [ { "name": "refresh_token", - "value": "YAXw1Kh8dLOuSYc1ZW_sgYHWRzo0XivYXmAHAr2RV-o", + "value": "VqeLgoC9z5NgU2AtA-9xjl4DaRt0hYKDkmAJMN3dcnw", "domain": "localhost", "path": "/api/auth", - "expires": 1781792619.603786, + "expires": 1782155879.453865, "httpOnly": true, "secure": true, "sameSite": "Strict" }, { "name": "access_token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiIzMzAwYjVjNy0yMzIxLTQxMWMtOWZiZC03NzY4ZjM4NTI1OWYiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzQwMTc1MTl9.2XDC_S2IuKeF_S9HTeHEOCWlUAtlrGpByLdIwrtoxfw", + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiI3ZjA2Njc5OS03NjI1LTQ0ZGQtODIwYi0zNWRjZmFlYWVjNTMiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzQzODA3Nzl9.o9urIT0cfexJY8OarcJ4gtJKqN-_DZOw50VToPl25LU", "domain": "localhost", "path": "/", "expires": -1, @@ -27,11 +27,11 @@ "localStorage": [ { "name": "authSyncEvent", - "value": "{\"type\":\"logout\",\"at\":1774016619428}" + "value": "{\"type\":\"logout\",\"at\":1774379879245}" }, { "name": "parentAuth", - "value": "{\"expiresAt\":1774189419776}" + "value": "{\"expiresAt\":1774552679625}" } ] } diff --git a/frontend/vue-app/e2e/plans/bugs-1.0.6-set01.plan.md b/frontend/vue-app/e2e/plans/bugs-1.0.6-set01.plan.md new file mode 100644 index 0000000..5f96dd9 --- /dev/null +++ b/frontend/vue-app/e2e/plans/bugs-1.0.6-set01.plan.md @@ -0,0 +1,281 @@ +# E2E Test Plan — Bug Fixes 1.0.6 Set 1 + +Covers the bug fixes from `.github/specs/bugs-1.0.6-01/bugs-1.0.6-set01.md`. Each section proposes new E2E specs or additions to existing specs. + +--- + +## 1. Assign View Shows Child Name + +**File:** `e2e/mode_parent/task-assignment/assign-title-shows-child-name.spec.ts` +**Project:** `chromium-task-assignment` + +**Seed:** Create an isolated child with a known name via API. Create at least one chore, reward, kindness, and penalty task. + +#### 1.1. "Assign Chores" heading contains child name + +- Navigate to `/parent/{childId}/assign-chores?name={childName}` +- Verify the heading text includes the child's name + +#### 1.2. "Assign Rewards" heading contains child name + +- Navigate to `/parent/{childId}/assign-rewards?name={childName}` +- Verify heading + +#### 1.3. "Assign Kindnesses" heading contains child name + +- Navigate to `/parent/{childId}/assign-kindness?name={childName}` +- Verify heading + +#### 1.4. "Assign Penalties" heading contains child name + +- Navigate to `/parent/{childId}/assign-penalties?name={childName}` +- Verify heading + +--- + +## 2. General Chore Approval — No COMPLETED Stamp + +**File:** `e2e/mode_parent/task-activation/general-chore-no-completed.spec.ts` +**Project:** `chromium-task-activation` + +**Seed:** Create an isolated child, one general (unscheduled) chore, and one scheduled chore. Assign both to the child. + +#### 2.1. Approving a general chore awards points + +- Click general chore → confirm in dialog → approve +- Verify child's points increase by chore value + +#### 2.2. General chore does NOT show COMPLETED stamp after approval + +- After approval, verify the chore card does not contain "COMPLETED" text + +#### 2.3. General chore can be triggered again immediately after approval + +- Click the same general chore again +- Verify the confirmation dialog re-appears (chore is re-triggerable) + +#### 2.4. Scheduled chore DOES show COMPLETED after approval (contrast) + +- Trigger and approve the scheduled chore +- Verify "COMPLETED" stamp is visible on that card + +--- + +## 3. Extend Time Removes TOO LATE Banner + +**Existing file:** `e2e/mode_parent/chore-scheduler/extend-time.spec.ts` + +The existing spec already has tests for "Extend Time removes TOO LATE badge" and "gray styling removed." The bug fix ensures `refresh()` is called immediately, so the existing tests should pass without page reload. If not already asserted: + +#### 3.1. TOO LATE disappears without page reload (addition to existing spec) + +- After clicking "Extend Time," assert TOO LATE text is hidden within 3 seconds +- Do NOT call `page.reload()` between extension and assertion + +--- + +## 4. Notification Card Overflow + +**File:** `e2e/mode_parent/notifications/notification-card-overflow.spec.ts` +**Project:** `chromium-task-activation` or new `chromium-notifications` + +**Seed:** Create child + a chore with a very long name (50+ characters). Assign and trigger it so a notification is created. + +#### 4.1. Notification icon stays within card boundaries + +- Navigate to notifications view +- Get the bounding box of the notification card and its icon +- Assert the icon's bounding box is fully contained within the card's bounding box + +#### 4.2. Long notification text does not visually overflow + +- Assert the card element has appropriate overflow CSS (`overflow: hidden` or similar) +- Optionally use `toHaveScreenshot()` for visual regression + +**Note:** This test benefits from visual regression testing (`expect(page).toHaveScreenshot()`) more than functional assertions, since overflow is inherently visual. + +--- + +## 5. Override SSE Updates Child View + +**File:** `e2e/mode_parent/task-modification/override-sse-child-update.spec.ts` +**Project:** `chromium-task-modification` + +**Seed:** Create child + one chore, one kindness, one penalty. Assign all to the child. + +#### 5.1. Changing chore override points updates child view in real time + +- Open child view in the browser (`goToChildView`) +- Via API, set a point override on the chore: `PUT /api/child/{id}/task/{taskId}/override` +- Without page reload, verify the chore card displays the new point value + +#### 5.2. Changing kindness override points updates child view + +- Same pattern for kindness task — set override via API, assert child view updates + +#### 5.3. Changing penalty override points updates child view + +- Same pattern for penalty task + +#### 5.4. Deleting an override reverts child view to original points + +- Remove the override via API +- Verify child view reverts to original point value without reload + +--- + +## 6. Scroll to Edited Task After Save + +**File:** `e2e/mode_parent/task-modification/scroll-to-edited-task.spec.ts` +**Project:** `chromium-task-modification` + +**Seed:** Create child + 10–15 chores and 5+ rewards (enough to require both vertical and horizontal scrolling). Assign all to the child. + +#### 6.1. Edited chore card scrolls into viewport after override save + +- Navigate to parent child view +- Open override modal on a chore that requires scrolling to reach +- Save a new point value +- Assert the edited card `toBeInViewport()` after the list refreshes + +#### 6.2. Edited reward card scrolls into viewport after override save + +- Open override modal on a distant reward +- Save, assert card `toBeInViewport()` + +--- + +## 7. Task Sorting — Parent Mode + +**File:** `e2e/mode_parent/task-sorting/parent-sort-order.spec.ts` +**Project:** New `chromium-task-sorting` or `chromium-chore-scheduler` + +**Seed:** Create child + 5 chores in different states: + +1. A pending chore (child triggered, not yet approved) +2. A general (unscheduled) chore +3. An expired scheduled chore (today, deadline in the past → TOO LATE) +4. A completed scheduled chore (triggered + approved) +5. A chore scheduled on a non-today day (unscheduled for today) + +Also create 2+ rewards: one with a pending request, one without. + +#### 7.1. Parent chore sort: pending → general → expired → completed → unscheduled + +- Navigate to parent child view +- Read all chore card names in DOM order +- Assert the order matches: pending chore first, then general, then expired, then completed, then unscheduled-today + +#### 7.2. Parent reward sort: pending requests appear first + +- Read all reward card names in DOM order +- Assert the pending-request reward appears before other rewards + +--- + +## 8. Task Sorting — Child Mode + +**File:** `e2e/mode_parent/task-sorting/child-sort-order.spec.ts` +**Project:** Same as §7 + +**Seed:** Same child + tasks as §7, plus rewards with varying costs. + +#### 8.1. Child chore sort: scheduled today (earliest deadline first) → general → pending + +- Switch to child view (`goToChildView`) +- Read all visible chore card names in DOM order +- Assert: scheduled-today chores first (sorted by deadline), then general, then pending +- Non-today chores should be hidden in child mode + +#### 8.2. Child reward sort: pending first, then ascending by points needed + +- Read all reward card names in DOM order +- Assert: pending reward first, remaining sorted by ascending cost/points-needed + +--- + +## 9. Notification Click Navigates and Scrolls to Task + +**File:** `e2e/mode_parent/notifications/notification-click-scroll.spec.ts` +**Project:** `chromium-task-activation` or new `chromium-notifications` + +**Seed:** Create child + many chores (10+). Trigger one chore to create a pending notification. The triggered chore should be far enough in the list to require scrolling. + +#### 9.1. Clicking a chore notification navigates to parent child view + +- Navigate to notifications page +- Click the pending chore notification +- Verify URL matches `/parent/{childId}` with `scrollTo` and `entityType` query params + +#### 9.2. Target chore card is scrolled into viewport after navigation + +- After navigation from notification click, assert the target chore card `toBeInViewport()` + +#### 9.3. Works for reward notifications too + +- Request a reward → navigate to notifications → click → verify reward card is in viewport + +--- + +## 10. Pending Status Resets on Task/Reward Modification + +**File:** `e2e/mode_parent/task-modification/pending-reset-on-edit.spec.ts` +**Project:** `chromium-task-modification` + +**Seed:** Create child + one chore + one reward. Assign both. Create a chore schedule. + +#### 10.1. Editing chore points clears pending status + +- Trigger the chore (creates pending confirmation) +- Verify chore card shows pending state +- Edit the parent task's points via API: `POST /api/task/{taskId}/edit` with new points +- Refresh or wait for SSE — verify the chore card no longer shows pending state + +#### 10.2. Changing chore schedule clears pending status + +- Trigger the chore again +- Set a new schedule via API: `PUT /api/child/{childId}/task/{taskId}/schedule` +- Verify pending is cleared + +#### 10.3. Editing reward cost clears pending reward request + +- Request the reward (creates pending request) +- Edit the reward's cost via API +- Verify the reward card no longer shows pending state + +#### 10.4. Completed chores remain completed after point edit + +- Complete and approve a scheduled chore so it shows COMPLETED +- Edit the parent task's points +- Verify the chore still shows COMPLETED stamp + +#### 10.5. Editing task name only (no point change) does NOT clear pending + +- Trigger the chore +- Edit the task name only (points unchanged) via API +- Verify the chore still shows pending state + +--- + +## Summary + +| # | Area | Spec File | Tests | Priority | +| --- | -------------------------- | ------------------------------------------------------- | ----- | -------- | +| 1 | Assign view child name | `task-assignment/assign-title-shows-child-name.spec.ts` | 4 | Medium | +| 2 | General chore no COMPLETED | `task-activation/general-chore-no-completed.spec.ts` | 4 | **High** | +| 3 | Extend time TOO LATE | Existing `chore-scheduler/extend-time.spec.ts` | 0–1 | Low | +| 4 | Notification overflow | `notifications/notification-card-overflow.spec.ts` | 2 | Low | +| 5 | Override SSE child update | `task-modification/override-sse-child-update.spec.ts` | 4 | **High** | +| 6 | Scroll to edited task | `task-modification/scroll-to-edited-task.spec.ts` | 2 | Medium | +| 7 | Parent sort order | `task-sorting/parent-sort-order.spec.ts` | 2 | **High** | +| 8 | Child sort order | `task-sorting/child-sort-order.spec.ts` | 2 | **High** | +| 9 | Notification click scroll | `notifications/notification-click-scroll.spec.ts` | 3 | Medium | +| 10 | Pending reset on edit | `task-modification/pending-reset-on-edit.spec.ts` | 5 | **High** | + +**Recommended implementation order:** #2, #10, #7, #8, #5, #9, #6, #1, #3, #4 + +**Notes:** + +- Bug #1 (uppercase verification code) is omitted from this E2E plan because reaching the PIN setup step requires email delivery; this is better covered by a component-level test on `ParentPinSetup.vue`. +- Bug #4 (extend time) is largely already covered by the existing `extend-time.spec.ts` — only a minor addition may be needed. +- Bug #5 (notification overflow) is best validated through visual regression (`toHaveScreenshot()`) rather than functional assertions. diff --git a/frontend/vue-app/src/components/auth/ParentPinSetup.vue b/frontend/vue-app/src/components/auth/ParentPinSetup.vue index 89f507c..a95759d 100644 --- a/frontend/vue-app/src/components/auth/ParentPinSetup.vue +++ b/frontend/vue-app/src/components/auth/ParentPinSetup.vue @@ -21,8 +21,9 @@
    @@ -260,6 +261,9 @@ function goBack() { box-sizing: border-box; text-align: center; } +.uppercase { + text-transform: uppercase; +} .button-group { display: flex; gap: 1rem; diff --git a/frontend/vue-app/src/components/child/ChildView.vue b/frontend/vue-app/src/components/child/ChildView.vue index 3dd2213..addcc82 100644 --- a/frontend/vue-app/src/components/child/ChildView.vue +++ b/frontend/vue-app/src/components/child/ChildView.vue @@ -27,6 +27,8 @@ import type { ChoreScheduleModifiedPayload, ChoreTimeExtendedPayload, ChildChoreConfirmationPayload, + ChildOverrideSetPayload, + ChildOverrideDeletedPayload, } from '@/common/models' import { confirmChore, cancelConfirmChore } from '@/common/api' import { @@ -380,6 +382,8 @@ function removeInactivityListeners() { } const childChoreListRef = ref() +const childKindnessListRef = ref() +const childPenaltyListRef = ref() const readyItemId = ref(null) const expiryTimers = ref([]) const lastFetchDate = ref(toLocalISODate(new Date())) @@ -411,6 +415,32 @@ function handleChoreConfirmation(event: Event) { } } +function handleOverrideSet(event: Event) { + const payload = event.payload as ChildOverrideSetPayload + if (child.value && payload.override.child_id === child.value.id) { + if (payload.override.entity_type === 'task') { + childChoreListRef.value?.refresh() + childKindnessListRef.value?.refresh() + childPenaltyListRef.value?.refresh() + } else if (payload.override.entity_type === 'reward') { + childRewardListRef.value?.refresh() + } + } +} + +function handleOverrideDeleted(event: Event) { + const payload = event.payload as ChildOverrideDeletedPayload + if (child.value && payload.child_id === child.value.id) { + if (payload.entity_type === 'task') { + childChoreListRef.value?.refresh() + childKindnessListRef.value?.refresh() + childPenaltyListRef.value?.refresh() + } else if (payload.entity_type === 'reward') { + childRewardListRef.value?.refresh() + } + } +} + function isChoreScheduledToday(item: ChildTask): boolean { if (!item.schedule) return true const today = new Date() @@ -435,6 +465,35 @@ function choreDueLabel(item: ChildTask): string | null { return `Due by ${formatDueTimeLabel(due.hour, due.minute)}` } +// ── Sorting ─────────────────────────────────────────────────────────────────── + +function childChoreSortPriority(item: ChildTask): number { + if (item.pending_status === 'pending') return 2 + if (!item.schedule) return 1 // general chore + return 0 // scheduled for today +} + +function childChoreSort(a: ChildTask, b: ChildTask): number { + const pa = childChoreSortPriority(a) + const pb = childChoreSortPriority(b) + if (pa !== pb) return pa - pb + // Both scheduled: sort by earliest deadline + if (pa === 0) { + const now = new Date() + const dueA = getDueTimeToday(a.schedule!, now) + const dueB = getDueTimeToday(b.schedule!, now) + const minsA = dueA ? dueA.hour * 60 + dueA.minute : Infinity + const minsB = dueB ? dueB.hour * 60 + dueB.minute : Infinity + return minsA - minsB + } + return 0 +} + +function childRewardSort(a: RewardStatus, b: RewardStatus): number { + if (a.redeeming !== b.redeeming) return a.redeeming ? -1 : 1 + return a.points_needed - b.points_needed +} + function clearExpiryTimers() { expiryTimers.value.forEach((t) => clearTimeout(t)) expiryTimers.value = [] @@ -487,6 +546,8 @@ onMounted(async () => { eventBus.on('chore_schedule_modified', handleChoreScheduleModified) eventBus.on('chore_time_extended', handleChoreTimeExtended) eventBus.on('child_chore_confirmation', handleChoreConfirmation) + eventBus.on('child_override_set', handleOverrideSet) + eventBus.on('child_override_deleted', handleOverrideDeleted) document.addEventListener('visibilitychange', onVisibilityChange) if (route.params.id) { const idParam = Array.isArray(route.params.id) ? route.params.id[0] : route.params.id @@ -522,6 +583,8 @@ onUnmounted(() => { eventBus.off('chore_schedule_modified', handleChoreScheduleModified) eventBus.off('chore_time_extended', handleChoreTimeExtended) eventBus.off('child_chore_confirmation', handleChoreConfirmation) + eventBus.off('child_override_set', handleOverrideSet) + eventBus.off('child_override_deleted', handleOverrideDeleted) document.removeEventListener('visibilitychange', onVisibilityChange) clearExpiryTimers() removeInactivityListeners() @@ -555,6 +618,7 @@ onUnmounted(() => { }) " :filter-fn="(item: ChildTask) => item.type === 'chore' && isChoreScheduledToday(item)" + :sort-fn="childChoreSort" >