mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 08:03:13 +00:00
Add Verify & Close button and verified banner to dispatch detail
- Verify & Close button at bottom of dispatch modal - Disabled with tooltip showing missing items (checklist, signoffs) - Green verified banner after verification with verifier name + timestamp - Verified status badge (dark green) - Added Verified to DISPATCH_STATUSES constant - verifyDispatch API function
This commit is contained in:
parent
5c24b9249d
commit
199715d5b6
3 changed files with 78 additions and 2 deletions
|
|
@ -134,6 +134,10 @@ export const addChecklistItem = async (dispatchId, itemText, workOrderId) => {
|
|||
return post(`${API_URL}/WorkOrder/AddChecklistItem`, { DispatchId: dispatchId, ItemText: itemText, WorkOrderId: workOrderId });
|
||||
};
|
||||
|
||||
export const verifyDispatch = async (dispatchId) => {
|
||||
return post(`${API_URL}/WorkOrder/VerifyDispatch?dispatchId=${dispatchId}`);
|
||||
};
|
||||
|
||||
export const addDispatchSignoff = async (dispatchId, signoffType, name, signature, signatureMethod) => {
|
||||
return post(`${API_URL}/WorkOrder/AddDispatchSignoff`, {
|
||||
DispatchId: dispatchId,
|
||||
|
|
|
|||
|
|
@ -629,6 +629,7 @@
|
|||
.woDispatchStatus.acknowledged { background: #ffc107; color: #333; }
|
||||
.woDispatchStatus.completed { background: #28a745; }
|
||||
.woDispatchStatus.cancelled { background: #dc3545; }
|
||||
.woDispatchStatus.verified { background: #155724; }
|
||||
|
||||
.dispatchClickRow {
|
||||
cursor: pointer;
|
||||
|
|
@ -748,6 +749,34 @@
|
|||
font-weight: 600;
|
||||
}
|
||||
|
||||
.dispatchVerifiedBanner {
|
||||
padding: 12px 20px;
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
border-top: 1px solid #c3e6cb;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dispatchVerifyFooter {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 20px;
|
||||
border-top: 1px solid #eee;
|
||||
background: #fafafa;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.dispatchVerifyMissing {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dispatchSignoffs {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from "react";
|
|||
import ReactDOM from "react-dom";
|
||||
import { useParams, useNavigate } from "react-router";
|
||||
import { toast } from "react-toastify";
|
||||
import { getWorkOrderById, addComment, changeWorkOrderStatus, fetchUsers, changeWorkOrderAssignment, fetchVendorsDropdown, dispatchToVendor, getDispatchById, updateDispatch, addDispatchComment, fetchTaskTemplates, updateChecklistItem, addChecklistItem, addDispatchSignoff } from "../api";
|
||||
import { getWorkOrderById, addComment, changeWorkOrderStatus, fetchUsers, changeWorkOrderAssignment, fetchVendorsDropdown, dispatchToVendor, getDispatchById, updateDispatch, addDispatchComment, fetchTaskTemplates, updateChecklistItem, addChecklistItem, addDispatchSignoff, verifyDispatch } from "../api";
|
||||
import { getCurrentUser } from "../../../services/authService";
|
||||
import { timeSince, timeBetween, waitTimeColor } from "../../../utils/timeSince";
|
||||
import SignatureCapture from "../../../components/SignatureCapture";
|
||||
|
|
@ -164,7 +164,7 @@ function AuditTimeline({ entries }) {
|
|||
);
|
||||
}
|
||||
|
||||
const DISPATCH_STATUSES = ["Sent", "Acknowledged", "In Progress", "Completed", "Cancelled"];
|
||||
const DISPATCH_STATUSES = ["Sent", "Acknowledged", "In Progress", "Completed", "Verified", "Cancelled"];
|
||||
|
||||
function DispatchDetailModal({ dispatchId, onClose, onUpdated }) {
|
||||
const [dispatch, setDispatch] = useState(null);
|
||||
|
|
@ -468,6 +468,49 @@ function DispatchDetailModal({ dispatchId, onClose, onUpdated }) {
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{dispatch.verifiedAt ? (
|
||||
<div className="dispatchVerifiedBanner">
|
||||
✓ Verified by {dispatch.verifiedBy} on {formatDateTime(dispatch.verifiedAt)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="dispatchVerifyFooter">
|
||||
{(() => {
|
||||
const checklistTotal = (dispatch.checklistItems || []).length;
|
||||
const checklistDone = (dispatch.checklistItems || []).filter(i => i.isCompleted).length;
|
||||
const signoffCount = (dispatch.signoffs || []).length;
|
||||
const missing = [];
|
||||
if (checklistTotal > 0 && checklistDone < checklistTotal) missing.push(`${checklistTotal - checklistDone} checklist item(s)`);
|
||||
if (signoffCount < 2) {
|
||||
const types = (dispatch.signoffs || []).map(s => s.signoffType);
|
||||
if (!types.includes("Customer")) missing.push("Customer sign-off");
|
||||
if (!types.includes("Vendor")) missing.push("Vendor sign-off");
|
||||
}
|
||||
const canVerify = missing.length === 0 && (checklistTotal > 0 || signoffCount >= 2);
|
||||
return (
|
||||
<>
|
||||
{missing.length > 0 && <span className="dispatchVerifyMissing">Missing: {missing.join(", ")}</span>}
|
||||
<button
|
||||
className="btn btn-success"
|
||||
disabled={!canVerify}
|
||||
onClick={async () => {
|
||||
try {
|
||||
const result = await verifyDispatch(dispatchId);
|
||||
toast.success("Dispatch verified");
|
||||
loadDispatch();
|
||||
onUpdated();
|
||||
} catch (err) {
|
||||
toast.error(err.message || "Failed to verify");
|
||||
}
|
||||
}}
|
||||
>
|
||||
Verify & Close
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue