Fix bugs, add auto-refresh, quick-complete tasks, and every-N-day routines

- Fix bot auth: merge duplicate on_ready handlers so session restore runs (#13)
- Fix push notifications: pass Uint8Array directly as applicationServerKey (#6)
- Show specific conflict reason on schedule save instead of generic error (#17)
- Add inline checkmark button to complete tasks on routines timeline (#18)
- Add visibility-change + 60s polling auto-refresh to routines, meds, tasks (#15)
- Add every-N-day routine scheduling: schema, API, scheduler, and UI (#16)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 19:04:52 -06:00
parent 24a1d18b25
commit ecb79af44e
10 changed files with 288 additions and 96 deletions

View File

@@ -661,17 +661,20 @@ def register(app):
continue
steps = postgres.select("routine_steps", where={"routine_id": r["id"]})
total_duration = sum(s.get("duration_minutes") or 0 for s in steps)
result.append(
{
"routine_id": r["id"],
"routine_name": r.get("name", ""),
"routine_icon": r.get("icon", ""),
"days": sched.get("days", []),
"time": sched.get("time"),
"remind": sched.get("remind", True),
"total_duration_minutes": total_duration,
}
)
entry = {
"routine_id": r["id"],
"routine_name": r.get("name", ""),
"routine_icon": r.get("icon", ""),
"days": sched.get("days", []),
"time": sched.get("time"),
"remind": sched.get("remind", True),
"total_duration_minutes": total_duration,
"frequency": sched.get("frequency", "weekly"),
}
if sched.get("frequency") == "every_n_days":
entry["interval_days"] = sched.get("interval_days")
entry["start_date"] = str(sched.get("start_date")) if sched.get("start_date") else None
result.append(entry)
return flask.jsonify(result), 200
def _get_routine_duration_minutes(routine_id):
@@ -745,7 +748,10 @@ def register(app):
@app.route("/api/routines/<routine_id>/schedule", methods=["PUT"])
def api_setRoutineSchedule(routine_id):
"""Set when this routine should run. Body: {days: ["mon","tue",...], time: "08:00", remind: true}"""
"""Set when this routine should run.
Body: {days, time, remind, frequency?, interval_days?, start_date?}
frequency: 'weekly' (default, uses days) or 'every_n_days' (uses interval_days + start_date)
"""
user_uuid = _auth(flask.request)
if not user_uuid:
return flask.jsonify({"error": "unauthorized"}), 401
@@ -758,15 +764,18 @@ def register(app):
if not data:
return flask.jsonify({"error": "missing body"}), 400
# Check for schedule conflicts
new_days = data.get("days", [])
new_time = data.get("time")
has_conflict, conflict_msg = _check_schedule_conflicts(
user_uuid, new_days, new_time, exclude_routine_id=routine_id,
new_routine_id=routine_id,
)
if has_conflict:
return flask.jsonify({"error": conflict_msg}), 409
frequency = data.get("frequency", "weekly")
# Check for schedule conflicts (only for weekly — interval conflicts checked at reminder time)
if frequency == "weekly":
new_days = data.get("days", [])
new_time = data.get("time")
has_conflict, conflict_msg = _check_schedule_conflicts(
user_uuid, new_days, new_time, exclude_routine_id=routine_id,
new_routine_id=routine_id,
)
if has_conflict:
return flask.jsonify({"error": conflict_msg}), 409
existing = postgres.select_one("routine_schedules", {"routine_id": routine_id})
schedule_data = {
@@ -774,6 +783,9 @@ def register(app):
"days": json.dumps(data.get("days", [])),
"time": data.get("time"),
"remind": data.get("remind", True),
"frequency": frequency,
"interval_days": data.get("interval_days"),
"start_date": data.get("start_date"),
}
if existing:
result = postgres.update(