From 3b15a438791867c7c626eaad02f4f6e0e99bb023 Mon Sep 17 00:00:00 2001 From: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> Date: Thu, 16 Apr 2026 18:27:00 -0400 Subject: [PATCH] Add interactive status dropdown on work order view page - Click the status badge to open a dropdown with all status options - Status change calls ChangeStatus API and updates locally - Color-coded status dots in the dropdown menu - Closes on outside click - Also logged in audit trail via backend ChangeStatus endpoint --- src/pages/workorders/api.js | 4 ++ .../workorders/view/WorkOrderViewPage.css | 69 +++++++++++++++++++ .../workorders/view/WorkOrderViewPage.js | 62 +++++++++++++++-- 3 files changed, 131 insertions(+), 4 deletions(-) diff --git a/src/pages/workorders/api.js b/src/pages/workorders/api.js index d0b19031..37b5e44e 100644 --- a/src/pages/workorders/api.js +++ b/src/pages/workorders/api.js @@ -78,6 +78,10 @@ export const addComment = async (workOrderId, text, commentType = "internal") => }); }; +export const changeWorkOrderStatus = async (id, status) => { + return post(`${API_URL}/WorkOrder/ChangeStatus?id=${id}&status=${encodeURIComponent(status)}`); +}; + export const deleteWorkOrder = async (id) => { try { await workOrderApi.delete(id); diff --git a/src/pages/workorders/view/WorkOrderViewPage.css b/src/pages/workorders/view/WorkOrderViewPage.css index 37c7357e..56130541 100644 --- a/src/pages/workorders/view/WorkOrderViewPage.css +++ b/src/pages/workorders/view/WorkOrderViewPage.css @@ -34,6 +34,75 @@ .woStatusBadge.done, .woStatusBadge.completed { background: #28a745; } .woStatusBadge.cancelled { background: #dc3545; } +.woStatusDropdown { + position: relative; + display: inline-block; +} + +.woStatusClickable { + cursor: pointer; + user-select: none; +} + +.woStatusClickable:hover { + filter: brightness(0.9); +} + +.woStatusCaret { + font-size: 10px; + margin-left: 4px; +} + +.woStatusMenu { + position: absolute; + top: 100%; + left: 0; + margin-top: 4px; + background: #fff; + border: 1px solid #ddd; + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0,0,0,0.12); + z-index: 100; + min-width: 160px; + overflow: hidden; +} + +.woStatusOption { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + padding: 8px 14px; + border: none; + background: none; + font-size: 13px; + color: #333; + cursor: pointer; + text-align: left; +} + +.woStatusOption:hover { + background: #f5f5f5; +} + +.woStatusOption.active { + font-weight: 600; + background: #f0f7ff; +} + +.woStatusDot { + width: 8px; + height: 8px; + border-radius: 50%; + flex-shrink: 0; +} + +.woStatusDot.open { background: #007bff; } +.woStatusDot.in-progress { background: #ffc107; } +.woStatusDot.on-hold { background: #fd7e14; } +.woStatusDot.done { background: #28a745; } +.woStatusDot.cancelled { background: #dc3545; } + .woViewActions { display: flex; gap: 8px; diff --git a/src/pages/workorders/view/WorkOrderViewPage.js b/src/pages/workorders/view/WorkOrderViewPage.js index eaa3a83e..960d8c8d 100644 --- a/src/pages/workorders/view/WorkOrderViewPage.js +++ b/src/pages/workorders/view/WorkOrderViewPage.js @@ -1,7 +1,7 @@ import React, { useState, useEffect, useRef } from "react"; import { useParams, useNavigate } from "react-router"; import { toast } from "react-toastify"; -import { getWorkOrderById, addComment } from "../api"; +import { getWorkOrderById, addComment, changeWorkOrderStatus } from "../api"; import "./WorkOrderViewPage.css"; function formatDateTime(value) { @@ -117,12 +117,68 @@ function CommentInput({ workOrderId, commentType, onCommentAdded }) { ); } +const STATUS_OPTIONS = ["Open", "In Progress", "On Hold", "Done", "Cancelled"]; + +function StatusDropdown({ status, onStatusChange }) { + const [open, setOpen] = useState(false); + const ref = useRef(null); + const statusClass = (status || "").toLowerCase().replace(/\s+/g, "-"); + + useEffect(() => { + const handleClick = (e) => { + if (ref.current && !ref.current.contains(e.target)) setOpen(false); + }; + document.addEventListener("mousedown", handleClick); + return () => document.removeEventListener("mousedown", handleClick); + }, []); + + return ( +