mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-03 19:03:15 +00:00
Add time tracking to dispatch workflow
- timeSince utility: relative time display (2h ago, 3d ago) - timeBetween utility: duration between two timestamps - waitTimeColor utility: green (<4h), yellow (4-24h), red (>24h) - Dispatch detail modal shows accepted time with duration - Awaiting acceptance shows elapsed time with color coding - Dispatches table shows Wait column with color-coded time for Sent dispatches
This commit is contained in:
parent
5dcfc9d303
commit
31479029e3
3 changed files with 52 additions and 4 deletions
|
|
@ -735,11 +735,19 @@
|
|||
}
|
||||
|
||||
.dispatchWaiting {
|
||||
color: #ffc107;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.dispatchWait-green { color: #28a745; }
|
||||
.dispatchWait-yellow { color: #ffc107; }
|
||||
.dispatchWait-red { color: #dc3545; }
|
||||
|
||||
.dispatchWaitDot {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.dispatchChecklist {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ 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 } from "../api";
|
||||
import { getCurrentUser } from "../../../services/authService";
|
||||
import { timeSince, timeBetween, waitTimeColor } from "../../../utils/timeSince";
|
||||
import "./WorkOrderViewPage.css";
|
||||
|
||||
function AssignDropdown({ value, onAssign }) {
|
||||
|
|
@ -314,9 +315,9 @@ function DispatchDetailModal({ dispatchId, onClose, onUpdated }) {
|
|||
<div className="dispatchDetailField">
|
||||
<label>Accepted</label>
|
||||
{dispatch.acknowledgedAt ? (
|
||||
<span style={{ color: "#28a745" }}>{formatDateTime(dispatch.acknowledgedAt)}</span>
|
||||
<span style={{ color: "#28a745" }}>{formatDateTime(dispatch.acknowledgedAt)} (after {timeBetween(dispatch.dispatchedAt, dispatch.acknowledgedAt)})</span>
|
||||
) : dispatch.status === "Sent" ? (
|
||||
<span className="dispatchWaiting">Awaiting acceptance</span>
|
||||
<span className={`dispatchWaiting dispatchWait-${waitTimeColor(dispatch.dispatchedAt)}`}>Awaiting acceptance · {timeSince(dispatch.dispatchedAt)}</span>
|
||||
) : (
|
||||
<span>—</span>
|
||||
)}
|
||||
|
|
@ -883,7 +884,7 @@ export default function WorkOrderViewPage() {
|
|||
{wo.dispatches?.length ? (
|
||||
<table className="woDispatchTable">
|
||||
<thead>
|
||||
<tr><th>Dispatch #</th><th>Vendor</th><th>NTE</th><th>Status</th><th>Date</th></tr>
|
||||
<tr><th>Dispatch #</th><th>Vendor</th><th>NTE</th><th>Status</th><th>Date</th><th>Wait</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{wo.dispatches.map(d => (
|
||||
|
|
@ -893,6 +894,7 @@ export default function WorkOrderViewPage() {
|
|||
<td>{d.nteAmount != null ? `$${parseFloat(d.nteAmount).toFixed(2)}` : "—"}</td>
|
||||
<td><span className={`woDispatchStatus ${(d.status || "").toLowerCase()}`}>{d.status}</span></td>
|
||||
<td>{d.dispatchedAt ? formatDateTime(d.dispatchedAt) : "—"}</td>
|
||||
<td>{d.status === "Sent" ? <span className={`dispatchWaitDot dispatchWait-${waitTimeColor(d.dispatchedAt)}`}>{timeSince(d.dispatchedAt)}</span> : "—"}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
|
|
|||
38
src/utils/timeSince.js
Normal file
38
src/utils/timeSince.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
export function timeSince(dateString) {
|
||||
if (!dateString) return "";
|
||||
const d = new Date(dateString.endsWith("Z") ? dateString : dateString + "Z");
|
||||
if (isNaN(d)) return "";
|
||||
const now = new Date();
|
||||
const diffMs = now - d;
|
||||
const mins = Math.floor(diffMs / 60000);
|
||||
if (mins < 1) return "just now";
|
||||
if (mins < 60) return `${mins}m ago`;
|
||||
const hours = Math.floor(mins / 60);
|
||||
if (hours < 24) return `${hours}h ago`;
|
||||
const days = Math.floor(hours / 24);
|
||||
if (days < 7) return `${days}d ago`;
|
||||
return `${Math.floor(days / 7)}w ago`;
|
||||
}
|
||||
|
||||
export function timeBetween(startStr, endStr) {
|
||||
if (!startStr || !endStr) return "";
|
||||
const start = new Date(startStr.endsWith("Z") ? startStr : startStr + "Z");
|
||||
const end = new Date(endStr.endsWith("Z") ? endStr : endStr + "Z");
|
||||
if (isNaN(start) || isNaN(end)) return "";
|
||||
const diffMs = end - start;
|
||||
const mins = Math.floor(diffMs / 60000);
|
||||
if (mins < 60) return `${mins}m`;
|
||||
const hours = Math.floor(mins / 60);
|
||||
if (hours < 24) return `${hours}h`;
|
||||
const days = Math.floor(hours / 24);
|
||||
return `${days}d`;
|
||||
}
|
||||
|
||||
export function waitTimeColor(dateString) {
|
||||
if (!dateString) return "";
|
||||
const d = new Date(dateString.endsWith("Z") ? dateString : dateString + "Z");
|
||||
const hours = (new Date() - d) / 3600000;
|
||||
if (hours < 4) return "green";
|
||||
if (hours < 24) return "yellow";
|
||||
return "red";
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue