From 913d75802c540385031ada412e54a5ca7591425c Mon Sep 17 00:00:00 2001
From: Adam Moussa <166072409+amoussa1229@users.noreply.github.com>
Date: Fri, 17 Apr 2026 16:26:22 -0400
Subject: [PATCH] =?UTF-8?q?Polish=20dispatch=20workflow=20=E2=80=94=20cust?=
=?UTF-8?q?om=20items,=20name=20input,=20read-only=20after=20verify?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Custom checklist items in dispatch modal (type + Enter, removable chips)
- Add checklist item input in dispatch detail modal
- SignatureCapture now has proper Full Name field instead of prompt()
- Verified dispatches are read-only: edit hidden, checkboxes disabled, signoff buttons disabled
- Custom items sent as CustomChecklistItems in dispatch payload
---
src/components/SignatureCapture.css | 12 ++++
src/components/SignatureCapture.js | 17 ++++-
.../workorders/view/WorkOrderViewPage.css | 37 ++++++++++
.../workorders/view/WorkOrderViewPage.js | 69 ++++++++++++++++---
4 files changed, 122 insertions(+), 13 deletions(-)
diff --git a/src/components/SignatureCapture.css b/src/components/SignatureCapture.css
index 68a3236e..15f281f2 100644
--- a/src/components/SignatureCapture.css
+++ b/src/components/SignatureCapture.css
@@ -40,6 +40,18 @@
border-bottom: 2px solid #e5e5e5;
}
+.sigNameField {
+ margin-bottom: 12px;
+}
+
+.sigNameField label {
+ display: block;
+ font-size: 12px;
+ font-weight: 600;
+ color: #666;
+ margin-bottom: 4px;
+}
+
.sigModeTab {
padding: 6px 16px;
border: none;
diff --git a/src/components/SignatureCapture.js b/src/components/SignatureCapture.js
index ef51ebf5..dc9e5ac0 100644
--- a/src/components/SignatureCapture.js
+++ b/src/components/SignatureCapture.js
@@ -4,6 +4,7 @@ import ReactDOM from "react-dom";
export default function SignatureCapture({ onSave, onClose, title }) {
const [mode, setMode] = useState("draw");
const [typedName, setTypedName] = useState("");
+ const [signerName, setSignerName] = useState("");
const canvasRef = useRef(null);
const isDrawing = useRef(false);
@@ -52,11 +53,12 @@ export default function SignatureCapture({ onSave, onClose, title }) {
};
const handleSave = () => {
+ if (!signerName.trim()) return;
if (mode === "draw") {
const dataUrl = canvasRef.current.toDataURL("image/png");
- onSave(dataUrl, "drawn");
+ onSave(dataUrl, "drawn", signerName.trim());
} else {
- onSave(typedName, "typed");
+ onSave(typedName, "typed", signerName.trim());
}
};
@@ -68,6 +70,15 @@ export default function SignatureCapture({ onSave, onClose, title }) {
+
+
+ setSignerName(e.target.value)}
+ placeholder="Enter signer's full name"
+ />
+
@@ -103,7 +114,7 @@ export default function SignatureCapture({ onSave, onClose, title }) {
-
+
,
diff --git a/src/pages/workorders/view/WorkOrderViewPage.css b/src/pages/workorders/view/WorkOrderViewPage.css
index 7747a645..2762e0e9 100644
--- a/src/pages/workorders/view/WorkOrderViewPage.css
+++ b/src/pages/workorders/view/WorkOrderViewPage.css
@@ -803,6 +803,43 @@
border-bottom: none;
}
+.dispatchChecklistAdd {
+ margin-top: 8px;
+}
+
+.dispatchChecklistAdd .form-control {
+ font-size: 13px;
+}
+
+.dispatchCustomItems {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin-top: 6px;
+}
+
+.dispatchCustomItem {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 4px 8px;
+ background: #f8f9fa;
+ border-radius: 4px;
+ font-size: 12px;
+}
+
+.dispatchCustomItem button {
+ background: none;
+ border: none;
+ color: #999;
+ cursor: pointer;
+ font-size: 14px;
+}
+
+.dispatchCustomItem button:hover {
+ color: #dc3545;
+}
+
.dispatchVerifiedBanner {
padding: 12px 20px;
background: #d4edda;
diff --git a/src/pages/workorders/view/WorkOrderViewPage.js b/src/pages/workorders/view/WorkOrderViewPage.js
index ac053183..11918238 100644
--- a/src/pages/workorders/view/WorkOrderViewPage.js
+++ b/src/pages/workorders/view/WorkOrderViewPage.js
@@ -170,6 +170,7 @@ function DispatchDetailModal({ dispatchId, onClose, onUpdated }) {
const [dispatch, setDispatch] = useState(null);
const [loading, setLoading] = useState(true);
const [editing, setEditing] = useState(false);
+ const [newChecklistItem, setNewChecklistItem] = useState("");
const [form, setForm] = useState({});
const [saving, setSaving] = useState(false);
const [commentText, setCommentText] = useState("");
@@ -336,7 +337,7 @@ function DispatchDetailModal({ dispatchId, onClose, onUpdated }) {
)}
-
+ {!dispatch.verifiedAt && }
>
)}
@@ -344,15 +345,16 @@ function DispatchDetailModal({ dispatchId, onClose, onUpdated }) {
- {dispatch.checklistItems && dispatch.checklistItems.length > 0 && (
+ {((dispatch.checklistItems && dispatch.checklistItems.length > 0) || !dispatch.verifiedAt) && (
<>
-
Checklist ({dispatch.checklistItems.filter(i => i.isCompleted).length}/{dispatch.checklistItems.length})
+
Checklist ({(dispatch.checklistItems || []).filter(i => i.isCompleted).length}/{(dispatch.checklistItems || []).length})
- {dispatch.checklistItems.map((item) => (
+ {(dispatch.checklistItems || []).map((item) => (
{
const currentUser = getCurrentUser();
const userName = currentUser?.fullname || "User";
@@ -374,6 +376,27 @@ function DispatchDetailModal({ dispatchId, onClose, onUpdated }) {
))}
+ {!dispatch.verifiedAt && (
+
+ setNewChecklistItem(e.target.value)}
+ onKeyDown={async (e) => {
+ if (e.key === "Enter" && newChecklistItem.trim()) {
+ e.preventDefault();
+ const result = await addChecklistItem(dispatchId, newChecklistItem.trim());
+ setDispatch((prev) => ({
+ ...prev,
+ checklistItems: [...(prev.checklistItems || []), { ...result, isCompleted: false }],
+ }));
+ setNewChecklistItem("");
+ }
+ }}
+ placeholder="Add checklist item..."
+ />
+
+ )}
>
)}
Sign-offs
@@ -394,8 +417,8 @@ function DispatchDetailModal({ dispatchId, onClose, onUpdated }) {
{signoff.name} ยท {signoff.signedAt ? formatDateTime(signoff.signedAt) : ""}
) : (
-