feat: add chore, kindness, and penalty management components
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 2m34s
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 2m34s
- Implemented ChoreAssignView for assigning chores to children. - Created ChoreConfirmDialog for confirming chore completion. - Developed KindnessAssignView for assigning kindness acts. - Added PenaltyAssignView for assigning penalties. - Introduced ChoreEditView and ChoreView for editing and viewing chores. - Created KindnessEditView and KindnessView for managing kindness acts. - Developed PenaltyEditView and PenaltyView for managing penalties. - Added TaskSubNav for navigation between chores, kindness acts, and penalties.
This commit is contained in:
@@ -16,15 +16,15 @@ class ChildOverride(BaseModel):
|
||||
"""
|
||||
child_id: str
|
||||
entity_id: str
|
||||
entity_type: Literal['task', 'reward']
|
||||
entity_type: Literal['task', 'reward', 'chore', 'kindness', 'penalty']
|
||||
custom_value: int
|
||||
|
||||
def __post_init__(self):
|
||||
"""Validate custom_value range and entity_type."""
|
||||
if self.custom_value < 0 or self.custom_value > 10000:
|
||||
raise ValueError("custom_value must be between 0 and 10000")
|
||||
if self.entity_type not in ['task', 'reward']:
|
||||
raise ValueError("entity_type must be 'task' or 'reward'")
|
||||
if self.entity_type not in ['task', 'reward', 'chore', 'kindness', 'penalty']:
|
||||
raise ValueError("entity_type must be 'task', 'reward', 'chore', 'kindness', or 'penalty'")
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, d: dict):
|
||||
@@ -52,7 +52,7 @@ class ChildOverride(BaseModel):
|
||||
def create_override(
|
||||
child_id: str,
|
||||
entity_id: str,
|
||||
entity_type: Literal['task', 'reward'],
|
||||
entity_type: Literal['task', 'reward', 'chore', 'kindness', 'penalty'],
|
||||
custom_value: int
|
||||
) -> 'ChildOverride':
|
||||
"""Factory method to create a new override."""
|
||||
|
||||
43
backend/models/pending_confirmation.py
Normal file
43
backend/models/pending_confirmation.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal, Optional
|
||||
from models.base import BaseModel
|
||||
|
||||
|
||||
PendingEntityType = Literal['chore', 'reward']
|
||||
PendingStatus = Literal['pending', 'approved', 'rejected']
|
||||
|
||||
|
||||
@dataclass
|
||||
class PendingConfirmation(BaseModel):
|
||||
child_id: str
|
||||
entity_id: str
|
||||
entity_type: PendingEntityType
|
||||
user_id: str
|
||||
status: PendingStatus = "pending"
|
||||
approved_at: Optional[str] = None # ISO 8601 UTC timestamp, set on approval
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, d: dict):
|
||||
return cls(
|
||||
child_id=d.get('child_id'),
|
||||
entity_id=d.get('entity_id'),
|
||||
entity_type=d.get('entity_type'),
|
||||
user_id=d.get('user_id'),
|
||||
status=d.get('status', 'pending'),
|
||||
approved_at=d.get('approved_at'),
|
||||
id=d.get('id'),
|
||||
created_at=d.get('created_at'),
|
||||
updated_at=d.get('updated_at')
|
||||
)
|
||||
|
||||
def to_dict(self):
|
||||
base = super().to_dict()
|
||||
base.update({
|
||||
'child_id': self.child_id,
|
||||
'entity_id': self.entity_id,
|
||||
'entity_type': self.entity_type,
|
||||
'user_id': self.user_id,
|
||||
'status': self.status,
|
||||
'approved_at': self.approved_at
|
||||
})
|
||||
return base
|
||||
@@ -1,20 +1,28 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
from models.base import BaseModel
|
||||
|
||||
TaskType = Literal['chore', 'kindness', 'penalty']
|
||||
|
||||
@dataclass
|
||||
class Task(BaseModel):
|
||||
name: str
|
||||
points: int
|
||||
is_good: bool
|
||||
type: TaskType
|
||||
image_id: str | None = None
|
||||
user_id: str | None = None
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, d: dict):
|
||||
# Support legacy is_good field for migration
|
||||
task_type = d.get('type')
|
||||
if task_type is None:
|
||||
is_good = d.get('is_good', True)
|
||||
task_type = 'chore' if is_good else 'penalty'
|
||||
return cls(
|
||||
name=d.get('name'),
|
||||
points=d.get('points', 0),
|
||||
is_good=d.get('is_good', True),
|
||||
type=task_type,
|
||||
image_id=d.get('image_id'),
|
||||
user_id=d.get('user_id'),
|
||||
id=d.get('id'),
|
||||
@@ -27,8 +35,13 @@ class Task(BaseModel):
|
||||
base.update({
|
||||
'name': self.name,
|
||||
'points': self.points,
|
||||
'is_good': self.is_good,
|
||||
'type': self.type,
|
||||
'image_id': self.image_id,
|
||||
'user_id': self.user_id
|
||||
})
|
||||
return base
|
||||
|
||||
@property
|
||||
def is_good(self) -> bool:
|
||||
"""Backward compatibility: chore and kindness are 'good', penalty is not."""
|
||||
return self.type != 'penalty'
|
||||
|
||||
@@ -4,8 +4,8 @@ from typing import Literal, Optional
|
||||
from models.base import BaseModel
|
||||
|
||||
|
||||
EntityType = Literal['task', 'reward', 'penalty']
|
||||
ActionType = Literal['activated', 'requested', 'redeemed', 'cancelled']
|
||||
EntityType = Literal['task', 'reward', 'penalty', 'chore', 'kindness']
|
||||
ActionType = Literal['activated', 'requested', 'redeemed', 'cancelled', 'confirmed', 'approved', 'rejected', 'reset']
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
Reference in New Issue
Block a user