258 lines
8.6 KiB
JavaScript
258 lines
8.6 KiB
JavaScript
import React, { useEffect, useRef } from "react";
|
|
import { useServiceProjectJobDetails } from "../../../hooks/useServiceProject";
|
|
import { SpinnerLoader } from "../../common/Loader";
|
|
import Error from "../../common/Error";
|
|
import { formatUTCToLocalTime } from "../../../utils/dateUtils";
|
|
import Avatar from "../../common/Avatar";
|
|
import EmployeeAvatarGroup from "../../common/EmployeeAvatarGroup";
|
|
import { daysLeft, getJobStatusBadge } from "../../../utils/appUtils";
|
|
import HoverPopup from "../../common/HoverPopup";
|
|
import ChangeStatus from "./ChangeStatus";
|
|
import { useParams } from "react-router-dom";
|
|
import { STATUS_JOB_CLOSED } from "../../../utils/constants";
|
|
import Tooltip from "../../common/Tooltip";
|
|
import BranchDetails from "../ServiceProjectBranch/BranchDetails";
|
|
import { JobDetailsSkeleton } from "../ServiceProjectSeketon";
|
|
import JobComments from "./JobComments";
|
|
import JobStatusLog from "./JobStatusLog";
|
|
|
|
const ManageJobTicket = ({ Job }) => {
|
|
const { projectId } = useParams();
|
|
const { data, isLoading, isError, error } = useServiceProjectJobDetails(
|
|
Job?.job
|
|
);
|
|
const drawerRef = useRef();
|
|
const tabsData = [
|
|
{
|
|
id: "comment",
|
|
title: "Comments",
|
|
icon: "bx bx-comment me-2",
|
|
active: true,
|
|
content: <JobComments data={data} />,
|
|
},
|
|
{
|
|
id: "history",
|
|
title: "History",
|
|
icon: "bx bx-time me-2",
|
|
active: false,
|
|
content: <JobStatusLog data={data?.updateLogs} />,
|
|
},
|
|
];
|
|
|
|
if (isLoading) return <JobDetailsSkeleton />;
|
|
if (isError)
|
|
return (
|
|
<div>
|
|
<Error error={error} />
|
|
</div>
|
|
);
|
|
return (
|
|
<div
|
|
className=" text-start position-relative"
|
|
ref={drawerRef}
|
|
style={{ overflow: "visible" }}
|
|
>
|
|
<div className="col-12">
|
|
<h6 className="fs-5 fw-semibold">{data?.title}</h6>
|
|
<div className="d-flex justify-content-between align-items-start flex-wrap mb-2">
|
|
<p className="mb-0">
|
|
<span className="fw-medium me-1">Job Id :</span>
|
|
{data?.jobTicketUId || "N/A"}
|
|
</p>
|
|
<div className="d-flex flex-column align-items-end gap-3 mb-3">
|
|
<div className="d-flex flex-row gap-2 position-relative">
|
|
<span className={`badge ${getJobStatusBadge(data?.status?.id)}`}>
|
|
{data?.status?.displayName}
|
|
</span>
|
|
{STATUS_JOB_CLOSED !== data?.status?.id && (
|
|
<HoverPopup
|
|
id="STATUS_CHANEG"
|
|
Mode="click"
|
|
className=""
|
|
align="left"
|
|
content={
|
|
<ChangeStatus
|
|
statusId={data?.status?.id}
|
|
projectId={projectId}
|
|
jobId={Job?.job}
|
|
popUpId="STATUS_CHANEG"
|
|
/>
|
|
}
|
|
>
|
|
<Tooltip
|
|
text={"Change Status"}
|
|
placement="left"
|
|
children={
|
|
<i className="bx bx-edit bx-sm cursor-pointer"></i>
|
|
}
|
|
/>
|
|
</HoverPopup>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="d-flex flex-wrap">
|
|
<p>{data?.description || "N/A"}</p>
|
|
</div>
|
|
|
|
<div className="d-flex justify-content-between align-items-center mb-4">
|
|
<div className="d-flex flex-row gap-1 text-secondry">
|
|
<i className="bx bx-calendar"></i>{" "}
|
|
<span>
|
|
Created Date : {formatUTCToLocalTime(data?.createdAt, true)}
|
|
</span>
|
|
</div>
|
|
<div className="d-flex flex-row gap-2 text-wraps">
|
|
{data?.tags?.map((tag, ind) => (
|
|
<span
|
|
key={`${ind}0-${tag?.name}`}
|
|
className="badge bg-label-primary"
|
|
>
|
|
{tag?.name}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="d-flex justify-content-md-between ">
|
|
<div className="d-flex flex-row gap-5">
|
|
<span className="text-secondry">
|
|
<i className="bx bx-calendar"></i> Start Date :{" "}
|
|
{formatUTCToLocalTime(data?.startDate)}
|
|
</span>{" "}
|
|
<i className="bx bx-right-arrow-alt"></i>{" "}
|
|
<span className="text-secondry">
|
|
<i className="bx bx-calendar"></i> Due on :{" "}
|
|
{formatUTCToLocalTime(data?.startDate)}
|
|
</span>
|
|
</div>
|
|
{data?.dueDate &&
|
|
(() => {
|
|
const { days, color } = daysLeft(data?.startDate, data?.dueDate);
|
|
return (
|
|
<span>
|
|
<span className="text-secondry me-1">Days Left:</span>
|
|
<span className={`badge bg-${color}`}>
|
|
{days !== null ? `${days} days` : "N/A"}
|
|
</span>
|
|
</span>
|
|
);
|
|
})()}
|
|
</div>
|
|
{data?.projectBranch && (
|
|
<div className="d-flex gap-3 my-2 position-relative" ref={drawerRef} >
|
|
<span className="text-secondary">
|
|
<i className="bx bx-buildings"></i> Branch Name:
|
|
</span>
|
|
|
|
<HoverPopup
|
|
id="BRANCH_DETAILS"
|
|
Mode="click"
|
|
align="right"
|
|
minWidth="340px"
|
|
boundaryRef={drawerRef}
|
|
content={<BranchDetails branch={data?.projectBranch?.id} />}
|
|
>
|
|
<span className="text-decoration-underline cursor-pointer">
|
|
{data?.projectBranch?.branchName}
|
|
</span>
|
|
</HoverPopup>
|
|
</div>
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div className="border-top my-1">
|
|
<p className="m-0 py-1">
|
|
<i className="bx bx-group"></i> Peoples
|
|
</p>
|
|
|
|
{/* Created By */}
|
|
<div className="d-flex justify-content-between align-items-start w-100">
|
|
<p className="text-secondary m-0 me-3">Created By</p>
|
|
|
|
<div className="flex-grow-1 d-flex align-items-center gap-2">
|
|
<Avatar
|
|
size="xs"
|
|
firstName={data?.createdBy?.firstName}
|
|
lastName={data?.createdBy?.lastName}
|
|
/>
|
|
<div className="d-flex flex-column">
|
|
<p className="m-0 text-truncate">
|
|
{data?.createdBy?.firstName} {data?.createdBy?.lastName}
|
|
</p>
|
|
<small className="text-secondary text-xs">
|
|
{data?.createdBy?.jobRoleName}
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Assigned To */}
|
|
<div className="d-flex flex-column flex-md-row align-items-start w-100 mt-2">
|
|
<p className="text-secondary m-0 me-3">Assigned To</p>
|
|
|
|
<div className="flex-grow-1">
|
|
<div className="d-flex flex-wrap gap-3">
|
|
{data?.assignees?.map((emp) => (
|
|
<div key={emp.id} className="d-flex align-items-center">
|
|
<Avatar
|
|
size="xs"
|
|
firstName={emp.firstName}
|
|
lastName={emp.lastName}
|
|
/>
|
|
|
|
<div className="d-flex flex-column ms-2 text-truncate">
|
|
<span className="text-truncate">
|
|
{emp.firstName} {emp.lastName}
|
|
</span>
|
|
<small className="text-secondary text-xs text-truncate">
|
|
{emp.jobRoleName}
|
|
</small>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="nav-align-top nav-tabs-shadow p-0 shadow-none">
|
|
<ul className="nav nav-tabs" role="tablist">
|
|
{tabsData.map((tab) => (
|
|
<li className="nav-item" key={tab.id}>
|
|
<button
|
|
type="button"
|
|
className={`nav-link ${tab.active ? "active" : ""}`}
|
|
data-bs-toggle="tab"
|
|
data-bs-target={`#tab-${tab.id}`}
|
|
role="tab"
|
|
>
|
|
<i className={tab.icon} /> {tab.title}
|
|
</button>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<div className="tab-content p-1 px-sm-3">
|
|
{tabsData.map((tab) => (
|
|
<div
|
|
key={tab.id}
|
|
className={`tab-pane fade ${tab.active ? "show active" : ""}`}
|
|
id={`tab-${tab.id}`}
|
|
role="tabpanel"
|
|
>
|
|
{tab.content}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ManageJobTicket;
|