This commit is contained in:
@@ -0,0 +1,239 @@
|
||||
import { useMemo } from 'preact/hooks';
|
||||
import ModalShell from '../ui/ModalShell';
|
||||
import { downloadWorkOrderInvoicesPdf, downloadWorkOrderPdf, downloadWorkOrderServiceRecordsPdf } from '../../stores/fleetDashboardStore';
|
||||
|
||||
function formatTimestamp(value) {
|
||||
if (!value) return '';
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return '';
|
||||
return new Intl.DateTimeFormat('hr-HR', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
function detectNotificationKind(notification) {
|
||||
const title = String(notification?.title || '').toLowerCase();
|
||||
if (title.includes('servisni kontekst')) return 'service_context';
|
||||
if (title.includes('pdf')) return 'work_order_pdf';
|
||||
if (title.includes('putni nalog kreiran')) return 'work_order_created';
|
||||
if (title.includes('novi putni nalog')) return 'work_order_assigned';
|
||||
if (title.includes('putni nalog zatvoren')) return 'work_order_closed';
|
||||
if (title.includes('putni nalog obrisan')) return 'work_order_deleted';
|
||||
if (title.includes('servis zabilježen')) return 'service_record_created';
|
||||
if (title.includes('servis na vašem vozilu')) return 'service_record_vehicle_update';
|
||||
if (title.includes('uskoro servis')) return 'service_due_warning';
|
||||
return 'generic';
|
||||
}
|
||||
|
||||
function getEntityAction(notification, meta) {
|
||||
const metadata = notification?.metadata || {};
|
||||
if (metadata.entity_type === 'service_context' && metadata.vehicle_id) {
|
||||
return {
|
||||
label: 'Otvori servisni kontekst',
|
||||
run() {
|
||||
window.dispatchEvent(new CustomEvent('notification:open-entity', {
|
||||
detail: {
|
||||
entityType: 'service_context',
|
||||
vehicleId: metadata.vehicle_id,
|
||||
section: metadata.section || meta.section,
|
||||
},
|
||||
}));
|
||||
},
|
||||
};
|
||||
}
|
||||
if (
|
||||
metadata.entity_type === 'work_order_pdf' &&
|
||||
metadata.stage === 'completed' &&
|
||||
metadata.work_order_id
|
||||
) {
|
||||
return {
|
||||
label: metadata.pdf_type === 'invoices'
|
||||
? 'Preuzmi PDF računa'
|
||||
: metadata.pdf_type === 'service_records'
|
||||
? 'Preuzmi PDF servisnih zapisa'
|
||||
: 'Preuzmi PDF putnog naloga',
|
||||
run() {
|
||||
if (metadata.pdf_type === 'invoices') {
|
||||
return downloadWorkOrderInvoicesPdf(metadata.work_order_id);
|
||||
}
|
||||
if (metadata.pdf_type === 'service_records') {
|
||||
return downloadWorkOrderServiceRecordsPdf(metadata.work_order_id);
|
||||
}
|
||||
return downloadWorkOrderPdf(metadata.work_order_id);
|
||||
},
|
||||
};
|
||||
}
|
||||
if (metadata.entity_type === 'work_order' && metadata.work_order_id) {
|
||||
return {
|
||||
label: 'Otvori detalje naloga',
|
||||
run() {
|
||||
window.dispatchEvent(new CustomEvent('notification:open-entity', {
|
||||
detail: {
|
||||
entityType: 'work_order',
|
||||
entityId: metadata.work_order_id,
|
||||
vehicleId: metadata.vehicle_id || null,
|
||||
section: metadata.section || meta.section,
|
||||
},
|
||||
}));
|
||||
},
|
||||
};
|
||||
}
|
||||
if (metadata.entity_type === 'service_record' && metadata.service_record_id) {
|
||||
return {
|
||||
label: 'Otvori detalje servisa',
|
||||
run() {
|
||||
window.dispatchEvent(new CustomEvent('notification:open-entity', {
|
||||
detail: {
|
||||
entityType: 'service_record',
|
||||
entityId: metadata.service_record_id,
|
||||
vehicleId: metadata.vehicle_id || null,
|
||||
section: metadata.section || meta.section,
|
||||
},
|
||||
}));
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
label: `Otvori ${meta.category}`,
|
||||
run() {
|
||||
window.dispatchEvent(new CustomEvent('navbar:navigate', { detail: meta.section }));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getNotificationTypeMeta(kind) {
|
||||
if (kind.startsWith('work_order_pdf')) {
|
||||
return {
|
||||
category: 'PDF dokumenti',
|
||||
section: 'work-orders',
|
||||
recommendation: 'Možete odmah preuzeti generirani PDF dokument.',
|
||||
};
|
||||
}
|
||||
if (kind.startsWith('work_order')) {
|
||||
return {
|
||||
category: 'Putni nalozi',
|
||||
section: 'work-orders',
|
||||
recommendation: 'Provjerite status naloga i povezano vozilo.',
|
||||
};
|
||||
}
|
||||
if (kind === 'service_context') {
|
||||
return {
|
||||
category: 'Servisni kontekst',
|
||||
section: 'service-records',
|
||||
recommendation: 'Kontekst je postavljen i spreman za rad na servisnim zapisima.',
|
||||
};
|
||||
}
|
||||
if (kind.startsWith('service_')) {
|
||||
return {
|
||||
category: 'Servisni zapisi',
|
||||
section: 'service-records',
|
||||
recommendation: 'Provjerite detalje servisa i priloge.',
|
||||
};
|
||||
}
|
||||
return {
|
||||
category: 'Sustav',
|
||||
section: 'dashboard',
|
||||
recommendation: 'Pregledajte dashboard za dodatni kontekst.',
|
||||
};
|
||||
}
|
||||
|
||||
function levelClass(level) {
|
||||
if (level === 'critical') return 'bg-red-100 text-red-700';
|
||||
if (level === 'warning') return 'bg-amber-100 text-amber-700';
|
||||
return 'bg-indigo-100 text-indigo-700';
|
||||
}
|
||||
|
||||
export default function NotificationDetailModal({ open, notification, onClose }) {
|
||||
const kind = useMemo(() => detectNotificationKind(notification), [notification]);
|
||||
const meta = useMemo(() => getNotificationTypeMeta(kind), [kind]);
|
||||
const action = useMemo(() => getEntityAction(notification, meta), [notification, meta]);
|
||||
const showPdfLink = Boolean(
|
||||
notification?.metadata?.entity_type === 'work_order_pdf' &&
|
||||
notification?.metadata?.stage === 'completed' &&
|
||||
notification?.metadata?.work_order_id
|
||||
);
|
||||
|
||||
if (!open || !notification) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalShell
|
||||
onClose={onClose}
|
||||
overlayClassName="z-50 p-4"
|
||||
contentClassName="flex items-center justify-center min-h-full"
|
||||
panelClassName="max-h-[calc(100vh-2rem)] w-full max-w-2xl rounded-xl border border-border-hairline bg-canvas-elevated shadow-xl overflow-y-auto"
|
||||
>
|
||||
<div>
|
||||
<div className="flex items-center justify-between border-b border-border-hairline px-5 py-4">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-text-main">Detalji notifikacije</h3>
|
||||
<p className="text-xs text-text-muted">{formatTimestamp(notification.created_at)}</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="rounded-md px-2 py-1 text-sm text-text-muted hover:bg-canvas-deep"
|
||||
aria-label="Zatvori"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 px-5 py-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`rounded-full px-2.5 py-1 text-xs font-semibold ${levelClass(notification.level)}`}>
|
||||
{notification.level || 'info'}
|
||||
</span>
|
||||
<span className="rounded-full bg-canvas-base px-2.5 py-1 text-xs font-semibold text-text-muted">
|
||||
{meta.category}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-border-hairline bg-canvas-base p-3">
|
||||
<p className="text-sm font-semibold text-text-main">{notification.title || 'Obavijest'}</p>
|
||||
<p className="mt-2 text-sm text-text-muted">{notification.message || '-'}</p>
|
||||
{showPdfLink && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => action.run()}
|
||||
className="mt-2 text-sm font-medium text-indigo-600 underline underline-offset-2 hover:text-indigo-700"
|
||||
>
|
||||
{action.label}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border border-border-hairline bg-canvas-base p-3">
|
||||
<p className="text-xs uppercase tracking-wide text-text-muted">Preporuka</p>
|
||||
<p className="mt-1 text-sm text-text-main">{meta.recommendation}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2 border-t border-border-hairline pt-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
action.run();
|
||||
onClose?.();
|
||||
}}
|
||||
className="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700"
|
||||
>
|
||||
{action.label}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="rounded-lg border border-border-hairline px-4 py-2 text-sm font-medium text-text-main hover:bg-canvas-deep"
|
||||
>
|
||||
Zatvori
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalShell>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user