Add TimeSelector and ScheduleModal components with tests
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 2m45s

- Implemented TimeSelector component for selecting time with AM/PM toggle and minute/hour increment/decrement functionality.
- Created ScheduleModal component for scheduling chores with options for specific days or intervals.
- Added utility functions for scheduling logic in scheduleUtils.ts.
- Developed comprehensive tests for TimeSelector and scheduleUtils functions to ensure correct behavior.
This commit is contained in:
2026-02-23 15:44:55 -05:00
parent d8822b44be
commit 234adbe05f
26 changed files with 2880 additions and 60 deletions

View File

@@ -0,0 +1,39 @@
from db.db import chore_schedules_db
from models.chore_schedule import ChoreSchedule
from tinydb import Query
def get_schedule(child_id: str, task_id: str) -> ChoreSchedule | None:
q = Query()
result = chore_schedules_db.search((q.child_id == child_id) & (q.task_id == task_id))
if not result:
return None
return ChoreSchedule.from_dict(result[0])
def upsert_schedule(schedule: ChoreSchedule) -> None:
q = Query()
existing = chore_schedules_db.get((q.child_id == schedule.child_id) & (q.task_id == schedule.task_id))
if existing:
chore_schedules_db.update(schedule.to_dict(), (q.child_id == schedule.child_id) & (q.task_id == schedule.task_id))
else:
chore_schedules_db.insert(schedule.to_dict())
def delete_schedule(child_id: str, task_id: str) -> bool:
q = Query()
existing = chore_schedules_db.get((q.child_id == child_id) & (q.task_id == task_id))
if not existing:
return False
chore_schedules_db.remove((q.child_id == child_id) & (q.task_id == task_id))
return True
def delete_schedules_for_child(child_id: str) -> None:
q = Query()
chore_schedules_db.remove(q.child_id == child_id)
def delete_schedules_for_task(task_id: str) -> None:
q = Query()
chore_schedules_db.remove(q.task_id == task_id)

View File

@@ -75,6 +75,8 @@ pending_reward_path = os.path.join(base_dir, 'pending_rewards.json')
users_path = os.path.join(base_dir, 'users.json')
tracking_events_path = os.path.join(base_dir, 'tracking_events.json')
child_overrides_path = os.path.join(base_dir, 'child_overrides.json')
chore_schedules_path = os.path.join(base_dir, 'chore_schedules.json')
task_extensions_path = os.path.join(base_dir, 'task_extensions.json')
# Use separate TinyDB instances/files for each collection
_child_db = TinyDB(child_path, indent=2)
@@ -85,6 +87,8 @@ _pending_rewards_db = TinyDB(pending_reward_path, indent=2)
_users_db = TinyDB(users_path, indent=2)
_tracking_events_db = TinyDB(tracking_events_path, indent=2)
_child_overrides_db = TinyDB(child_overrides_path, indent=2)
_chore_schedules_db = TinyDB(chore_schedules_path, indent=2)
_task_extensions_db = TinyDB(task_extensions_path, indent=2)
# Expose table objects wrapped with locking
child_db = LockedTable(_child_db)
@@ -95,6 +99,8 @@ pending_reward_db = LockedTable(_pending_rewards_db)
users_db = LockedTable(_users_db)
tracking_events_db = LockedTable(_tracking_events_db)
child_overrides_db = LockedTable(_child_overrides_db)
chore_schedules_db = LockedTable(_chore_schedules_db)
task_extensions_db = LockedTable(_task_extensions_db)
if os.environ.get('DB_ENV', 'prod') == 'test':
child_db.truncate()
@@ -105,4 +111,6 @@ if os.environ.get('DB_ENV', 'prod') == 'test':
users_db.truncate()
tracking_events_db.truncate()
child_overrides_db.truncate()
chore_schedules_db.truncate()
task_extensions_db.truncate()

View File

@@ -0,0 +1,27 @@
from db.db import task_extensions_db
from models.task_extension import TaskExtension
from tinydb import Query
def get_extension(child_id: str, task_id: str, date: str) -> TaskExtension | None:
q = Query()
result = task_extensions_db.search(
(q.child_id == child_id) & (q.task_id == task_id) & (q.date == date)
)
if not result:
return None
return TaskExtension.from_dict(result[0])
def add_extension(extension: TaskExtension) -> None:
task_extensions_db.insert(extension.to_dict())
def delete_extensions_for_child(child_id: str) -> None:
q = Query()
task_extensions_db.remove(q.child_id == child_id)
def delete_extensions_for_task(task_id: str) -> None:
q = Query()
task_extensions_db.remove(q.task_id == task_id)