fix(work-orders): allow read-only open for canceled board rows

This commit is contained in:
Arthur Bassi 2026-07-22 10:18:27 -03:00 • committed by Alexandre Brandizzi
parent 32609659b7
commit fef7918f6b
2 changed files with 83 additions and 43 deletions

View file

@ -3,7 +3,7 @@ import { Eye, Pencil } from "lucide-react";
type WoTableRowActionsProps = {
onView: () => void;
onEdit: () => void;
onEdit?: () => void;
};
export function WoTableRowActions({ onView, onEdit }: WoTableRowActionsProps) {
@ -61,7 +61,7 @@ export function WoTableRowActions({ onView, onEdit }: WoTableRowActionsProps) {
<div className="flex items-center justify-end gap-1 opacity-0 transition-opacity group-hover/row:opacity-100">
{mk(Eye, "View details", onView, "#e6f4fb", "var(--primary)")}
{mk(Pencil, "Edit", onEdit, "#e6f4fb", "var(--primary)")}
{onEdit ? mk(Pencil, "Edit", onEdit, "#e6f4fb", "var(--primary)") : null}
</div>
);
}

View file

@ -85,23 +85,37 @@ export function WoTableRow({
}
const patchAndMaybeAuto = (patch: Partial<WorkOrderTableRow>) => {
if (isCanceled) return;
const merged = { ...row, ...patch };
const auto = maybeAutoSchedulePatch(merged);
handlers.onPatch(row.id, { ...patch, ...auto });
};
const patchRow = (patch: Partial<WorkOrderTableRow>) => {
if (isCanceled) return;
handlers.onPatch(row.id, patch);
};
/** Block mutation controls on canceled rows while keeping row/View clickable. */
const mutationCellStyle = isCanceled ? ({ pointerEvents: "none" } as const) : undefined;
return (
<tr
id={`wo-row-${row.id}`}
draggable={!isCanceled}
title={isCanceled ? "This work order has been cancelled" : undefined}
onClick={() => handlers.onRowClick(row)}
onDragStart={() => handlers.onDragStart(row.id)}
onDragStart={() => {
if (isCanceled) return;
handlers.onDragStart(row.id);
}}
onDragOver={(e) => {
if (isCanceled) return;
e.preventDefault();
handlers.onDragOver(row.id);
}}
onDrop={(e) => {
if (isCanceled) return;
e.preventDefault();
handlers.onDrop();
}}
@ -112,7 +126,6 @@ export function WoTableRow({
borderBottom: "1px solid var(--border)",
borderTop: isDropTarget ? "2px solid var(--color-primary)" : undefined,
opacity: isCanceled ? 0.45 : isDragging ? 0.4 : 1,
pointerEvents: isCanceled ? "none" : undefined,
background: isFlashing || isSelected ? "var(--color-primary-tint)" : "var(--card)",
outline: isFlashing ? "2px solid var(--color-primary)" : "none",
outlineOffset: isFlashing ? "-2px" : undefined,
@ -131,23 +144,34 @@ export function WoTableRow({
}}
>
<td
style={{ padding: "0 0 0 6px", boxShadow: firstCellShadow }}
style={{ padding: "0 0 0 6px", boxShadow: firstCellShadow, ...mutationCellStyle }}
onClick={(e) => e.stopPropagation()}
>
<span
className="flex items-center justify-center"
style={{
cursor: isDragging ? "grabbing" : "grab",
cursor: isCanceled ? "default" : isDragging ? "grabbing" : "grab",
color: "var(--color-text-muted)",
}}
>
<GripVertical size={14} />
</span>
</td>
<td style={{ padding: "0 2px 0 8px" }} onClick={(e) => e.stopPropagation()}>
<FlagCell color={flagColor} onChange={(c) => handlers.setFlag(row.id, c)} />
<td
style={{ padding: "0 2px 0 8px", ...mutationCellStyle }}
onClick={(e) => e.stopPropagation()}
>
<FlagCell
color={flagColor}
disabled={isCanceled}
onChange={(c) => handlers.setFlag(row.id, c)}
/>
</td>
<td className="group/cell" style={{ padding: "0 14px" }} onClick={(e) => e.stopPropagation()}>
<td
className="group/cell"
style={{ padding: "0 14px", ...mutationCellStyle }}
onClick={(e) => e.stopPropagation()}
>
<SiteCell
value={row.site}
location={row.location}
@ -158,7 +182,7 @@ export function WoTableRow({
sites={sites}
q={search}
onSave={(p) =>
handlers.onPatch(row.id, {
patchRow({
site: p.site,
location: p.location,
locationId: p.locationId,
@ -169,7 +193,7 @@ export function WoTableRow({
}
/>
</td>
<td style={{ padding: "0 14px", whiteSpace: "nowrap" }}>
<td style={{ padding: "0 14px", whiteSpace: "nowrap", ...mutationCellStyle }}>
<EditableWONumberCell
value={row.woNumber}
rescheduleCount={row.rescheduleCount}
@ -177,18 +201,22 @@ export function WoTableRow({
q={search}
currentId={row.id}
allOrders={allRows}
onSave={(v) => handlers.onPatch(row.id, { woNumber: v })}
onSave={(v) => patchRow({ woNumber: v })}
onDuplicateFound={handlers.onDuplicateFound}
/>
</td>
<td className="group/cell" style={{ padding: "0 14px" }} onClick={(e) => e.stopPropagation()}>
<TypeCell
value={row.type}
q={search}
onSave={(t) => handlers.onPatch(row.id, { type: t })}
/>
<td
className="group/cell"
style={{ padding: "0 14px", ...mutationCellStyle }}
onClick={(e) => e.stopPropagation()}
>
<TypeCell value={row.type} q={search} onSave={(t) => patchRow({ type: t })} />
</td>
<td className="group/cell" style={{ padding: "0 14px" }} onClick={(e) => e.stopPropagation()}>
<td
className="group/cell"
style={{ padding: "0 14px", ...mutationCellStyle }}
onClick={(e) => e.stopPropagation()}
>
<AssignedToCell
dispatcherId={row.dispatcherId}
users={users}
@ -196,7 +224,11 @@ export function WoTableRow({
onSave={(id, name) => patchAndMaybeAuto({ dispatcherId: id, dispatcherName: name })}
/>
</td>
<td className="group/cell" style={{ padding: "0 14px" }} onClick={(e) => e.stopPropagation()}>
<td
className="group/cell"
style={{ padding: "0 14px", ...mutationCellStyle }}
onClick={(e) => e.stopPropagation()}
>
<DateCell
value={row.scheduledOn}
q={search}
@ -215,22 +247,25 @@ export function WoTableRow({
}
/>
</td>
<td className="group/cell" style={{ padding: "0 14px" }} onClick={(e) => e.stopPropagation()}>
<DateCell
value={row.dueDate}
q={search}
onSave={(date) => handlers.onPatch(row.id, { dueDate: date })}
/>
<td
className="group/cell"
style={{ padding: "0 14px", ...mutationCellStyle }}
onClick={(e) => e.stopPropagation()}
>
<DateCell value={row.dueDate} q={search} onSave={(date) => patchRow({ dueDate: date })} />
</td>
<td className="group/cell" style={{ padding: "0 14px" }} onClick={(e) => e.stopPropagation()}>
<PMTypeCell
value={row.pm}
pmTypes={pmTypes}
q={search}
onSave={(pm) => handlers.onPatch(row.id, { pm })}
/>
<td
className="group/cell"
style={{ padding: "0 14px", ...mutationCellStyle }}
onClick={(e) => e.stopPropagation()}
>
<PMTypeCell value={row.pm} pmTypes={pmTypes} q={search} onSave={(pm) => patchRow({ pm })} />
</td>
<td className="group/cell" style={{ padding: "0 14px" }} onClick={(e) => e.stopPropagation()}>
<td
className="group/cell"
style={{ padding: "0 14px", ...mutationCellStyle }}
onClick={(e) => e.stopPropagation()}
>
<VendorCell
company={row.company}
tech={row.tech}
@ -238,7 +273,7 @@ export function WoTableRow({
vendors={vendors}
q={search}
onSave={(p) =>
handlers.onPatch(row.id, {
patchRow({
company: p.company,
tech: p.tech,
techPhone: p.techPhone,
@ -246,16 +281,21 @@ export function WoTableRow({
}
/>
</td>
<td className="group/cell" style={{ padding: "0 14px" }} onClick={(e) => e.stopPropagation()}>
<ApptTimeCell
apptTime={row.apptTime}
onSave={(v) => handlers.onPatch(row.id, { apptTime: v })}
/>
<td
className="group/cell"
style={{ padding: "0 14px", ...mutationCellStyle }}
onClick={(e) => e.stopPropagation()}
>
<ApptTimeCell apptTime={row.apptTime} onSave={(v) => patchRow({ apptTime: v })} />
</td>
<td className="group/cell" style={{ padding: "0 14px" }} onClick={(e) => e.stopPropagation()}>
<td
className="group/cell"
style={{ padding: "0 14px", ...mutationCellStyle }}
onClick={(e) => e.stopPropagation()}
>
<StatusCell
wo={row}
onChangeStatus={(status) => handlers.onPatch(row.id, { status })}
onChangeStatus={(status) => patchRow({ status })}
onOpenSlideOver={() => handlers.onOpenSlideOver(row)}
onPastDueAttempt={() => handlers.onPastDueAttempt?.(row.id)}
/>
@ -272,7 +312,7 @@ export function WoTableRow({
<td style={{ padding: "0 8px" }} onClick={(e) => e.stopPropagation()}>
<WoTableRowActions
onView={() => handlers.onView(row)}
onEdit={() => handlers.onEdit(row)}
onEdit={isCanceled ? undefined : () => handlers.onEdit(row)}
/>
</td>
</tr>