Sort schedule blocks by start time instead of order_index

Backend queries and model relationship now order by time_start.
Frontend also sorts blocks client-side for reliability across all views.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 16:22:17 -08:00
parent 3670bc63e7
commit 7346159745
5 changed files with 17 additions and 5 deletions

View File

@@ -20,7 +20,7 @@ class ScheduleTemplate(TimestampMixin, Base):
user: Mapped["User"] = relationship("User", back_populates="schedule_templates") # noqa: F821
child: Mapped["Child | None"] = relationship("Child") # noqa: F821
blocks: Mapped[list["ScheduleBlock"]] = relationship(
"ScheduleBlock", back_populates="template", cascade="all, delete-orphan", order_by="ScheduleBlock.order_index"
"ScheduleBlock", back_populates="template", cascade="all, delete-orphan", order_by="ScheduleBlock.time_start"
)
daily_sessions: Mapped[list["DailySession"]] = relationship( # noqa: F821
"DailySession", back_populates="template"

View File

@@ -50,7 +50,7 @@ async def get_dashboard(child_id: int, db: AsyncSession = Depends(get_db)):
select(ScheduleBlock)
.where(ScheduleBlock.template_id == session.template_id)
.options(selectinload(ScheduleBlock.subject).selectinload(Subject.options))
.order_by(ScheduleBlock.order_index)
.order_by(ScheduleBlock.time_start)
)
blocks = blocks_result.scalars().all()

View File

@@ -28,7 +28,7 @@ async def _broadcast_session(db: AsyncSession, session: DailySession) -> None:
select(ScheduleBlock)
.where(ScheduleBlock.template_id == session.template_id)
.options(selectinload(ScheduleBlock.subject).selectinload(Subject.options))
.order_by(ScheduleBlock.order_index)
.order_by(ScheduleBlock.time_start)
)
blocks = [
{

View File

@@ -24,9 +24,16 @@ export const useScheduleStore = defineStore('schedule', () => {
return Math.round((completedBlockIds.value.length / blocks.value.length) * 100)
})
function sortBlocksByTime(arr) {
return (arr || []).slice().sort((a, b) => {
const toMin = (t) => { if (!t) return 0; const [h, m] = t.split(':').map(Number); return h * 60 + m }
return toMin(a.time_start) - toMin(b.time_start)
})
}
function applySnapshot(snapshot) {
session.value = snapshot.session
blocks.value = snapshot.blocks || []
blocks.value = sortBlocksByTime(snapshot.blocks)
completedBlockIds.value = snapshot.completed_block_ids || []
isPaused.value = false
if (snapshot.child) child.value = snapshot.child

View File

@@ -186,7 +186,7 @@
</div>
<div class="block-list">
<template v-for="block in template.blocks" :key="block.id">
<template v-for="block in sortedBlocks(template.blocks)" :key="block.id">
<!-- Edit mode -->
<form
v-if="editingBlock && editingBlock.id === block.id"
@@ -384,6 +384,11 @@ function subjectName(id) {
return subjects.value.find((s) => s.id === id)?.name || null
}
function sortedBlocks(blocks) {
const toMin = (t) => { if (!t) return 0; const [h, m] = t.split(':').map(Number); return h * 60 + m }
return (blocks || []).slice().sort((a, b) => toMin(a.time_start) - toMin(b.time_start))
}
function blockDurationLabel(block) {
if (block.duration_minutes != null) return `${block.duration_minutes} min`
if (block.time_start && block.time_end) {