fix: premjesti kalendar widget u Layout, popravi PDF toast i Servisi danas

- TaskCalendarPortal u Layout.astro (van AnimatedPage transforma) pa fixed
  pozicija uvijek prati viewport rub bez probijanja sadržaja
- Kalendar widgeta panel koristi clipping wrapper (overflow:hidden + animirana
  width) umjesto translate3d — potpuno skriva sjenu i border kad je zatvoren
- Serviser vidi samo svoje taskove, supervisor vidi sve; neprijavljeni korisnik
  ne vidi widget (return null)
- FleetDashboardShell sluša calendar:open-task event i otvara TaskServiceRecordsModal
- Servisi danas broji Taskove (scheduled_date=danas, status aktivan/servis)
  umjesto Servisnih zapisa
- downloadGeneratedPdfByUrl hvata grešku i prikazuje Toast (npr. PDF nije
  dostupan ili je istekao)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
mariomitte
2026-07-19 15:27:20 +02:00
parent 0c9e1b7d50
commit fe7edf5643
5 changed files with 91 additions and 17 deletions

View File

@@ -2,6 +2,7 @@ import { atom, computed } from 'nanostores';
import { api } from '../services/apiClient';
import { $accessToken } from './authStore';
import { showToast } from './toastStore';
import { $tasks } from './taskStore';
const DB_NAME = 'erp-offline-db';
const DB_VERSION = 1;
@@ -440,12 +441,15 @@ export const $workOrdersWithVehicle = computed(
);
export const $dashboardStats = computed(
[$workOrders, $serviceRecords],
(workOrders, serviceRecords) => {
[$workOrders, $serviceRecords, $tasks],
(workOrders, serviceRecords, tasks) => {
const today = new Date().toISOString().slice(0, 10);
const openWorkOrders = workOrders.filter((workOrder) => workOrder.status !== 'closed').length;
const closedWorkOrders = workOrders.filter((workOrder) => workOrder.status === 'closed').length;
const servicesToday = serviceRecords.filter((record) => record.service_date === today).length;
// Broji Taskove čiji je scheduled_date danas i status aktivan ili u tijeku
const servicesToday = tasks.filter(
(t) => t.scheduled_date === today && (t.status === 'aktivan' || t.status === 'servis')
).length;
const warnings = serviceRecords.filter((record) => {
if (record.next_service_due_at == null || record.mileage == null) return false;
return Number(record.next_service_due_at) - Number(record.mileage) <= 1000;
@@ -809,11 +813,18 @@ export async function downloadWorkOrderServiceRecordsPdf(workOrderId) {
export async function downloadGeneratedPdfByUrl(downloadUrl, filename = 'document.pdf') {
if (!downloadUrl) {
showToast('Nedostaje URL za preuzimanje PDF-a.', 'error');
throw new Error('Nedostaje URL za preuzimanje PDF-a.');
}
const blob = await api.get(downloadUrl, { responseType: 'blob' });
saveBlobToFile(blob, filename);
return true;
try {
const blob = await api.get(downloadUrl, { responseType: 'blob' });
saveBlobToFile(blob, filename);
return true;
} catch (err) {
const msg = err?.message || 'Preuzimanje PDF-a nije uspjelo.';
showToast(msg, 'error');
throw err;
}
}
export function openWorkOrderInvoicesPdfPage(workOrderId) {