feat: async email slanje s cache PDF fallbackom i admin logom

Prebaci slanje emaila putnog naloga u pozadinski task bez blokiranja UI-a i dodaj fallback kad queue nije dostupna. Kod greške slanja spremi generirane PDF-ove u cache i pošalji notifikaciju s download linkovima.

Dodaj EmailDispatchLog model i Django admin pregled za praćenje kome je poslano, što je poslano, status i detalje privitaka.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
mariomitte
2026-07-21 14:41:45 +02:00
parent 42fcd836ed
commit 5945423346
11 changed files with 1338 additions and 85 deletions

View File

@@ -887,9 +887,19 @@ export async function sendWorkOrderEmail(workOrderId, payload = {}, options = {}
if (!workOrderId) {
throw new Error('Work order ID je obavezan.');
}
const response = await api.post(`fleet/work-orders/${encodeURIComponent(workOrderId)}/send-email/`, payload);
const recipients = Array.isArray(payload?.recipients)
? payload.recipients.filter(Boolean)
: [];
const normalizedPayload = {
...payload,
recipients: recipients.length > 0 ? recipients : undefined,
};
if (!normalizedPayload.recipients || normalizedPayload.recipients.length === 0) {
delete normalizedPayload.recipients;
}
const response = await api.post(`fleet/work-orders/${encodeURIComponent(workOrderId)}/send-email/`, normalizedPayload);
if (options.toast !== false) {
showToast('Email za putni nalog je poslan.', 'success');
showToast('Zahtjev za slanje emaila je pokrenut u pozadini.', 'info');
}
return response;
}
@@ -907,6 +917,34 @@ export async function sendServiceRecordEmail(serviceRecordId, payload = {}, opti
if (!serviceRecordId) {
throw new Error('Service record ID je obavezan.');
}
const recipients = Array.isArray(payload?.recipients)
? payload.recipients.filter(Boolean)
: [];
if (recipients.length > 0) {
const failures = [];
for (const recipient of recipients) {
try {
await api.post(`fleet/service-records/${encodeURIComponent(serviceRecordId)}/send-email/`, {
...payload,
recipient,
});
} catch (error) {
failures.push({ recipient, error });
}
}
if (failures.length > 0) {
const firstError = failures[0]?.error;
const message = firstError?.message || `Neuspješno slanje za ${failures.length} primatelja.`;
const batchError = new Error(message);
batchError.failures = failures;
throw batchError;
}
if (options.toast !== false) {
showToast('Email za servisni zapis je poslan.', 'success');
}
return { sent: true, recipients };
}
const response = await api.post(`fleet/service-records/${encodeURIComponent(serviceRecordId)}/send-email/`, payload);
if (options.toast !== false) {
showToast('Email za servisni zapis je poslan.', 'success');