fix: filtriraj putne naloge i prosiri servisni zapis modal
Ogranicava prikaz 'Dohvati sve putne naloge' na naloge trenutnog servisera, dok supervisor bez serviser uloge i dalje vidi sve. U detaljima servisnog zapisa dodaje gumb za upload datoteka i povezuje ga s postojecim ServicePhotoUpload modalom. U modalu Novi servisni zapis dodaje polje Naziv servisnog zapisa iznad opisa i salje ga kao service_title. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -91,6 +91,13 @@ function formatCost(value) {
|
|||||||
return `${numeric.toFixed(2)} EUR`;
|
return `${numeric.toFixed(2)} EUR`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeComparableId(value) {
|
||||||
|
if (value && typeof value === 'object') {
|
||||||
|
return value.id ?? value.pk ?? '';
|
||||||
|
}
|
||||||
|
return value ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
const PAGE_MODE_TO_SECTION = {
|
const PAGE_MODE_TO_SECTION = {
|
||||||
dashboard: 'dashboard',
|
dashboard: 'dashboard',
|
||||||
'work-orders': 'work-orders',
|
'work-orders': 'work-orders',
|
||||||
@@ -344,14 +351,31 @@ export default function FleetDashboardShell({ initialSection = 'dashboard', page
|
|||||||
});
|
});
|
||||||
const scopedWorkOrders = useMemo(() => {
|
const scopedWorkOrders = useMemo(() => {
|
||||||
const sorted = [...workOrders].sort((a, b) => String(b.date || '').localeCompare(String(a.date || '')));
|
const sorted = [...workOrders].sort((a, b) => String(b.date || '').localeCompare(String(a.date || '')));
|
||||||
|
const isServiserUser = Boolean(user?.is_serviser);
|
||||||
|
const isSupervisorUser = Boolean(user?.is_team_member) && !Boolean(user?.is_serviser);
|
||||||
|
const currentUserId = normalizeComparableId(user?.id);
|
||||||
|
|
||||||
|
const roleScoped = sorted.filter((order) => {
|
||||||
|
if (!isServiserUser || isSupervisorUser) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!currentUserId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const creatorCandidates = [order?.creator, order?.creator_id];
|
||||||
|
return creatorCandidates.some((candidate) => (
|
||||||
|
String(normalizeComparableId(candidate)) === String(currentUserId)
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
if (workOrderScope === 'all') {
|
if (workOrderScope === 'all') {
|
||||||
return sorted;
|
return roleScoped;
|
||||||
}
|
}
|
||||||
if (!hydratedSelectedVehicleId) {
|
if (!hydratedSelectedVehicleId) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return sorted.filter((order) => String(order.vehicle) === String(hydratedSelectedVehicleId));
|
return roleScoped.filter((order) => String(order.vehicle) === String(hydratedSelectedVehicleId));
|
||||||
}, [workOrders, workOrderScope, hydratedSelectedVehicleId]);
|
}, [workOrders, workOrderScope, hydratedSelectedVehicleId, user?.id, user?.is_serviser, user?.is_team_member]);
|
||||||
const pageSize = 10;
|
const pageSize = 10;
|
||||||
const totalPages = Math.max(1, Math.ceil(scopedWorkOrders.length / pageSize));
|
const totalPages = Math.max(1, Math.ceil(scopedWorkOrders.length / pageSize));
|
||||||
const latestWorkOrders = useMemo(
|
const latestWorkOrders = useMemo(
|
||||||
@@ -1214,6 +1238,13 @@ export default function FleetDashboardShell({ initialSection = 'dashboard', page
|
|||||||
setSelectedServiceRecord(updatedRecord);
|
setSelectedServiceRecord(updatedRecord);
|
||||||
fetchFleetDashboardData({ silent: true });
|
fetchFleetDashboardData({ silent: true });
|
||||||
}}
|
}}
|
||||||
|
onOpenPhotoUpload={(record) => {
|
||||||
|
const targetId = record?.id ?? selectedServiceRecord?.id;
|
||||||
|
if (!targetId) return;
|
||||||
|
setSelectedServiceRecord(null);
|
||||||
|
setServiceRecordBackTask(null);
|
||||||
|
setPhotoUpload({ open: true, serviceRecordId: targetId });
|
||||||
|
}}
|
||||||
onBack={serviceRecordBackTask
|
onBack={serviceRecordBackTask
|
||||||
? () => {
|
? () => {
|
||||||
setSelectedServiceRecord(null);
|
setSelectedServiceRecord(null);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export default function ServiceRecordDetailModal({
|
|||||||
serviceRecord,
|
serviceRecord,
|
||||||
onClose,
|
onClose,
|
||||||
onUpdated,
|
onUpdated,
|
||||||
|
onOpenPhotoUpload = null,
|
||||||
onBack = null,
|
onBack = null,
|
||||||
backLabel = 'Natrag',
|
backLabel = 'Natrag',
|
||||||
}) {
|
}) {
|
||||||
@@ -411,6 +412,14 @@ export default function ServiceRecordDetailModal({
|
|||||||
)}
|
)}
|
||||||
{editMode && (
|
{editMode && (
|
||||||
<>
|
<>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => onOpenPhotoUpload?.(serviceRecord)}
|
||||||
|
disabled={editSaving}
|
||||||
|
className="rounded-md border border-border-hairline px-3 py-2 text-xs font-medium text-text-main hover:bg-canvas-deep disabled:opacity-60"
|
||||||
|
>
|
||||||
|
📎 Dodaj datoteke
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { showToast } from '../../stores/toastStore.js';
|
|||||||
|
|
||||||
const INITIAL = {
|
const INITIAL = {
|
||||||
task: '',
|
task: '',
|
||||||
|
service_title: '',
|
||||||
description: '',
|
description: '',
|
||||||
parts: '',
|
parts: '',
|
||||||
cost: '',
|
cost: '',
|
||||||
@@ -144,6 +145,7 @@ export default function ServiceRecordForm({
|
|||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
task: form.task,
|
task: form.task,
|
||||||
|
service_title: form.service_title.trim(),
|
||||||
description: form.description.trim(),
|
description: form.description.trim(),
|
||||||
parts: form.parts.trim(),
|
parts: form.parts.trim(),
|
||||||
cost,
|
cost,
|
||||||
@@ -268,6 +270,18 @@ export default function ServiceRecordForm({
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<label className="flex flex-col gap-1 text-sm">
|
||||||
|
<span className="font-medium text-text-main">Naziv servisnog zapisa</span>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={form.service_title}
|
||||||
|
onInput={field('service_title')}
|
||||||
|
disabled={disabled}
|
||||||
|
placeholder="Npr. Redovni servis hidraulike"
|
||||||
|
className="rounded-lg border border-border-hairline bg-canvas-base px-3 py-2 text-sm text-text-main placeholder-text-muted/40 focus:outline-none disabled:opacity-50"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
<label className="flex flex-col gap-1 text-sm">
|
<label className="flex flex-col gap-1 text-sm">
|
||||||
<span className="font-medium text-text-main">Opis posla *</span>
|
<span className="font-medium text-text-main">Opis posla *</span>
|
||||||
<textarea
|
<textarea
|
||||||
|
|||||||
Reference in New Issue
Block a user