diff --git a/backend/alembic/versions/013_drop_template_duration.py b/backend/alembic/versions/013_drop_template_duration.py new file mode 100644 index 0000000..298c087 --- /dev/null +++ b/backend/alembic/versions/013_drop_template_duration.py @@ -0,0 +1,27 @@ +"""drop duration_minutes from event_templates + +Revision ID: 013 +Revises: 012 +Create Date: 2026-02-23 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "013" +down_revision = "012" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.drop_column("event_templates", "duration_minutes") + + +def downgrade() -> None: + op.add_column( + "event_templates", + sa.Column("duration_minutes", sa.Integer(), server_default="60", nullable=False), + ) diff --git a/backend/app/models/event_template.py b/backend/app/models/event_template.py index 9ab2857..ed4cbee 100644 --- a/backend/app/models/event_template.py +++ b/backend/app/models/event_template.py @@ -1,4 +1,4 @@ -from sqlalchemy import String, Text, Integer, Boolean, ForeignKey +from sqlalchemy import String, Text, Boolean, ForeignKey, Integer from sqlalchemy.orm import Mapped, mapped_column from datetime import datetime from typing import Optional @@ -12,7 +12,6 @@ class EventTemplate(Base): name: Mapped[str] = mapped_column(String(255), nullable=False) title: Mapped[str] = mapped_column(String(255), nullable=False) description: Mapped[Optional[str]] = mapped_column(Text, nullable=True) - duration_minutes: Mapped[int] = mapped_column(Integer, default=60) calendar_id: Mapped[Optional[int]] = mapped_column( Integer, ForeignKey("calendars.id", ondelete="SET NULL"), nullable=True ) diff --git a/backend/app/schemas/event_template.py b/backend/app/schemas/event_template.py index 4afe417..9868537 100644 --- a/backend/app/schemas/event_template.py +++ b/backend/app/schemas/event_template.py @@ -7,7 +7,6 @@ class EventTemplateCreate(BaseModel): name: str title: str description: Optional[str] = None - duration_minutes: int = 60 calendar_id: Optional[int] = None recurrence_rule: Optional[str] = None all_day: bool = False @@ -19,7 +18,6 @@ class EventTemplateUpdate(BaseModel): name: Optional[str] = None title: Optional[str] = None description: Optional[str] = None - duration_minutes: Optional[int] = None calendar_id: Optional[int] = None recurrence_rule: Optional[str] = None all_day: Optional[bool] = None @@ -32,7 +30,6 @@ class EventTemplateResponse(BaseModel): name: str title: str description: Optional[str] - duration_minutes: int calendar_id: Optional[int] recurrence_rule: Optional[str] all_day: bool diff --git a/frontend/src/components/calendar/EventForm.tsx b/frontend/src/components/calendar/EventForm.tsx index 152cc1f..4778dca 100644 --- a/frontend/src/components/calendar/EventForm.tsx +++ b/frontend/src/components/calendar/EventForm.tsx @@ -75,12 +75,29 @@ function parseRecurrenceRule(raw?: string): RecurrenceRule | null { } } +function nowLocal(): string { + const now = new Date(); + const pad = (n: number) => n.toString().padStart(2, '0'); + return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}T${pad(now.getHours())}:${pad(now.getMinutes())}`; +} + +function plusOneHour(dt: string): string { + const d = new Date(dt); + d.setHours(d.getHours() + 1); + const pad = (n: number) => n.toString().padStart(2, '0'); + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`; +} + export default function EventForm({ event, initialStart, initialEnd, initialAllDay, editScope, onClose }: EventFormProps) { const queryClient = useQueryClient(); const { data: calendars = [] } = useCalendars(); const isAllDay = event?.all_day ?? initialAllDay ?? false; - const rawStart = event?.start_datetime || initialStart || ''; - const rawEnd = event?.end_datetime || initialEnd || ''; + + // Default to current time / +1 hour when creating a new event with no selection + const defaultStart = nowLocal(); + const defaultEnd = plusOneHour(defaultStart); + const rawStart = event?.start_datetime || initialStart || defaultStart; + const rawEnd = event?.end_datetime || initialEnd || defaultEnd; const defaultCalendar = calendars.find((c) => c.is_default); const initialCalendarId = event?.calendar_id?.toString() || defaultCalendar?.id?.toString() || ''; diff --git a/frontend/src/components/calendar/TemplateForm.tsx b/frontend/src/components/calendar/TemplateForm.tsx index 39a359f..4c99ec2 100644 --- a/frontend/src/components/calendar/TemplateForm.tsx +++ b/frontend/src/components/calendar/TemplateForm.tsx @@ -45,7 +45,6 @@ export default function TemplateForm({ template, onClose }: TemplateFormProps) { name: template?.name || '', title: template?.title || '', description: template?.description || '', - duration_minutes: template?.duration_minutes?.toString() || '60', calendar_id: template?.calendar_id?.toString() || '', location_id: template?.location_id?.toString() || '', all_day: template?.all_day || false, @@ -58,7 +57,6 @@ export default function TemplateForm({ template, onClose }: TemplateFormProps) { name: data.name, title: data.title, description: data.description || null, - duration_minutes: parseInt(data.duration_minutes) || 60, calendar_id: data.calendar_id ? parseInt(data.calendar_id) : null, location_id: data.location_id ? parseInt(data.location_id) : null, all_day: data.all_day, @@ -128,32 +126,18 @@ export default function TemplateForm({ template, onClose }: TemplateFormProps) { /> -
-
- - setFormData({ ...formData, duration_minutes: e.target.value })} - /> -
- -
- - -
+
+ +
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index d1a5ed3..9f88aee 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -232,7 +232,6 @@ export interface EventTemplate { name: string; title: string; description?: string; - duration_minutes: number; calendar_id?: number; recurrence_rule?: string; all_day: boolean;