All checks were successful
Gitea Actions Demo / build-and-push (push) Successful in 25s
- Implemented PendingRewardDialog for handling pending reward requests. - Created RewardConfirmDialog for confirming reward redemption. - Developed TaskConfirmDialog for task confirmation with child name display. test: add unit tests for ChildView and ParentView components - Added comprehensive tests for ChildView including task triggering and SSE event handling. - Implemented tests for ParentView focusing on override modal and SSE event management. test: add ScrollingList component tests - Created tests for ScrollingList to verify item fetching, loading states, and custom item classes. - Included tests for two-step click interactions and edit button display logic. - Moved toward hashed passwords.
520 lines
22 KiB
Markdown
520 lines
22 KiB
Markdown
# Feature: Dynamic Point and Cost Customization
|
|
|
|
## Overview
|
|
|
|
**Goal:** Allow parents to customize the point value of tasks/penalties and the cost of rewards on a per-child basis after assignment.
|
|
|
|
**User Story:**
|
|
As a parent, I want to assign different point values to the same task for different children, so I can tailor rewards to each child's needs and motivations. For example, "Clean Room" might be worth 10 points for one child but 5 points for another.
|
|
|
|
**Process:**
|
|
|
|
1. **Assignment First**: Tasks, penalties, and rewards must be assigned to a child before their points/cost can be customized.
|
|
2. **Edit Button Access**: After the first click on an item in ScrollingList (when it centers), an edit button appears in the corner (34x34px, using `edit.png` icon).
|
|
3. **Modal Customization**: Clicking the edit button opens a modal with a number input field allowing values from **0 to 10000**.
|
|
4. **Default Values**: The field defaults to the last user-set value or the entity's default points/cost if never customized.
|
|
5. **Visual Indicator**: Items with custom values show a ✏️ emoji badge next to the points/cost number.
|
|
6. **Activation Behavior**: The second click on an item activates it (triggers task/reward), not the first click.
|
|
|
|
**Architecture Decisions:**
|
|
|
|
- **Storage**: Use a separate `child_overrides.json` table (not embedded in child model) to store per-child customizations.
|
|
- **Lifecycle**: Overrides reset to default when a child is unassigned from a task/reward. Overrides are deleted when the entity or child is deleted (cascade).
|
|
- **Validation**: Allow 0 points/cost (not minimum 1). Disable save button on invalid input (empty, negative, >10000).
|
|
- **UI Flow**: First click centers item and shows edit button. Second click activates entity. Edit button opens modal for customization.
|
|
|
|
**UI:**
|
|
|
|
- Before first click: [feat-dynamic-points-before.png](feat-dynamic-points-before.png)
|
|
- After first click: [feat-dynamic-points-after.png](feat-dynamic-points-after.png)
|
|
- Edit button icon: `frontend/vue-app/public/edit.png` (34x34px)
|
|
- Button position: Corner of ScrollingList item, not interfering with text
|
|
- Badge: ✏️ emoji displayed next to points/cost number when override exists
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
**No new configuration required.** Range validation (0-10000) is hardcoded per requirements.
|
|
|
|
---
|
|
|
|
## Data Model Changes
|
|
|
|
### New Model: `ChildOverride`
|
|
|
|
**Python** (`backend/models/child_override.py`):
|
|
|
|
Create a dataclass that inherits from `BaseModel` with the following fields:
|
|
|
|
- `child_id` (str): ID of the child this override applies to
|
|
- `entity_id` (str): ID of the task/penalty/reward being customized
|
|
- `entity_type` (Literal['task', 'reward']): Type of entity
|
|
- `custom_value` (int): Custom points or cost value
|
|
|
|
Validation requirements:
|
|
|
|
- `custom_value` must be between 0 and 10000 (inclusive)
|
|
- `entity_type` must be either 'task' or 'reward'
|
|
- Include `__post_init__` method to enforce these validations
|
|
- Include static factory method `create_override()` that accepts the four main fields and returns a new instance
|
|
|
|
**TypeScript** (`frontend/vue-app/src/common/models.ts`):
|
|
|
|
Create an interface with 1:1 parity to the Python model:
|
|
|
|
- Define `EntityType` as a union type: 'task' | 'reward'
|
|
- Include all fields: `id`, `child_id`, `entity_id`, `entity_type`, `custom_value`, `created_at`, `updated_at`
|
|
- All string fields except `custom_value` which is number
|
|
|
|
### Database Table
|
|
|
|
**New Table**: `child_overrides.json`
|
|
|
|
**Indexes**:
|
|
|
|
- `child_id` (for lookup by child)
|
|
- `entity_id` (for lookup by task/reward)
|
|
- Composite `(child_id, entity_id)` (for uniqueness constraint)
|
|
|
|
**Database Helper** (`backend/db/child_overrides.py`):
|
|
|
|
Create database helper functions using TinyDB and the `child_overrides_db` table:
|
|
|
|
- `insert_override(override)`: Insert or update (upsert) based on composite key (child_id, entity_id). Only one override allowed per child-entity pair.
|
|
- `get_override(child_id, entity_id)`: Return Optional[ChildOverride] for a specific child and entity combination
|
|
- `get_overrides_for_child(child_id)`: Return List[ChildOverride] for all overrides belonging to a child
|
|
- `delete_override(child_id, entity_id)`: Delete specific override, return bool indicating success
|
|
- `delete_overrides_for_child(child_id)`: Delete all overrides for a child, return count deleted
|
|
- `delete_overrides_for_entity(entity_id)`: Delete all overrides for an entity, return count deleted
|
|
|
|
All functions should use `from_dict()` and `to_dict()` for model serialization.
|
|
|
|
---
|
|
|
|
## SSE Events
|
|
|
|
### 1. `child_override_set`
|
|
|
|
**Emitted When**: A parent sets or updates a custom value for a task/reward.
|
|
|
|
**Payload** (`backend/events/types/child_override_set.py`):
|
|
|
|
Create a dataclass `ChildOverrideSetPayload` that inherits from `EventPayload` with a single field:
|
|
|
|
- `override` (ChildOverride): The override object that was set
|
|
|
|
**TypeScript** (`frontend/vue-app/src/common/backendEvents.ts`):
|
|
|
|
Create an interface `ChildOverrideSetPayload` with:
|
|
|
|
- `override` (ChildOverride): The override object that was set
|
|
|
|
### 2. `child_override_deleted`
|
|
|
|
**Emitted When**: An override is deleted (manual reset, unassignment, or cascade).
|
|
|
|
**Payload** (`backend/events/types/child_override_deleted.py`):
|
|
|
|
Create a dataclass `ChildOverrideDeletedPayload` that inherits from `EventPayload` with three fields:
|
|
|
|
- `child_id` (str): ID of the child
|
|
- `entity_id` (str): ID of the entity
|
|
- `entity_type` (str): Type of entity ('task' or 'reward')
|
|
|
|
**TypeScript** (`frontend/vue-app/src/common/backendEvents.ts`):
|
|
|
|
Create an interface `ChildOverrideDeletedPayload` with:
|
|
|
|
- `child_id` (string): ID of the child
|
|
- `entity_id` (string): ID of the entity
|
|
- `entity_type` (string): Type of entity
|
|
|
|
---
|
|
|
|
## API Design
|
|
|
|
### 1. **PUT** `/child/<child_id>/override`
|
|
|
|
**Purpose**: Set or update a custom value for a task/reward.
|
|
|
|
**Auth**: User must own the child.
|
|
|
|
**Request Body**:
|
|
|
|
JSON object with three required fields:
|
|
|
|
- `entity_id` (string): UUID of the task or reward
|
|
- `entity_type` (string): Either "task" or "reward"
|
|
- `custom_value` (number): Integer between 0 and 10000
|
|
|
|
**Validation**:
|
|
|
|
- `entity_type` must be "task" or "reward"
|
|
- `custom_value` must be 0-10000
|
|
- Entity must be assigned to child
|
|
- Child must exist and belong to user
|
|
|
|
**Response**:
|
|
|
|
JSON object with a single key `override` containing the complete ChildOverride object with all fields (id, child_id, entity_id, entity_type, custom_value, created_at, updated_at in ISO format).
|
|
|
|
**Errors**:
|
|
|
|
- 404: Child not found or not owned
|
|
- 404: Entity not assigned to child
|
|
- 400: Invalid entity_type
|
|
- 400: custom_value out of range
|
|
|
|
**SSE**: Emits `child_override_set` to user.
|
|
|
|
### 2. **GET** `/child/<child_id>/overrides`
|
|
|
|
**Purpose**: Get all overrides for a child.
|
|
|
|
**Auth**: User must own the child.
|
|
|
|
**Response**:
|
|
|
|
JSON object with a single key `overrides` containing an array of ChildOverride objects. Each object includes all standard fields (id, child_id, entity_id, entity_type, custom_value, created_at, updated_at).
|
|
|
|
**Errors**:
|
|
|
|
- 404: Child not found or not owned
|
|
|
|
### 3. **DELETE** `/child/<child_id>/override/<entity_id>`
|
|
|
|
**Purpose**: Delete an override (reset to default).
|
|
|
|
**Auth**: User must own the child.
|
|
|
|
**Response**:
|
|
|
|
JSON object with `message` field set to "Override deleted".
|
|
|
|
**Errors**:
|
|
|
|
- 404: Child not found or not owned
|
|
- 404: Override not found
|
|
|
|
**SSE**: Emits `child_override_deleted` to user.
|
|
|
|
### Modified Endpoints
|
|
|
|
Update these existing endpoints to include override information:
|
|
|
|
1. **GET** `/child/<child_id>/list-tasks` - Include `custom_value` in task objects if override exists
|
|
2. **GET** `/child/<child_id>/list-rewards` - Include `custom_value` in reward objects if override exists
|
|
3. **POST** `/child/<child_id>/trigger-task` - Use `custom_value` if override exists when awarding points
|
|
4. **POST** `/child/<child_id>/trigger-reward` - Use `custom_value` if override exists when deducting points
|
|
5. **PUT** `/child/<child_id>/set-tasks` - Delete overrides for unassigned tasks
|
|
6. **PUT** `/child/<child_id>/set-rewards` - Delete overrides for unassigned rewards
|
|
|
|
---
|
|
|
|
## Implementation Details
|
|
|
|
### File Structure
|
|
|
|
**Backend**:
|
|
|
|
- `backend/models/child_override.py` - ChildOverride model
|
|
- `backend/db/child_overrides.py` - Database helpers
|
|
- `backend/api/child_override_api.py` - New API endpoints (PUT, GET, DELETE)
|
|
- `backend/events/types/child_override_set.py` - SSE event payload
|
|
- `backend/events/types/child_override_deleted.py` - SSE event payload
|
|
- `backend/events/types/event_types.py` - Add CHILD_OVERRIDE_SET, CHILD_OVERRIDE_DELETED enums
|
|
- `backend/tests/test_child_override_api.py` - Unit tests
|
|
|
|
**Frontend**:
|
|
|
|
- `frontend/vue-app/src/common/models.ts` - Add ChildOverride interface
|
|
- `frontend/vue-app/src/common/api.ts` - Add setChildOverride(), getChildOverrides(), deleteChildOverride()
|
|
- `frontend/vue-app/src/common/backendEvents.ts` - Add event types
|
|
- `frontend/vue-app/src/components/OverrideEditModal.vue` - New modal component
|
|
- `frontend/vue-app/src/components/ScrollingList.vue` - Add edit button and ✏️ badge
|
|
- `frontend/vue-app/components/__tests__/OverrideEditModal.spec.ts` - Component tests
|
|
|
|
### Logging Strategy
|
|
|
|
**Backend**: Log override operations to per-user rotating log files (same pattern as tracking).
|
|
|
|
**Log Messages**:
|
|
|
|
- `Override set: child={child_id}, entity={entity_id}, type={entity_type}, value={custom_value}`
|
|
- `Override deleted: child={child_id}, entity={entity_id}`
|
|
- `Overrides cascade deleted for child: child_id={child_id}, count={count}`
|
|
- `Overrides cascade deleted for entity: entity_id={entity_id}, count={count}`
|
|
|
|
**Frontend**: No additional logging beyond standard error handling.
|
|
|
|
---
|
|
|
|
## Acceptance Criteria (Definition of Done)
|
|
|
|
### Data Model
|
|
|
|
- [x] `ChildOverride` Python dataclass created with validation (0-10000 range, entity_type literal)
|
|
- [x] `ChildOverride` TypeScript interface created (1:1 parity with Python)
|
|
- [x] `child_overrides.json` TinyDB table created in `backend/db/db.py`
|
|
- [x] Database helper functions created (insert, get, delete by child, delete by entity)
|
|
- [x] Composite uniqueness constraint enforced (child_id, entity_id)
|
|
|
|
### Backend Implementation
|
|
|
|
- [x] PUT `/child/<child_id>/override` endpoint created with validation
|
|
- [x] GET `/child/<child_id>/overrides` endpoint created
|
|
- [x] DELETE `/child/<child_id>/override/<entity_id>` endpoint created
|
|
- [x] GET `/child/<child_id>/list-tasks` modified to include `custom_value` when override exists
|
|
- [x] GET `/child/<child_id>/list-rewards` modified to include `custom_value` when override exists
|
|
- [x] POST `/child/<child_id>/trigger-task` modified to use override value
|
|
- [x] POST `/child/<child_id>/trigger-reward` modified to use override value
|
|
- [x] PUT `/child/<child_id>/set-tasks` modified to delete overrides for unassigned tasks
|
|
- [x] PUT `/child/<child_id>/set-rewards` modified to delete overrides for unassigned rewards
|
|
- [x] Cascade delete implemented: deleting child removes all its overrides
|
|
- [x] Cascade delete implemented: deleting task/reward removes all its overrides
|
|
- [x] Authorization checks: user must own child to access overrides
|
|
- [x] Validation: entity must be assigned to child before override can be set
|
|
|
|
### SSE Events
|
|
|
|
- [x] `child_override_set` event type added to event_types.py
|
|
- [x] `child_override_deleted` event type added to event_types.py
|
|
- [x] `ChildOverrideSetPayload` class created (Python)
|
|
- [x] `ChildOverrideDeletedPayload` class created (Python)
|
|
- [x] PUT endpoint emits `child_override_set` event
|
|
- [x] DELETE endpoint emits `child_override_deleted` event
|
|
- [x] Frontend TypeScript interfaces for event payloads created
|
|
|
|
### Frontend Implementation
|
|
|
|
- [x] `OverrideEditModal.vue` component created
|
|
- [x] Modal has number input field with 0-10000 validation
|
|
- [x] Modal disables save button on invalid input (empty, negative, >10000)
|
|
- [x] Modal defaults to current override value or entity default
|
|
- [x] Modal calls PUT `/child/<id>/override` API on save
|
|
- [x] Edit button (34x34px) added to ScrollingList items
|
|
- [x] Edit button only appears after first click (when item is centered)
|
|
- [x] Edit button uses `edit.png` icon from public folder
|
|
- [x] ✏️ emoji badge displayed next to points/cost when override exists
|
|
- [x] Badge only shows for items with active overrides
|
|
- [x] Second click on item activates entity (not first click)
|
|
- [x] SSE listeners registered for `child_override_set` and `child_override_deleted`
|
|
- [x] Real-time UI updates when override events received
|
|
|
|
### Backend Unit Tests
|
|
|
|
#### API Tests (`backend/tests/test_child_override_api.py`)
|
|
|
|
- [x] Test PUT creates new override with valid data
|
|
- [x] Test PUT updates existing override
|
|
- [x] Test PUT returns 400 for custom_value < 0
|
|
- [x] Test PUT returns 400 for custom_value > 10000
|
|
- [x] Test PUT returns 400 for invalid entity_type
|
|
- [ ] Test PUT returns 404 for non-existent child
|
|
- [ ] Test PUT returns 404 for unassigned entity
|
|
- [ ] Test PUT returns 403 for child not owned by user
|
|
- [ ] Test PUT emits child_override_set event
|
|
- [x] Test GET returns all overrides for child
|
|
- [ ] Test GET returns empty array when no overrides
|
|
- [ ] Test GET returns 404 for non-existent child
|
|
- [ ] Test GET returns 403 for child not owned by user
|
|
- [x] Test DELETE removes override
|
|
- [ ] Test DELETE returns 404 when override doesn't exist
|
|
- [ ] Test DELETE returns 404 for non-existent child
|
|
- [ ] Test DELETE returns 403 for child not owned by user
|
|
- [ ] Test DELETE emits child_override_deleted event
|
|
|
|
#### Integration Tests
|
|
|
|
- [ ] Test list-tasks includes custom_value for overridden tasks
|
|
- [ ] Test list-tasks shows default points for non-overridden tasks
|
|
- [ ] Test list-rewards includes custom_value for overridden rewards
|
|
- [ ] Test trigger-task uses custom_value when awarding points
|
|
- [ ] Test trigger-task uses default points when no override
|
|
- [ ] Test trigger-reward uses custom_value when deducting points
|
|
- [ ] Test trigger-reward uses default cost when no override
|
|
- [ ] Test set-tasks deletes overrides for unassigned tasks
|
|
- [ ] Test set-tasks preserves overrides for still-assigned tasks
|
|
- [ ] Test set-rewards deletes overrides for unassigned rewards
|
|
- [ ] Test set-rewards preserves overrides for still-assigned rewards
|
|
|
|
#### Cascade Delete Tests
|
|
|
|
- [x] Test deleting child removes all its overrides
|
|
- [x] Test deleting task removes all overrides for that task
|
|
- [x] Test deleting reward removes all overrides for that reward
|
|
- [x] Test unassigning task from child deletes override
|
|
- [x] Test reassigning task to child resets override (not preserved)
|
|
|
|
#### Edge Cases
|
|
|
|
- [x] Test custom_value = 0 is allowed
|
|
- [x] Test custom_value = 10000 is allowed
|
|
- [ ] Test cannot set override for entity not assigned to child
|
|
- [ ] Test cannot set override for non-existent entity
|
|
- [ ] Test multiple children can have different overrides for same entity
|
|
|
|
### Frontend Unit Tests
|
|
|
|
#### Component Tests (`components/__tests__/OverrideEditModal.spec.ts`)
|
|
|
|
- [x] Test modal renders with default value
|
|
- [x] Test modal renders with existing override value
|
|
- [x] Test save button disabled when input is empty
|
|
- [x] Test save button disabled when value < 0
|
|
- [x] Test save button disabled when value > 10000
|
|
- [x] Test save button enabled when value is 0-10000
|
|
- [x] Test modal calls API with correct parameters on save
|
|
- [x] Test modal emits close event after successful save
|
|
- [x] Test modal shows error message on API failure
|
|
- [x] Test cancel button closes modal without saving
|
|
|
|
#### Component Tests (`components/__tests__/ScrollingList.spec.ts`)
|
|
|
|
- [ ] Test edit button hidden before first click
|
|
- [ ] Test edit button appears after first click (when centered)
|
|
- [ ] Test edit button opens OverrideEditModal
|
|
- [ ] Test ✏️ badge displayed when override exists
|
|
- [ ] Test ✏️ badge hidden when no override exists
|
|
- [ ] Test second click activates entity (not first click)
|
|
- [ ] Test edit button positioned correctly (34x34px, corner)
|
|
- [ ] Test edit button doesn't interfere with text
|
|
|
|
#### Integration Tests
|
|
|
|
- [ ] Test SSE event updates UI when override is set
|
|
- [ ] Test SSE event updates UI when override is deleted
|
|
- [ ] Test override value displayed in task/reward list
|
|
- [ ] Test points calculation uses override when triggering task
|
|
- [ ] Test cost calculation uses override when triggering reward
|
|
|
|
#### Edge Cases
|
|
|
|
- [ ] Test 0 points/cost displays correctly
|
|
- [ ] Test 10000 points/cost displays correctly
|
|
- [ ] Test badge updates immediately after setting override
|
|
- [ ] Test badge disappears immediately after deleting override
|
|
|
|
### Logging & Monitoring
|
|
|
|
- [ ] Override set operations logged to per-user log files
|
|
- [ ] Override delete operations logged
|
|
- [ ] Cascade delete operations logged with count
|
|
- [ ] Log messages include child_id, entity_id, entity_type, custom_value
|
|
|
|
### Documentation
|
|
|
|
- [ ] API endpoints documented in this spec
|
|
- [ ] Data model documented in this spec
|
|
- [ ] SSE events documented in this spec
|
|
- [ ] UI behavior documented in this spec
|
|
- [ ] Edge cases and validation rules documented
|
|
|
|
---
|
|
|
|
## Testing Strategy
|
|
|
|
### Unit Test Files
|
|
|
|
**Backend** (`backend/tests/test_child_override_api.py`):
|
|
|
|
Create six test classes:
|
|
|
|
1. **TestChildOverrideModel**: Test model validation (6 tests)
|
|
- Valid override creation
|
|
- Negative custom_value raises ValueError
|
|
- custom_value > 10000 raises ValueError
|
|
- custom_value = 0 is allowed
|
|
- custom_value = 10000 is allowed
|
|
- Invalid entity_type raises ValueError
|
|
|
|
2. **TestChildOverrideDB**: Test database operations (8 tests)
|
|
- Insert new override
|
|
- Insert updates existing (upsert behavior)
|
|
- Get existing override returns object
|
|
- Get nonexistent override returns None
|
|
- Get all overrides for a child
|
|
- Delete specific override
|
|
- Delete all overrides for a child (returns count)
|
|
- Delete all overrides for an entity (returns count)
|
|
|
|
3. **TestChildOverrideAPI**: Test all three API endpoints (18 tests)
|
|
- PUT creates new override
|
|
- PUT updates existing override
|
|
- PUT returns 400 for negative value
|
|
- PUT returns 400 for value > 10000
|
|
- PUT returns 400 for invalid entity_type
|
|
- PUT returns 404 for nonexistent child
|
|
- PUT returns 404 for unassigned entity
|
|
- PUT returns 403 for child not owned by user
|
|
- PUT emits child_override_set event
|
|
- GET returns all overrides for child
|
|
- GET returns empty array when no overrides
|
|
- GET returns 404 for nonexistent child
|
|
- GET returns 403 for child not owned
|
|
- DELETE removes override successfully
|
|
- DELETE returns 404 when override doesn't exist
|
|
- DELETE returns 404 for nonexistent child
|
|
- DELETE returns 403 for child not owned
|
|
- DELETE emits child_override_deleted event
|
|
|
|
4. **TestIntegration**: Test override integration with existing endpoints (11 tests)
|
|
- list-tasks includes custom_value for overridden tasks
|
|
- list-tasks shows default points for non-overridden tasks
|
|
- list-rewards includes custom_value for overridden rewards
|
|
- trigger-task uses custom_value when awarding points
|
|
- trigger-task uses default points when no override
|
|
- trigger-reward uses custom_value when deducting points
|
|
- trigger-reward uses default cost when no override
|
|
- set-tasks deletes overrides for unassigned tasks
|
|
- set-tasks preserves overrides for still-assigned tasks
|
|
- set-rewards deletes overrides for unassigned rewards
|
|
- set-rewards preserves overrides for still-assigned rewards
|
|
|
|
5. **TestCascadeDelete**: Test cascade deletion behavior (5 tests)
|
|
- Deleting child removes all its overrides
|
|
- Deleting task removes all overrides for that task
|
|
- Deleting reward removes all overrides for that reward
|
|
- Unassigning task deletes override
|
|
- Reassigning task resets override (not preserved)
|
|
|
|
6. **TestEdgeCases**: Test boundary conditions (5 tests)
|
|
- custom_value = 0 is allowed
|
|
- custom_value = 10000 is allowed
|
|
- Cannot set override for unassigned entity
|
|
- Cannot set override for nonexistent entity
|
|
- Multiple children can have different overrides for same entity
|
|
|
|
### Test Fixtures
|
|
|
|
Create pytest fixtures for common test scenarios:
|
|
|
|
- `child_with_task`: Uses existing `child` and `task` fixtures, calls set-tasks endpoint to assign task to child, asserts 200 response, returns child dict
|
|
- `child_with_task_override`: Builds on `child_with_task`, calls PUT override endpoint to set custom_value=15 for the task, asserts 200 response, returns child dict
|
|
- Similar fixtures for rewards: `child_with_reward`, `child_with_reward_override`
|
|
- `child_with_overrides`: Child with multiple overrides for testing bulk operations
|
|
|
|
### Assertions
|
|
|
|
Test assertions should verify three main areas:
|
|
|
|
1. **API Response Correctness**: Check status code (200, 400, 403, 404), verify returned override object has correct values for all fields (custom_value, child_id, entity_id, etc.)
|
|
|
|
2. **SSE Event Emission**: Use mock_sse fixture to assert `send_event_for_current_user` was called exactly once with the correct EventType (CHILD_OVERRIDE_SET or CHILD_OVERRIDE_DELETED)
|
|
|
|
3. **Points Calculation**: After triggering tasks/rewards, verify the child's points reflect the custom_value (not the default). For example, if default is 10 but override is 15, child.points should increase by 15.
|
|
|
|
---
|
|
|
|
## Future Considerations
|
|
|
|
1. **Bulk Override Management**: Add endpoint to set/get/delete multiple overrides at once for performance.
|
|
2. **Override History**: Track changes to override values over time for analytics.
|
|
3. **Copy Overrides**: Allow copying overrides from one child to another.
|
|
4. **Override Templates**: Save common override patterns as reusable templates.
|
|
5. **Percentage-Based Overrides**: Allow setting overrides as percentage of default (e.g., "150% of default").
|
|
6. **Override Expiration**: Add optional expiration dates for temporary adjustments.
|
|
7. **Undo Override**: Add "Restore Default" button in UI that deletes override with one click.
|
|
8. **Admin Dashboard**: Show overview of all overrides across all children for analysis.
|