feat: dodaj scheduled_date na Task i kalendar widget

- Novo polje Task.scheduled_date (DateField, nullable, db_index) za planiranje
- Migracija 0009_task_scheduled_date
- Celery task notify_upcoming_tasks: dnevno u 08:00 šalje Pusher + email za sutrašnje zadatke
- TaskCalendarWidget: react-spring slide panel s lijeve strane, grid prikaz mjeseca, dot indikatori, klik otvara TaskServiceRecordsModal
- FleetDashboardShell: import + render kalendara, Planirano stupac u tablici zadataka
- TaskCreateModal + TaskServiceRecordsModal: scheduled_date input i prikaz
- Admin + serializer + views: scheduled_date filter i prikaz
- 47 backend testova prolaze (uključuje 8 novih za notify_upcoming_tasks)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
mariomitte
2026-07-19 14:52:10 +02:00
parent bf8e25efe7
commit 0c9e1b7d50
17 changed files with 610 additions and 7 deletions

View File

@@ -10,6 +10,7 @@ const INITIAL_FORM = {
description: '',
status: 'aktivan',
work_order: '',
scheduled_date: '',
};
export default function TaskCreateModal({ open, workOrders = [], contextCrane = null, onClose, onSuccess }) {
@@ -50,6 +51,7 @@ export default function TaskCreateModal({ open, workOrders = [], contextCrane =
description: selectedTemplate.description || '',
status: 'aktivan',
work_order: '',
// scheduled_date is kept so user can set it in template mode too
}));
}, [selectedTemplate]);
@@ -98,6 +100,7 @@ export default function TaskCreateModal({ open, workOrders = [], contextCrane =
template_id: templateId,
vehicle: contextCrane.id,
work_order: null,
scheduled_date: form.scheduled_date || null,
};
} else {
const title = String(form.title || '').trim();
@@ -115,6 +118,7 @@ export default function TaskCreateModal({ open, workOrders = [], contextCrane =
status: form.status || 'aktivan',
vehicle: contextCrane.id,
work_order: form.work_order || null,
scheduled_date: form.scheduled_date || null,
};
}
@@ -231,6 +235,17 @@ export default function TaskCreateModal({ open, workOrders = [], contextCrane =
</label>
<label className="flex flex-col gap-1 text-sm">
<span className="font-medium text-text-main">Planirani datum izvršenja</span>
<input
type="date"
value={form.scheduled_date}
onInput={(event) => setField('scheduled_date', event.currentTarget.value)}
disabled={submitting}
className="rounded-lg border border-border-hairline bg-canvas-base px-3 py-2 text-text-main disabled:opacity-50"
/>
</label>
<label className="flex flex-col gap-1 text-sm sm:col-span-2">
<span className="font-medium text-text-main">Povezani putni nalog</span>
<select
value={form.work_order}