# Feature: Chore Completion Confirmation + Task Refactor ## Overview **Goal:** Refactor the "task" concept into three distinct entity types — **Chore**, **Kindness**, and **Penalty** — and implement a chore completion confirmation flow where children can confirm chores and parents approve them. **User Stories:** - As a **child**, I can click on a chore and confirm that I completed it. I see a **PENDING** banner (yellow) until a parent confirms. - As a **child**, I can click an already PENDING chore and cancel my confirmation. - As a **child**, I see a **COMPLETED** banner (green) on a chore that a parent has approved. That chore is disabled for the rest of the day. - As a **parent**, I see pending chore confirmations in the **Notifications** tab alongside pending reward requests. - As a **parent**, I can click a PENDING chore to **approve** it (awarding points) or **reject** it (resetting to available). - As a **parent**, I can click a non-pending/non-completed chore and award points directly (current behavior). The child view then shows the COMPLETED banner. - As a **parent**, I can **reset** a completed chore from the kebab menu so the child can confirm it again (points are kept). - As an **admin**, I can view full tracking history in the database/logs for all confirmation lifecycle events. **Rules:** .github/copilot-instructions.md --- ## Design Decisions (Resolved) ### Task Refactor → Chore / Kindness / Penalty **Decision: Full refactor.** | Old Concept | New Concept | Behavior | | -------------------------------------------------------------- | ------------ | ------------------------------------------ | | Task with `is_good=true` (schedulable) | **Chore** | Scheduled, expirable, confirmable by child | | Task with `is_good=true` (ad-hoc, e.g. "Child was good today") | **Kindness** | Parent-only award, not confirmable | | Task with `is_good=false` | **Penalty** | Parent-only deduction | - The `is_good` field is **removed**. Entity type itself determines point direction. - The `Task` model is retained in the backend but gains a `type` field: `'chore' | 'kindness' | 'penalty'`. - `task_api.py` is split into `chore_api.py`, `kindness_api.py`, `penalty_api.py`. - Existing `is_good=true` tasks are auto-classified as **chore**; `is_good=false` as **penalty**. - Kindness items must be manually created post-migration (acceptable). ### Merged Pending Table **Decision: Single `PendingConfirmation` model replaces `PendingReward`.** - Both pending reward requests and pending chore confirmations live in one `pending_confirmations` table, differentiated by `entity_type`. - The `/pending-rewards` endpoint is replaced by `/pending-confirmations`. - `pending_rewards.json` DB file is replaced by `pending_confirmations.json`. ### "Completed Today" Tracking **Decision: `PendingConfirmation` record with `status='approved'` + `approved_at` timestamp.** - An approved `PendingConfirmation` record persists (DB-backed, survives restart) and serves as the "completed today" marker. - The frontend checks if `approved_at` is today to determine the COMPLETED state. - On **reset**, the record is deleted (status returns to available). - **Multi-completion history** is preserved via `TrackingEvent` — each confirm/approve/reset cycle generates tracking entries. Query `TrackingEvent` by `child_id + entity_id + date + action='approved'` to count completions per day. ### Navigation **Decision: Sub-nav under "Tasks" tab.** - Top-level nav stays 4 items: **Children | Tasks | Rewards | Notifications** - The "Tasks" tab opens a view with 3 sub-tabs: **Chores | Kindness | Penalties** - Each sub-tab has its own list view, edit view, and assign view. - No mobile layout changes needed to the top bar. ### Chore Confirmation Scoping - Each `PendingConfirmation` is scoped to a **single child**. If a chore is assigned to multiple children, each confirms independently. - Expired chores **cannot** be confirmed. - A chore that is already PENDING or COMPLETED today **cannot** be confirmed again (unless reset by parent). --- ## Data Model Changes ### Backend Models #### `Task` Model (Modified) File: `backend/models/task.py` ```python @dataclass class Task(BaseModel): name: str points: int type: Literal['chore', 'kindness', 'penalty'] # replaces is_good image_id: str | None = None user_id: str | None = None ``` - `is_good: bool` → **removed** - `type: Literal['chore', 'kindness', 'penalty']` → **added** - Migration: `is_good=True` → `type='chore'`, `is_good=False` → `type='penalty'` #### `PendingConfirmation` Model (New — replaces `PendingReward`) File: `backend/models/pending_confirmation.py` ```python @dataclass class PendingConfirmation(BaseModel): child_id: str entity_id: str # task_id or reward_id entity_type: str # 'chore' | 'reward' user_id: str status: str = "pending" # 'pending' | 'approved' | 'rejected' approved_at: str | None = None # ISO 8601 UTC timestamp, set on approval ``` - Replaces `PendingReward` (which had `child_id`, `reward_id`, `user_id`, `status`) - `entity_id` generalizes `reward_id` to work for both chores and rewards - `entity_type` differentiates between chore confirmations and reward requests - `approved_at` enables "completed today" checks #### `TrackingEvent` Model (Extended Types) File: `backend/models/tracking_event.py` ```python EntityType = Literal['task', 'reward', 'penalty', 'chore', 'kindness'] ActionType = Literal['activated', 'requested', 'redeemed', 'cancelled', 'confirmed', 'approved', 'rejected', 'reset'] ``` New actions: - `confirmed` — child marks chore as done - `approved` — parent approves chore completion (points awarded) - `rejected` — parent rejects chore completion (no point change) - `reset` — parent resets a completed chore (no point change) #### `ChildOverride` Model (Extended Types) File: `backend/models/child_override.py` ```python entity_type: Literal['task', 'reward'] # → Literal['chore', 'kindness', 'penalty', 'reward'] ``` #### `ChildTask` DTO (Modified) File: `backend/api/child_tasks.py` ```python class ChildTask: def __init__(self, name, type, points, image_id, id): self.id = id self.name = name self.type = type # replaces is_good self.points = points self.image_id = image_id ``` #### SSE Event Types (New) File: `backend/events/types/event_types.py` ```python class EventType(Enum): # ... existing ... CHILD_CHORE_CONFIRMATION = "child_chore_confirmation" ``` New payload class — File: `backend/events/types/child_chore_confirmation.py` ```python class ChildChoreConfirmation(Payload): # child_id: str # task_id: str # operation: 'CONFIRMED' | 'APPROVED' | 'REJECTED' | 'CANCELLED' | 'RESET' ``` #### Error Codes (New) File: `backend/api/error_codes.py` ```python class ErrorCodes: # ... existing ... CHORE_EXPIRED = "CHORE_EXPIRED" CHORE_ALREADY_PENDING = "CHORE_ALREADY_PENDING" CHORE_ALREADY_COMPLETED = "CHORE_ALREADY_COMPLETED" PENDING_NOT_FOUND = "PENDING_NOT_FOUND" INSUFFICIENT_POINTS = "INSUFFICIENT_POINTS" ``` ### Frontend Models File: `frontend/vue-app/src/common/models.ts` ```typescript // Task — modified export interface Task { id: string; name: string; points: number; type: "chore" | "kindness" | "penalty"; // replaces is_good image_id: string | null; image_url?: string | null; } // ChildTask — modified export interface ChildTask { id: string; name: string; type: "chore" | "kindness" | "penalty"; // replaces is_good points: number; image_id: string | null; image_url?: string | null; custom_value?: number | null; schedule?: ChoreSchedule | null; extension_date?: string | null; } // PendingConfirmation — new (replaces PendingReward) export interface PendingConfirmation { id: string; child_id: string; child_name: string; child_image_id: string | null; child_image_url?: string | null; entity_id: string; entity_type: "chore" | "reward"; entity_name: string; entity_image_id: string | null; entity_image_url?: string | null; status: "pending" | "approved" | "rejected"; approved_at: string | null; } // EntityType — extended export type EntityType = "chore" | "kindness" | "penalty" | "reward"; // ActionType — extended export type ActionType = | "activated" | "requested" | "redeemed" | "cancelled" | "confirmed" | "approved" | "rejected" | "reset"; // SSE event payload — new export interface ChildChoreConfirmationPayload { child_id: string; task_id: string; operation: "CONFIRMED" | "APPROVED" | "REJECTED" | "CANCELLED" | "RESET"; } ``` --- ## Backend Implementation ### API Changes #### New Files | File | Description | | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `backend/api/chore_api.py` | CRUD for chores (type='chore'). Routes: `/chore/add`, `/chore/`, `/chore//edit`, `/chore/list`, `DELETE /chore/` | | `backend/api/kindness_api.py` | CRUD for kindness acts (type='kindness'). Routes: `/kindness/add`, `/kindness/`, `/kindness//edit`, `/kindness/list`, `DELETE /kindness/` | | `backend/api/penalty_api.py` | CRUD for penalties (type='penalty'). Routes: `/penalty/add`, `/penalty/`, `/penalty//edit`, `/penalty/list`, `DELETE /penalty/` | | `backend/models/pending_confirmation.py` | `PendingConfirmation` dataclass | | `backend/events/types/child_chore_confirmation.py` | SSE payload class | | `backend/api/pending_confirmation.py` | Response DTO for hydrated pending confirmation | #### Modified Files | File | Changes | | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `backend/models/task.py` | `is_good` → `type` field | | `backend/models/tracking_event.py` | Extend `EntityType` and `ActionType` literals | | `backend/models/child_override.py` | Extend `entity_type` literal | | `backend/api/child_tasks.py` | `is_good` → `type` field in DTO | | `backend/api/child_api.py` | Add chore confirmation endpoints, replace `/pending-rewards` with `/pending-confirmations`, update trigger-task to set COMPLETED state, update all `is_good` references to `type` | | `backend/api/task_api.py` | Deprecate/remove — logic moves to entity-specific API files | | `backend/api/error_codes.py` | Add new error codes | | `backend/events/types/event_types.py` | Add `CHILD_CHORE_CONFIRMATION` | | `backend/db/db.py` | Add `pending_confirmations_db`, remove `pending_reward_db` | | `backend/main.py` | Register new blueprints, remove `task_api` blueprint | #### New Endpoints (on `child_api.py`) | Method | Route | Actor | Description | | ------ | ---------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `POST` | `/child//confirm-chore` | Child | Body: `{ task_id }`. Creates `PendingConfirmation(entity_type='chore', status='pending')`. Validates: chore assigned, not expired, not already pending/completed today. Tracking: `action='confirmed'`, `delta=0`. SSE: `CHILD_CHORE_CONFIRMATION` (CONFIRMED). | | `POST` | `/child//cancel-confirm-chore` | Child | Body: `{ task_id }`. Deletes the pending confirmation. Tracking: `action='cancelled'`, `delta=0`. SSE: `CHILD_CHORE_CONFIRMATION` (CANCELLED). | | `POST` | `/child//approve-chore` | Parent | Body: `{ task_id }`. Sets `status='approved'`, `approved_at=now()`. Awards points (respects overrides). Tracking: `action='approved'`, `delta=+points`. SSE: `CHILD_CHORE_CONFIRMATION` (APPROVED) + `CHILD_TASK_TRIGGERED`. | | `POST` | `/child//reject-chore` | Parent | Body: `{ task_id }`. Deletes the pending confirmation. Tracking: `action='rejected'`, `delta=0`. SSE: `CHILD_CHORE_CONFIRMATION` (REJECTED). | | `POST` | `/child//reset-chore` | Parent | Body: `{ task_id }`. Deletes the approved confirmation record. Tracking: `action='reset'`, `delta=0`. SSE: `CHILD_CHORE_CONFIRMATION` (RESET). | | `GET` | `/pending-confirmations` | Parent | Returns all pending `PendingConfirmation` records for the user, hydrated with child/entity names and images. Replaces `/pending-rewards`. | #### Modified Endpoints | Endpoint | Change | | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | `POST /child//trigger-task` | When a parent triggers a chore directly (no pending), create an approved `PendingConfirmation` so child view shows COMPLETED. Update entity_type references from `'task'` to the actual type. | | `POST /child//request-reward` | Create `PendingConfirmation(entity_type='reward')` instead of `PendingReward`. | | `POST /child//cancel-request-reward` | Query `PendingConfirmation` by `entity_type='reward'` instead of `PendingReward`. | | `POST /child//trigger-reward` | Query/remove `PendingConfirmation` by `entity_type='reward'` instead of `PendingReward`. | | `GET /child//list-tasks` | Add `pending_status` and `approved_at` fields to each chore in the response (from `PendingConfirmation` lookup). | | `PUT /child//set-tasks` | Accept `type` parameter instead of `type: 'good' | 'bad'`. | #### Database Migration A one-time migration script (`backend/scripts/migrate_tasks_to_types.py`): 1. For each record in `tasks.json`: if `is_good=True` → set `type='chore'`, if `is_good=False` → set `type='penalty'`. Remove `is_good` field. 2. For each record in `pending_rewards.json`: convert to `PendingConfirmation` format with `entity_type='reward'`, `entity_id=reward_id`. Write to `pending_confirmations.json`. 3. For each record in `tracking_events.json`: update `entity_type='task'` → `'chore'` or `'penalty'` based on the referenced task's old `is_good` value. 4. For each record in `child_overrides.json`: update `entity_type='task'` → `'chore'` or `'penalty'` based on the referenced task's old `is_good` value. --- ## Backend Tests ### Test File: `backend/tests/test_chore_api.py` (New) - [ ] `test_add_chore` — PUT `/chore/add` with `name`, `points` → 201, type auto-set to `'chore'` - [ ] `test_add_chore_missing_fields` — 400 with `MISSING_FIELDS` - [ ] `test_list_chores` — GET `/chore/list` returns only `type='chore'` tasks - [ ] `test_get_chore` — GET `/chore/` → 200 - [ ] `test_get_chore_not_found` — 404 - [ ] `test_edit_chore` — PUT `/chore//edit` → 200 - [ ] `test_edit_system_chore_clones_to_user` — editing a `user_id=None` chore creates a user copy - [ ] `test_delete_chore` — DELETE `/chore/` → 200, removed from children's task lists - [ ] `test_delete_chore_not_found` — 404 - [ ] `test_delete_chore_removes_from_assigned_children` — cascade cleanup ### Test File: `backend/tests/test_kindness_api.py` (New) - [ ] `test_add_kindness` — PUT `/kindness/add` → 201, type auto-set to `'kindness'` - [ ] `test_list_kindness` — returns only `type='kindness'` tasks - [ ] `test_edit_kindness` — PUT `/kindness//edit` → 200 - [ ] `test_delete_kindness` — cascade removal ### Test File: `backend/tests/test_penalty_api.py` (New) - [ ] `test_add_penalty` — PUT `/penalty/add` → 201, type auto-set to `'penalty'` - [ ] `test_list_penalties` — returns only `type='penalty'` tasks - [ ] `test_edit_penalty` — PUT `/penalty//edit` → 200 - [ ] `test_delete_penalty` — cascade removal ### Test File: `backend/tests/test_chore_confirmation.py` (New) #### Child Confirm Flow - [ ] `test_child_confirm_chore_success` — POST `/child//confirm-chore` with `{ task_id }` → 200, `PendingConfirmation` record created with `status='pending'`, `entity_type='chore'` - [ ] `test_child_confirm_chore_not_assigned` — 400 `ENTITY_NOT_ASSIGNED` when chore is not in child's task list - [ ] `test_child_confirm_chore_not_found` — 404 `TASK_NOT_FOUND` when task_id doesn't exist - [ ] `test_child_confirm_chore_child_not_found` — 404 `CHILD_NOT_FOUND` - [ ] `test_child_confirm_chore_already_pending` — 400 `CHORE_ALREADY_PENDING` when a pending confirmation already exists - [ ] `test_child_confirm_chore_already_completed_today` — 400 `CHORE_ALREADY_COMPLETED` when an approved confirmation exists for today - [ ] `test_child_confirm_chore_expired` — 400 `CHORE_EXPIRED` when chore is past its deadline - [ ] `test_child_confirm_chore_creates_tracking_event` — TrackingEvent with `action='confirmed'`, `delta=0`, `entity_type='chore'` - [ ] `test_child_confirm_chore_wrong_type` — 400 when task is kindness or penalty (not confirmable) #### Child Cancel Flow - [ ] `test_child_cancel_confirm_success` — POST `/child//cancel-confirm-chore` → 200, pending record deleted - [ ] `test_child_cancel_confirm_not_pending` — 400 `PENDING_NOT_FOUND` - [ ] `test_child_cancel_confirm_creates_tracking_event` — TrackingEvent with `action='cancelled'`, `delta=0` #### Parent Approve Flow - [ ] `test_parent_approve_chore_success` — POST `/child//approve-chore` → 200, points increased, `status='approved'`, `approved_at` set - [ ] `test_parent_approve_chore_with_override` — uses `custom_value` from override instead of base points - [ ] `test_parent_approve_chore_not_pending` — 400 `PENDING_NOT_FOUND` - [ ] `test_parent_approve_chore_creates_tracking_event` — TrackingEvent with `action='approved'`, `delta=+points` - [ ] `test_parent_approve_chore_points_correct` — `points_before` + task points == `points_after` on child #### Parent Reject Flow - [ ] `test_parent_reject_chore_success` — POST `/child//reject-chore` → 200, pending record deleted, points unchanged - [ ] `test_parent_reject_chore_not_pending` — 400 `PENDING_NOT_FOUND` - [ ] `test_parent_reject_chore_creates_tracking_event` — TrackingEvent with `action='rejected'`, `delta=0` #### Parent Reset Flow - [ ] `test_parent_reset_chore_success` — POST `/child//reset-chore` → 200, approved record deleted, points unchanged - [ ] `test_parent_reset_chore_not_completed` — 400 when no approved record exists - [ ] `test_parent_reset_chore_creates_tracking_event` — TrackingEvent with `action='reset'`, `delta=0` - [ ] `test_parent_reset_then_child_confirm_again` — full cycle: confirm → approve → reset → confirm → approve (two approvals tracked) #### Parent Direct Trigger - [ ] `test_parent_trigger_chore_directly_creates_approved_confirmation` — POST `/child//trigger-task` with a chore → creates approved PendingConfirmation so child view shows COMPLETED #### Pending Confirmations List - [ ] `test_list_pending_confirmations_returns_chores_and_rewards` — GET `/pending-confirmations` returns both types - [ ] `test_list_pending_confirmations_empty` — returns empty list when none exist - [ ] `test_list_pending_confirmations_hydrates_names_and_images` — response includes `child_name`, `entity_name`, image IDs - [ ] `test_list_pending_confirmations_excludes_approved` — only pending status returned - [ ] `test_list_pending_confirmations_filters_by_user` — only returns confirmations for the authenticated user's children ### Test File: `backend/tests/test_task_api.py` (Modified) - [ ] Update all existing tests that use `is_good` to use `type` instead - [ ] `test_add_task` → split into `test_add_chore`, `test_add_kindness`, `test_add_penalty` (or remove if fully migrated to entity-specific APIs) - [ ] `test_list_tasks_sorted` → update sort expectations for `type` field ### Test File: `backend/tests/test_child_api.py` (Modified) - [ ] Update tests referencing `is_good` to use `type` - [ ] Update `set-tasks` tests for new `type` parameter values (`'chore'`, `'kindness'`, `'penalty'`) - [ ] Update `list-tasks` response assertions to check for `pending_status` and `approved_at` fields on chores - [ ] Update `trigger-task` tests to verify `PendingConfirmation` creation for chores - [ ] Update `request-reward` / `cancel-request-reward` / `trigger-reward` tests to use `PendingConfirmation` model - [ ] Replace `pending-rewards` endpoint tests with `pending-confirmations` --- ## Frontend Implementation ### New Files | File | Description | | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | | `src/components/task/ChoreView.vue` | Admin list of chores (type='chore'). Same pattern as current `TaskView.vue` with blue theme. | | `src/components/task/KindnessView.vue` | Admin list of kindness acts (type='kindness'). Yellow theme. | | `src/components/task/PenaltyView.vue` | Admin list of penalties (type='penalty'). Red theme. | | `src/components/task/ChoreEditView.vue` | Create/edit chore form. Fields: name, points, image. No `is_good` toggle. | | `src/components/task/KindnessEditView.vue` | Create/edit kindness form. Fields: name, points, image. | | `src/components/task/PenaltyEditView.vue` | Create/edit penalty form. Fields: name, points, image. | | `src/components/task/TaskSubNav.vue` | Sub-nav component with Chores / Kindness / Penalties tabs. Renders as a tab bar within the Tasks view area. | | `src/components/child/ChoreAssignView.vue` | Assign chores to child (replaces `TaskAssignView` with `type='good'`). | | `src/components/child/KindnessAssignView.vue` | Assign kindness acts to child. | | `src/components/child/PenaltyAssignView.vue` | Assign penalties to child (replaces `TaskAssignView` with `type='bad'`). | | `src/components/child/ChoreConfirmDialog.vue` | Modal dialog for child to confirm chore completion. "Did you finish [chore name]?" with Confirm / Cancel buttons. | | `src/components/child/ChoreApproveDialog.vue` | Modal dialog for parent to approve/reject pending chore. Shows chore name, child name, points. Approve / Reject buttons. | ### Modified Files | File | Changes | | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `src/common/models.ts` | Replace `Task.is_good` with `Task.type`, add `PendingConfirmation` interface, extend `EntityType`/`ActionType`, add `ChildChoreConfirmationPayload`, replace `PendingReward` with `PendingConfirmation`. Add `pending_status` and `approved_at` to `ChildTask`. | | `src/common/backendEvents.ts` | Add `child_chore_confirmation` event listener pattern. | | `src/common/api.ts` | Add `confirmChore()`, `cancelConfirmChore()`, `approveChore()`, `rejectChore()`, `resetChore()`, `fetchPendingConfirmations()`. Remove `fetchPendingRewards()`. | | `src/components/child/ChildView.vue` | Add chore tap handler → show `ChoreConfirmDialog`. Add PENDING (yellow) / COMPLETED (green) banner rendering. Handle cancel-confirm on PENDING tap. Filter kindness acts into new scrolling row. Listen for `child_chore_confirmation` SSE events. | | `src/components/child/ParentView.vue` | Add PENDING/COMPLETED banners on chores. Handle approve/reject on PENDING chore tap. Add "Reset" to kebab menu for completed chores. Add "Assign Kindness" button. Update `trigger-task` to create approved confirmation. Replace `is_good` filters with `type` checks. Listen for `child_chore_confirmation` SSE events. | | `src/components/notification/NotificationView.vue` | Fetch from `/api/pending-confirmations` instead of `/api/pending-rewards`. Render both pending chores and pending rewards with differentiation (icon/label). Listen for `child_chore_confirmation` events in addition to existing `child_reward_request`. | | `src/layout/ParentLayout.vue` | "Tasks" nav icon remains, routes to a view housing `TaskSubNav` with sub-tabs. | | `src/components/task/TaskEditView.vue` | Remove or repurpose. Logic moves to entity-specific edit views (no `is_good` toggle). | | `src/components/task/TaskView.vue` | Remove or repurpose into the sub-nav container view. | | `src/components/child/TaskAssignView.vue` | Remove. Replaced by `ChoreAssignView`, `KindnessAssignView`, `PenaltyAssignView`. | | Router config | Add routes for new views. Update existing task routes to chore/kindness/penalty. | ### Files to Remove | File | Reason | | -------------------------------------- | ----------------------------------------- | | `backend/models/pending_reward.py` | Replaced by `pending_confirmation.py` | | `backend/api/pending_reward.py` | Replaced by `pending_confirmation.py` DTO | | `backend/data/db/pending_rewards.json` | Replaced by `pending_confirmations.json` | ### ChildView Chore Tap Flow ``` Child taps chore card ├─ Chore expired? → No action (grayed out, "TOO LATE" stamp) ├─ Chore COMPLETED today? → No action (grayed out, "COMPLETED" stamp) ├─ Chore PENDING? → Show ModalDialog "Cancel confirmation?" │ └─ Confirm → POST /child//cancel-confirm-chore └─ Chore available? → Show ChoreConfirmDialog "Did you finish [name]?" └─ Confirm → POST /child//confirm-chore ``` ### ParentView Chore Tap Flow ``` Parent taps chore card ├─ Chore PENDING? → Show ChoreApproveDialog │ ├─ Approve → POST /child//approve-chore (awards points) │ └─ Reject → POST /child//reject-chore (resets to available) ├─ Chore COMPLETED today? → No tap action. Kebab menu has "Reset" │ └─ Reset → POST /child//reset-chore └─ Chore available? → Show TaskConfirmDialog (current behavior) └─ Confirm → POST /child//trigger-task (sets COMPLETED) ``` ### Banner Styling | State | Banner Text | Text Color | Background | CSS Variable Suggestion | | --------- | ----------- | -------------------------- | ----------------------- | ----------------------- | | Pending | `PENDING` | Yellow (`--color-warning`) | Dark semi-transparent | `--banner-bg-pending` | | Completed | `COMPLETED` | Green (`--color-success`) | Dark semi-transparent | `--banner-bg-completed` | | Expired | `TOO LATE` | Red (existing) | Gray overlay (existing) | (existing styles) | --- ## Frontend Tests ### Test File: `components/__tests__/ChoreConfirmDialog.test.ts` (New) - [ ] `renders chore name in dialog` - [ ] `emits confirm event on confirm button click` - [ ] `emits cancel event on cancel button click` ### Test File: `components/__tests__/ChoreApproveDialog.test.ts` (New) - [ ] `renders chore name and points in dialog` - [ ] `emits approve event on approve button click` - [ ] `emits reject event on reject button click` ### Test File: `components/__tests__/TaskSubNav.test.ts` (New) - [ ] `renders three sub-tabs: Chores, Kindness, Penalties` - [ ] `highlights active tab based on route` - [ ] `navigates on tab click` ### Test File: `components/__tests__/ChoreView.test.ts` (New) - [ ] `fetches and renders chore list` - [ ] `navigates to edit on item click` - [ ] `shows delete confirmation modal` - [ ] `refreshes on task_modified SSE event` ### Test File: `components/__tests__/NotificationView.test.ts` (Modified) - [ ] `fetches from /api/pending-confirmations` - [ ] `renders both pending chores and pending rewards` - [ ] `differentiates chore vs reward with label/icon` - [ ] `refreshes on child_chore_confirmation SSE event` - [ ] `refreshes on child_reward_request SSE event` ### Test File: `components/__tests__/ChildView.test.ts` (Modified / New) - [ ] `shows PENDING banner on chore with pending confirmation` - [ ] `shows COMPLETED banner on chore completed today` - [ ] `opens ChoreConfirmDialog on available chore tap` - [ ] `opens cancel dialog on PENDING chore tap` - [ ] `does not allow tap on expired chore` - [ ] `does not allow tap on COMPLETED chore` - [ ] `renders kindness scrolling row` - [ ] `refreshes on child_chore_confirmation SSE event` ### Test File: `components/__tests__/ParentView.test.ts` (Modified / New) - [ ] `shows PENDING banner on chore with pending confirmation` - [ ] `shows COMPLETED banner on approved chore` - [ ] `opens ChoreApproveDialog on PENDING chore tap` - [ ] `opens TaskConfirmDialog on available chore tap` - [ ] `shows Reset in kebab menu for completed chore` - [ ] `renders kindness scrolling row` - [ ] `shows Assign Kindness button` --- ## Future Considerations - **Recurring chore auto-reset**: Automatically clear completed status on schedule rollover (e.g., daily chores reset at midnight). - **Chore streaks**: Track consecutive days a child completes a chore using `TrackingEvent` history. - **Multi-completion analytics dashboard**: Query `TrackingEvent` to show completion counts per chore per day/week. - **Partial credit**: Allow parents to award fewer points than the chore's value when approving. - **Chore delegation**: Allow one child to reassign a chore to a sibling. - **Photo proof**: Child attaches a photo when confirming a chore. - **Kindness auto-classification**: Suggested classification when creating new items based on name patterns. --- ## Acceptance Criteria (Definition of Done) ### Backend - [ ] `Task` model uses `type: 'chore' | 'kindness' | 'penalty'` — `is_good` removed - [ ] `PendingConfirmation` model created, `PendingReward` model removed - [ ] `pending_confirmations_db` created in `db.py`, `pending_reward_db` removed - [ ] Migration script converts existing tasks, pending rewards, tracking events, and overrides - [ ] `chore_api.py`, `kindness_api.py`, `penalty_api.py` created with CRUD endpoints - [ ] `task_api.py` removed or deprecated - [ ] Child chore confirmation endpoints: `confirm-chore`, `cancel-confirm-chore`, `approve-chore`, `reject-chore`, `reset-chore` - [ ] `GET /pending-confirmations` returns hydrated pending chores and rewards - [ ] `trigger-task` creates approved `PendingConfirmation` when parent triggers a chore directly - [ ] Reward request/cancel/trigger endpoints migrated to `PendingConfirmation` - [ ] `list-tasks` response includes `pending_status` and `approved_at` for chores - [ ] `TrackingEvent` created for every mutation: confirmed, cancelled, approved, rejected, reset - [ ] Tracking events logged to rotating file logger - [ ] SSE event `CHILD_CHORE_CONFIRMATION` sent for every confirmation lifecycle event - [ ] All new error codes defined and returned with proper HTTP status codes - [ ] All existing tests updated for `type` field (no `is_good` references) - [ ] All new backend tests pass ### Frontend - [ ] `Task` and `ChildTask` interfaces use `type` instead of `is_good` - [ ] `PendingConfirmation` interface replaces `PendingReward` - [ ] Sub-nav under "Tasks" with Chores / Kindness / Penalties tabs - [ ] `ChoreView`, `KindnessView`, `PenaltyView` list views created - [ ] `ChoreEditView`, `KindnessEditView`, `PenaltyEditView` edit/create views created - [ ] `ChoreAssignView`, `KindnessAssignView`, `PenaltyAssignView` assign views created - [ ] `TaskView`, `TaskEditView`, `TaskAssignView` removed or repurposed - [ ] `ChoreConfirmDialog` — child confirmation modal - [ ] `ChoreApproveDialog` — parent approve/reject modal - [ ] `ChildView` — chore tap opens confirm dialog, cancel dialog for pending, banners render correctly - [ ] `ChildView` — expired and completed chores are non-interactive - [ ] `ChildView` — kindness scrolling row added - [ ] `ParentView` — pending chore tap opens approve/reject dialog - [ ] `ParentView` — available chore tap uses existing trigger flow + creates completion record - [ ] `ParentView` — kebab menu "Reset" option for completed chores - [ ] `ParentView` — "Assign Kindness" button added - [ ] `NotificationView` — fetches from `/pending-confirmations`, renders both types - [ ] SSE listeners for `child_chore_confirmation` in all relevant components - [ ] Banner styles: yellow PENDING, green COMPLETED (using CSS variables) - [ ] All `is_good` references removed from frontend code - [ ] All frontend tests pass - [ ] Router updated with new routes