Refactor Time Selector and Scheduler UI; Implement TimePickerPopover Component
Some checks failed
Chore App Build, Test, and Push Docker Images / build-and-push (push) Failing after 1m5s

- Updated TimeSelector.vue styles for smaller dimensions and font sizes.
- Added new API proxy for '/events' in vite.config.ts.
- Created bug specifications for various UI issues and fixes in bugs-1.0.5-001.md and bugs-1.0.5-002.md.
- Introduced TimePickerPopover.vue for a new time selection interface in the chore scheduler.
- Refactored ScheduleModal.vue to replace checkbox rows with a chip-based design for selecting specific days.
- Enhanced chore scheduling logic to ensure proper handling of time extensions and UI updates.
This commit is contained in:
2026-02-25 19:45:31 -05:00
parent a41a357f50
commit 91a52c1973
19 changed files with 1250 additions and 176 deletions

View File

@@ -4,7 +4,7 @@ from api.utils import get_validated_user_id, send_event_for_current_user
from api.error_codes import ErrorCodes
from db.db import child_db
from db.chore_schedules import get_schedule, upsert_schedule, delete_schedule
from db.task_extensions import get_extension, add_extension
from db.task_extensions import get_extension, add_extension, delete_extension_for_child_task
from models.chore_schedule import ChoreSchedule
from models.task_extension import TaskExtension
from events.types.event import Event
@@ -58,11 +58,15 @@ def set_chore_schedule(child_id, task_id):
day_configs = data.get('day_configs', [])
if not isinstance(day_configs, list):
return jsonify({'error': 'day_configs must be a list', 'code': ErrorCodes.INVALID_VALUE}), 400
default_hour = data.get('default_hour', 8)
default_minute = data.get('default_minute', 0)
schedule = ChoreSchedule(
child_id=child_id,
task_id=task_id,
mode='days',
day_configs=day_configs,
default_hour=default_hour,
default_minute=default_minute,
)
else:
interval_days = data.get('interval_days', 2)
@@ -83,6 +87,7 @@ def set_chore_schedule(child_id, task_id):
interval_minute=interval_minute,
)
delete_extension_for_child_task(child_id, task_id)
upsert_schedule(schedule)
send_event_for_current_user(Event(

View File

@@ -25,3 +25,8 @@ def delete_extensions_for_child(child_id: str) -> None:
def delete_extensions_for_task(task_id: str) -> None:
q = Query()
task_extensions_db.remove(q.task_id == task_id)
def delete_extension_for_child_task(child_id: str, task_id: str) -> None:
q = Query()
task_extensions_db.remove((q.child_id == child_id) & (q.task_id == task_id))

View File

@@ -59,9 +59,15 @@ def sse_response_for_user(user_id: str):
def generate():
try:
while True:
# Get message from queue (blocks until available)
message = user_queue.get()
yield message
try:
# Use a timeout so the thread yields periodically and keepalives are sent.
# This prevents Werkzeug's dev server from starving other connections.
message = user_queue.get(timeout=15)
yield message
logger.info(f"Sent message to {user_id} connection {connection_id}")
except queue.Empty:
# Send an SSE comment as a keepalive ping to maintain the connection.
yield b': ping\n\n'
except GeneratorExit:
# Clean up when client disconnects
if user_id in user_queues and connection_id in user_queues[user_id]:

View File

@@ -33,6 +33,8 @@ class ChoreSchedule(BaseModel):
# mode='days' fields
day_configs: list = field(default_factory=list) # list of DayConfig dicts
default_hour: int = 8 # master deadline hour for 'days' mode
default_minute: int = 0 # master deadline minute for 'days' mode
# mode='interval' fields
interval_days: int = 2 # 27
@@ -47,6 +49,8 @@ class ChoreSchedule(BaseModel):
task_id=d.get('task_id'),
mode=d.get('mode', 'days'),
day_configs=d.get('day_configs', []),
default_hour=d.get('default_hour', 8),
default_minute=d.get('default_minute', 0),
interval_days=d.get('interval_days', 2),
anchor_weekday=d.get('anchor_weekday', 0),
interval_hour=d.get('interval_hour', 0),
@@ -63,6 +67,8 @@ class ChoreSchedule(BaseModel):
'task_id': self.task_id,
'mode': self.mode,
'day_configs': self.day_configs,
'default_hour': self.default_hour,
'default_minute': self.default_minute,
'interval_days': self.interval_days,
'anchor_weekday': self.anchor_weekday,
'interval_hour': self.interval_hour,