feat: Refactor ScheduleModal to support interval scheduling with date input and deadline toggle
Some checks failed
Chore App Build, Test, and Push Docker Images / build-and-push (push) Failing after 2m30s
Some checks failed
Chore App Build, Test, and Push Docker Images / build-and-push (push) Failing after 2m30s
- Updated ChoreSchedule model to include anchor_date and interval_has_deadline. - Refactored interval scheduling logic in scheduleUtils to use anchor_date. - Introduced DateInputField component for selecting anchor dates in ScheduleModal. - Enhanced ScheduleModal to include a stepper for interval days and a toggle for deadline. - Updated tests for ScheduleModal and scheduleUtils to reflect new interval scheduling logic. - Added DateInputField tests to ensure proper functionality and prop handling.
This commit is contained in:
@@ -117,7 +117,8 @@ def test_set_schedule_interval_mode(client):
|
||||
payload = {
|
||||
"mode": "interval",
|
||||
"interval_days": 3,
|
||||
"anchor_weekday": 2,
|
||||
"anchor_date": "2026-03-01",
|
||||
"interval_has_deadline": True,
|
||||
"interval_hour": 14,
|
||||
"interval_minute": 30,
|
||||
}
|
||||
@@ -126,25 +127,57 @@ def test_set_schedule_interval_mode(client):
|
||||
data = resp.get_json()
|
||||
assert data["mode"] == "interval"
|
||||
assert data["interval_days"] == 3
|
||||
assert data["anchor_weekday"] == 2
|
||||
assert data["anchor_date"] == "2026-03-01"
|
||||
assert data["interval_has_deadline"] is True
|
||||
assert data["interval_hour"] == 14
|
||||
assert data["interval_minute"] == 30
|
||||
|
||||
|
||||
def test_set_schedule_interval_bad_days(client):
|
||||
# interval_days = 1 is out of range [2–7]
|
||||
payload = {"mode": "interval", "interval_days": 1, "anchor_weekday": 0}
|
||||
def test_set_schedule_interval_days_1_valid(client):
|
||||
"""interval_days=1 is now valid (range changed to [1, 7])."""
|
||||
payload = {"mode": "interval", "interval_days": 1, "anchor_date": ""}
|
||||
resp = client.put(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule', json=payload)
|
||||
assert resp.status_code == 200
|
||||
assert resp.get_json()["interval_days"] == 1
|
||||
|
||||
|
||||
def test_set_schedule_interval_days_0_invalid(client):
|
||||
"""interval_days=0 is still out of range."""
|
||||
payload = {"mode": "interval", "interval_days": 0, "anchor_date": ""}
|
||||
resp = client.put(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule', json=payload)
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
def test_set_schedule_interval_bad_weekday(client):
|
||||
# anchor_weekday = 7 is out of range [0–6]
|
||||
payload = {"mode": "interval", "interval_days": 2, "anchor_weekday": 7}
|
||||
def test_set_schedule_interval_days_8_invalid(client):
|
||||
"""interval_days=8 is still out of range."""
|
||||
payload = {"mode": "interval", "interval_days": 8, "anchor_date": ""}
|
||||
resp = client.put(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule', json=payload)
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
def test_set_schedule_interval_has_deadline_false(client):
|
||||
"""interval_has_deadline=False is accepted and persisted."""
|
||||
payload = {
|
||||
"mode": "interval",
|
||||
"interval_days": 2,
|
||||
"anchor_date": "",
|
||||
"interval_has_deadline": False,
|
||||
"interval_hour": 0,
|
||||
"interval_minute": 0,
|
||||
}
|
||||
resp = client.put(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule', json=payload)
|
||||
assert resp.status_code == 200
|
||||
assert resp.get_json()["interval_has_deadline"] is False
|
||||
|
||||
|
||||
def test_set_schedule_interval_anchor_date_empty_string(client):
|
||||
"""anchor_date empty string is valid (means use today)."""
|
||||
payload = {"mode": "interval", "interval_days": 2, "anchor_date": ""}
|
||||
resp = client.put(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule', json=payload)
|
||||
assert resp.status_code == 200
|
||||
assert resp.get_json()["anchor_date"] == ""
|
||||
|
||||
|
||||
def test_set_schedule_invalid_mode(client):
|
||||
payload = {"mode": "weekly", "day_configs": []}
|
||||
resp = client.put(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule', json=payload)
|
||||
@@ -160,13 +193,56 @@ def test_set_schedule_upserts_existing(client):
|
||||
# Overwrite with interval mode
|
||||
client.put(
|
||||
f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule',
|
||||
json={"mode": "interval", "interval_days": 2, "anchor_weekday": 0, "interval_hour": 9, "interval_minute": 0},
|
||||
json={"mode": "interval", "interval_days": 2, "anchor_date": "", "interval_hour": 9, "interval_minute": 0},
|
||||
)
|
||||
resp = client.get(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule')
|
||||
assert resp.status_code == 200
|
||||
assert resp.get_json()["mode"] == "interval"
|
||||
|
||||
|
||||
def test_old_record_missing_new_fields_loads_with_defaults(client):
|
||||
"""Old DB records without anchor_date/interval_has_deadline load with correct defaults."""
|
||||
from db.chore_schedules import upsert_schedule
|
||||
from models.chore_schedule import ChoreSchedule
|
||||
|
||||
# Insert a schedule as if it was created before phase 2 (missing new fields)
|
||||
old_style = ChoreSchedule(
|
||||
child_id=TEST_CHILD_ID,
|
||||
task_id=TEST_TASK_ID,
|
||||
mode='interval',
|
||||
interval_days=3,
|
||||
anchor_date='', # default value
|
||||
interval_has_deadline=True, # default value
|
||||
interval_hour=8,
|
||||
interval_minute=0,
|
||||
)
|
||||
upsert_schedule(old_style)
|
||||
|
||||
# Manually wipe the new fields from the raw stored record to simulate a pre-phase-2 record
|
||||
from db.db import chore_schedules_db
|
||||
from tinydb import Query
|
||||
ScheduleQ = Query()
|
||||
record = chore_schedules_db.search(
|
||||
(ScheduleQ.child_id == TEST_CHILD_ID) & (ScheduleQ.task_id == TEST_TASK_ID)
|
||||
)[0]
|
||||
doc_id = chore_schedules_db.get(
|
||||
(ScheduleQ.child_id == TEST_CHILD_ID) & (ScheduleQ.task_id == TEST_TASK_ID)
|
||||
).doc_id
|
||||
chore_schedules_db.update(
|
||||
lambda rec: (
|
||||
rec.pop('anchor_date', None),
|
||||
rec.pop('interval_has_deadline', None),
|
||||
),
|
||||
doc_ids=[doc_id],
|
||||
)
|
||||
|
||||
resp = client.get(f'/child/{TEST_CHILD_ID}/task/{TEST_TASK_ID}/schedule')
|
||||
assert resp.status_code == 200
|
||||
data = resp.get_json()
|
||||
assert data['anchor_date'] == ''
|
||||
assert data['interval_has_deadline'] is True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# DELETE schedule
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user