diff --git a/backend/app/main.py b/backend/app/main.py index 9a84fa5..bbba44f 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -31,6 +31,7 @@ async def lifespan(app: FastAPI): # Idempotent column additions for schema migrations await _add_column_if_missing(conn, "schedule_templates", "day_start_time", "TIME NULL") await _add_column_if_missing(conn, "schedule_templates", "day_end_time", "TIME NULL") + await _add_column_if_missing(conn, "schedule_blocks", "duration_minutes", "INT NULL") yield diff --git a/backend/app/models/schedule.py b/backend/app/models/schedule.py index 9061f22..8296714 100644 --- a/backend/app/models/schedule.py +++ b/backend/app/models/schedule.py @@ -39,6 +39,7 @@ class ScheduleBlock(Base): ) time_start: Mapped[time] = mapped_column(Time, nullable=False) time_end: Mapped[time] = mapped_column(Time, nullable=False) + duration_minutes: Mapped[int | None] = mapped_column(Integer, nullable=True) label: Mapped[str | None] = mapped_column(String(100), nullable=True) # override subject name notes: Mapped[str | None] = mapped_column(Text, nullable=True) order_index: Mapped[int] = mapped_column(Integer, default=0) diff --git a/backend/app/schemas/schedule.py b/backend/app/schemas/schedule.py index 78c4d2a..3be64db 100644 --- a/backend/app/schemas/schedule.py +++ b/backend/app/schemas/schedule.py @@ -6,6 +6,7 @@ class ScheduleBlockCreate(BaseModel): subject_id: int | None = None time_start: time time_end: time + duration_minutes: int | None = None label: str | None = None notes: str | None = None order_index: int = 0 @@ -15,6 +16,7 @@ class ScheduleBlockUpdate(BaseModel): subject_id: int | None = None time_start: time | None = None time_end: time | None = None + duration_minutes: int | None = None label: str | None = None notes: str | None = None order_index: int | None = None @@ -25,6 +27,7 @@ class ScheduleBlockOut(BaseModel): subject_id: int | None time_start: time time_end: time + duration_minutes: int | None label: str | None notes: str | None order_index: int diff --git a/frontend/src/components/TimerDisplay.vue b/frontend/src/components/TimerDisplay.vue index 3c3934a..f2498a3 100644 --- a/frontend/src/components/TimerDisplay.vue +++ b/frontend/src/components/TimerDisplay.vue @@ -45,6 +45,7 @@ function parseTime(str) { const blockDuration = computed(() => { if (!props.block) return 0 + if (props.block.duration_minutes != null) return props.block.duration_minutes * 60 return parseTime(props.block.time_end) - parseTime(props.block.time_start) }) diff --git a/frontend/src/views/AdminView.vue b/frontend/src/views/AdminView.vue index d969061..6c67285 100644 --- a/frontend/src/views/AdminView.vue +++ b/frontend/src/views/AdminView.vue @@ -168,6 +168,12 @@ to + @@ -192,6 +198,12 @@ to + @@ -295,7 +307,7 @@ const templates = ref([]) const showCreateForm = ref(false) const editingTemplate = ref(null) const newTemplate = ref({ name: '', child_id: null, is_default: false }) -const newBlock = ref({ subject_id: null, time_start: '', time_end: '', label: '', order_index: 0 }) +const newBlock = ref({ subject_id: null, time_start: '', time_end: '', duration_minutes: null, label: '', order_index: 0 }) const editingBlock = ref(null) function childName(id) { @@ -331,7 +343,7 @@ async function addBlock(templateId) { order_index: templates.value.find((t) => t.id === templateId)?.blocks.length || 0, } await api.post(`/api/schedules/${templateId}/blocks`, payload) - newBlock.value = { subject_id: null, time_start: '', time_end: '', label: '', order_index: 0 } + newBlock.value = { subject_id: null, time_start: '', time_end: '', duration_minutes: null, label: '', order_index: 0 } await loadTemplates() } @@ -341,6 +353,7 @@ function startEditBlock(block) { subject_id: block.subject_id, time_start: block.time_start ? block.time_start.slice(0, 5) : '', time_end: block.time_end ? block.time_end.slice(0, 5) : '', + duration_minutes: block.duration_minutes ?? null, label: block.label || '', } }