added mutiple contact person inside branch
This commit is contained in:
parent
d167c57ab0
commit
047e563505
@ -2,15 +2,19 @@ import React, { useState } from "react";
|
|||||||
import HorizontalBarChart from "../Charts/HorizontalBarChart";
|
import HorizontalBarChart from "../Charts/HorizontalBarChart";
|
||||||
import { useProjects } from "../../hooks/useProjects";
|
import { useProjects } from "../../hooks/useProjects";
|
||||||
import { ITEMS_PER_PAGE } from "../../utils/constants";
|
import { ITEMS_PER_PAGE } from "../../utils/constants";
|
||||||
|
import { useProjectCompletionStatus } from "../../hooks/useDashboard_Data";
|
||||||
|
|
||||||
const ProjectCompletionChart = () => {
|
const ProjectCompletionChart = () => {
|
||||||
|
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const { data: projects, isLoading: loading, isError, error } = useProjects(50,currentPage);
|
const {
|
||||||
// Bar chart logic
|
data: projects,
|
||||||
const projectNames = projects?.data?.map((p) => p.name) || [];
|
isLoading: loading,
|
||||||
|
isError,
|
||||||
|
error,
|
||||||
|
} = useProjectCompletionStatus();
|
||||||
|
const projectNames = projects?.map((p) => p.name) || [];
|
||||||
const projectProgress =
|
const projectProgress =
|
||||||
projects?.data?.map((p) => {
|
projects?.map((p) => {
|
||||||
const completed = p.completedWork || 0;
|
const completed = p.completedWork || 0;
|
||||||
const planned = p.plannedWork || 1;
|
const planned = p.plannedWork || 1;
|
||||||
const percent = planned ? (completed / planned) * 100 : 0;
|
const percent = planned ? (completed / planned) * 100 : 0;
|
||||||
|
|||||||
@ -27,59 +27,34 @@ const BranchDetails = ({ branch }) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div className="">
|
<>
|
||||||
<div className="d-flex mb-2">
|
<div className="d-flex mb-2">
|
||||||
<span className="fs-6 fw-medium">
|
<span className="fs-6 fw-medium">
|
||||||
<i className="bx bx-sm me-2 bx-buildings"></i>Branch Details
|
<i className="bx bx-sm me-2 bx-buildings"></i>Branch Details
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="row mb-1">
|
<div className="row mb-1">
|
||||||
<div className="col-4 col-md-4 text-secondary">Name:</div>
|
<div className="col-4 text-secondary">Contact No:</div>
|
||||||
<div className="col-8 col-md-8">{data?.branchName}</div>
|
<div className="col-8">{data?.contactInformation}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="row mb-1">
|
<div className="row mb-1">
|
||||||
<div className="col-4 col-md-4 text-secondary">Type:</div>
|
<div className="col-4 text-secondary">Type:</div>
|
||||||
<div className="col-8 col-md-8">{data?.branchType}</div>
|
<div className="col-8">{data?.branchType}</div>
|
||||||
</div>
|
|
||||||
<div className="row mb-1">
|
|
||||||
<div className="col-4 col-md-4 text-secondary">Contact No:</div>
|
|
||||||
<div className="col-8 col-md-8">{data?.contactInformation}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="row mb-1">
|
<div className="row mb-1">
|
||||||
<div className="col-4 col-md-4 text-secondary">Address:</div>
|
<div className="col-4 text-secondary">Email:</div>
|
||||||
<div className="col-8 col-md-8">{data?.address}</div>
|
<div className="col-8">{data?.email}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{googleMapUrl && (
|
|
||||||
<div className="row mb-1">
|
<div className="row mb-1">
|
||||||
<div className="col-4 col-md-4 text-secondary">Map:</div>
|
<div className="col-4 text-secondary">Address:</div>
|
||||||
|
<div className="col-8">{data?.address}</div>
|
||||||
<div className="col-8 col-md-8 d-flex align-items-center gap-2">
|
|
||||||
<a
|
|
||||||
href={googleMapUrl}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="text-primary text-decoration-underline text-break"
|
|
||||||
style={{ wordBreak: "break-all" }}
|
|
||||||
>
|
|
||||||
Open in Google Maps
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<i
|
|
||||||
className={`bx ${
|
|
||||||
copied ? "bx-check-double text-secondry " : "bx-copy"
|
|
||||||
}`}
|
|
||||||
style={{ cursor: "pointer" }}
|
|
||||||
onClick={handleCopy}
|
|
||||||
></i>
|
|
||||||
|
|
||||||
{copied && <span className="text-secondry small">Copied!</span>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,6 @@ const ServiceBranch = () => {
|
|||||||
|
|
||||||
const { data, isLoading, isError, error } = useBranches(
|
const { data, isLoading, isError, error } = useBranches(
|
||||||
projectId,
|
projectId,
|
||||||
// true,
|
|
||||||
!showInactive,
|
!showInactive,
|
||||||
ITEMS_PER_PAGE - 10,
|
ITEMS_PER_PAGE - 10,
|
||||||
currentPage,
|
currentPage,
|
||||||
@ -78,18 +77,17 @@ const ServiceBranch = () => {
|
|||||||
<div className="card-datatable" id="payment-request-table">
|
<div className="card-datatable" id="payment-request-table">
|
||||||
{/* Header Section */}
|
{/* Header Section */}
|
||||||
<div className="row align-items-center justify-content-between mt-3 mx-1">
|
<div className="row align-items-center justify-content-between mt-3 mx-1">
|
||||||
<div className="col-md-6 col-sm-12 ms-n3 text-start ">
|
<div className="col-md-4 col-sm-12 ms-n3 text-start ">
|
||||||
<h5 className="mb-0">
|
<h5 className="mb-0">
|
||||||
<i className="bx bx-buildings text-primary"></i>
|
<i className="bx bx-buildings text-primary"></i>
|
||||||
<span className="ms-2 fw-bold">Branch</span>
|
<span className="ms-2 fw-bold">Branchs</span>
|
||||||
</h5>
|
</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Flex container for toggle + button */}
|
{/* Flex container for toggle + button */}
|
||||||
<div className="col-md-6 col-sm-12 text-end">
|
<div className="col-md-8 col-sm-12 text-end">
|
||||||
<div className="d-flex flex-column flex-md-row align-items-md-center justify-content-md-end gap-2">
|
<div className="d-flex flex-column flex-md-row align-items-md-center gap-2">
|
||||||
|
|
||||||
{/* Toggle Switch */}
|
|
||||||
<div className="form-check form-switch d-inline-flex align-items-center">
|
<div className="form-check form-switch d-inline-flex align-items-center">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@ -98,12 +96,14 @@ const ServiceBranch = () => {
|
|||||||
checked={showInactive}
|
checked={showInactive}
|
||||||
onChange={() => setShowInactive(!showInactive)}
|
onChange={() => setShowInactive(!showInactive)}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="inactiveEmployeesCheckbox" className="ms-2 mt-1">
|
<label
|
||||||
|
htmlFor="inactiveEmployeesCheckbox"
|
||||||
|
className="ms-2 mt-1"
|
||||||
|
>
|
||||||
Show Deleted Branches
|
Show Deleted Branches
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="d-flex justify-content-end">
|
||||||
{/* Add Branch Button */}
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-primary"
|
className="btn btn-sm btn-primary"
|
||||||
type="button"
|
type="button"
|
||||||
@ -115,11 +115,12 @@ const ServiceBranch = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<i className="bx bx-sm bx-plus-circle me-2"></i>
|
<i className="bx bx-sm bx-plus-circle me-2"></i>
|
||||||
<span className="d-none d-md-inline-block">Add Branch</span>
|
Add Branch
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="mx-2 mt-3">
|
<div className="mx-2 mt-3">
|
||||||
<table className="table border-top text-nowrap align-middle table-borderless">
|
<table className="table border-top text-nowrap align-middle table-borderless">
|
||||||
@ -152,7 +153,6 @@ const ServiceBranch = () => {
|
|||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
{isError && (
|
{isError && (
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td
|
||||||
@ -215,7 +215,6 @@ const ServiceBranch = () => {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
@ -247,7 +246,9 @@ const ServiceBranch = () => {
|
|||||||
<GlobalModel
|
<GlobalModel
|
||||||
isOpen
|
isOpen
|
||||||
size="md"
|
size="md"
|
||||||
closeModal={() => setManageState({ IsOpen: false, branchId: null })}
|
closeModal={() =>
|
||||||
|
setManageState({ IsOpen: false, branchId: null })
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<ManageBranch
|
<ManageBranch
|
||||||
key={manageState.branchId ?? "new"}
|
key={manageState.branchId ?? "new"}
|
||||||
@ -256,7 +257,6 @@ const ServiceBranch = () => {
|
|||||||
setManageState({ IsOpen: false, branchId: null })
|
setManageState({ IsOpen: false, branchId: null })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</GlobalModel>
|
</GlobalModel>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -150,7 +150,7 @@ const JobComments = ({ data }) => {
|
|||||||
type="submit"
|
type="submit"
|
||||||
disabled={!watch("comment")?.trim() || isPending}
|
disabled={!watch("comment")?.trim() || isPending}
|
||||||
>
|
>
|
||||||
Submit
|
Send
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -47,7 +47,11 @@ const ManageJobTicket = ({ Job }) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div className="row text-start" ref={drawerRef}>
|
<div
|
||||||
|
className=" text-start position-relative"
|
||||||
|
ref={drawerRef}
|
||||||
|
style={{ overflow: "visible" }}
|
||||||
|
>
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<h6 className="fs-5 fw-semibold">{data?.title}</h6>
|
<h6 className="fs-5 fw-semibold">{data?.title}</h6>
|
||||||
<div className="d-flex justify-content-between align-items-start flex-wrap mb-2">
|
<div className="d-flex justify-content-between align-items-start flex-wrap mb-2">
|
||||||
@ -56,7 +60,7 @@ const ManageJobTicket = ({ Job }) => {
|
|||||||
{data?.jobTicketUId || "N/A"}
|
{data?.jobTicketUId || "N/A"}
|
||||||
</p>
|
</p>
|
||||||
<div className="d-flex flex-column align-items-end gap-3 mb-3">
|
<div className="d-flex flex-column align-items-end gap-3 mb-3">
|
||||||
<div className="d-flex flex-row gap-2">
|
<div className="d-flex flex-row gap-2 position-relative">
|
||||||
<span className={`badge ${getJobStatusBadge(data?.status?.id)}`}>
|
<span className={`badge ${getJobStatusBadge(data?.status?.id)}`}>
|
||||||
{data?.status?.displayName}
|
{data?.status?.displayName}
|
||||||
</span>
|
</span>
|
||||||
@ -65,6 +69,7 @@ const ManageJobTicket = ({ Job }) => {
|
|||||||
id="STATUS_CHANEG"
|
id="STATUS_CHANEG"
|
||||||
Mode="click"
|
Mode="click"
|
||||||
className=""
|
className=""
|
||||||
|
align="right"
|
||||||
content={
|
content={
|
||||||
<ChangeStatus
|
<ChangeStatus
|
||||||
statusId={data?.status?.id}
|
statusId={data?.status?.id}
|
||||||
@ -91,23 +96,33 @@ const ManageJobTicket = ({ Job }) => {
|
|||||||
<p>{data?.description || "N/A"}</p>
|
<p>{data?.description || "N/A"}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="d-flex justify-content-between mb-4">
|
<div className="d-flex justify-content-between align-items-center mb-4">
|
||||||
<div className="d-flex flex-row gap-1 fw-medium">
|
<div className="d-flex flex-row gap-1 text-secondry">
|
||||||
<i className="bx bx-calendar"></i>{" "}
|
<i className="bx bx-calendar"></i>{" "}
|
||||||
<span>
|
<span>
|
||||||
Created Date : {formatUTCToLocalTime(data?.createdAt, true)}
|
Created Date : {formatUTCToLocalTime(data?.createdAt, true)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<div className="d-flex justify-content-md-between ">
|
<div className="d-flex justify-content-md-between ">
|
||||||
<div className="d-flex flex-row gap-5">
|
<div className="d-flex flex-row gap-5">
|
||||||
<span className="fw-medium">
|
<span className="text-secondry">
|
||||||
<i className="bx bx-calendar"></i> Start Date :{" "}
|
<i className="bx bx-calendar"></i> Start Date :{" "}
|
||||||
{formatUTCToLocalTime(data?.startDate)}
|
{formatUTCToLocalTime(data?.startDate)}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
<i className="bx bx-right-arrow-alt"></i>{" "}
|
<i className="bx bx-right-arrow-alt"></i>{" "}
|
||||||
<span className="fw-medium">
|
<span className="text-secondry">
|
||||||
<i className="bx bx-calendar"></i> Due on :{" "}
|
<i className="bx bx-calendar"></i> Due on :{" "}
|
||||||
{formatUTCToLocalTime(data?.startDate)}
|
{formatUTCToLocalTime(data?.startDate)}
|
||||||
</span>
|
</span>
|
||||||
@ -117,7 +132,7 @@ const ManageJobTicket = ({ Job }) => {
|
|||||||
const { days, color } = daysLeft(data?.startDate, data?.dueDate);
|
const { days, color } = daysLeft(data?.startDate, data?.dueDate);
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<span className="fw-medium me-1">Days Left:</span>
|
<span className="text-secondry me-1">Days Left:</span>
|
||||||
<span className={`badge bg-${color}`}>
|
<span className={`badge bg-${color}`}>
|
||||||
{days !== null ? `${days} days` : "N/A"}
|
{days !== null ? `${days} days` : "N/A"}
|
||||||
</span>
|
</span>
|
||||||
@ -125,28 +140,79 @@ const ManageJobTicket = ({ Job }) => {
|
|||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
{data?.projectBranch && (
|
{/* {data?.projectBranch && (
|
||||||
<div className="d-flex flex-row gap-3 my-2">
|
<div className="d-flex gap-3 my-2 position-relative" ref={drawerRef} style={{ overflow: "visible" }}>
|
||||||
<span className="fw-semibold">
|
<span className="text-secondary">
|
||||||
<i className="bx bx-buildings me-1"></i> Branch Name :
|
<i className="bx bx-buildings"></i> Branch Name:
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<HoverPopup
|
<HoverPopup
|
||||||
id="BRANCH_DETAILS"
|
id="BRANCH_DETAILS"
|
||||||
Mode="click"
|
Mode="click"
|
||||||
align="auto"
|
align="auto"
|
||||||
boundaryRef={drawerRef}
|
boundaryRef={drawerRef} // drawer has position-relative
|
||||||
content={<BranchDetails branch={data?.projectBranch?.id} />}
|
content={<BranchDetails branch={data?.projectBranch?.id} />}
|
||||||
>
|
>
|
||||||
<span className="text text-decoration-underline ">
|
<span className="text-decoration-underline cursor-pointer">
|
||||||
{data?.projectBranch?.branchName}
|
{data?.projectBranch?.branchName}
|
||||||
</span>
|
</span>
|
||||||
</HoverPopup>
|
</HoverPopup>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)} */}
|
||||||
|
|
||||||
<div className="card shadow-none border ">
|
<div className="border-top">
|
||||||
<span className="text-secondry">People</span>
|
<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>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { closePopup, openPopup, togglePopup } from "../../slices/localVariablesSlice";
|
import {
|
||||||
|
closePopup,
|
||||||
|
openPopup,
|
||||||
|
togglePopup,
|
||||||
|
} from "../../slices/localVariablesSlice";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* align: "auto" | "left" | "right"
|
* align: "auto" | "left" | "right"
|
||||||
@ -63,7 +67,8 @@ const HoverPopup = ({
|
|||||||
const popup = popupRef.current;
|
const popup = popupRef.current;
|
||||||
|
|
||||||
// choose boundary: provided boundaryRef or nearest positioned parent (popup.parentElement)
|
// choose boundary: provided boundaryRef or nearest positioned parent (popup.parentElement)
|
||||||
const boundaryEl = (boundaryRef && boundaryRef.current) || popup.parentElement;
|
const boundaryEl =
|
||||||
|
(boundaryRef && boundaryRef.current) || popup.parentElement;
|
||||||
if (!boundaryEl) return;
|
if (!boundaryEl) return;
|
||||||
|
|
||||||
const boundaryRect = boundaryEl.getBoundingClientRect();
|
const boundaryRect = boundaryEl.getBoundingClientRect();
|
||||||
@ -75,15 +80,12 @@ const HoverPopup = ({
|
|||||||
popup.style.transform = "";
|
popup.style.transform = "";
|
||||||
popup.style.top = "";
|
popup.style.top = "";
|
||||||
|
|
||||||
// default: place below trigger and center horizontally relative to parent
|
|
||||||
// We'll use absolute positioning with respect to the positioned parent container.
|
|
||||||
// Ensure popup is positioned using left/right in parent's coordinate system.
|
|
||||||
// Compute desired left (centered under trigger)
|
|
||||||
const popupRect = popup.getBoundingClientRect();
|
const popupRect = popup.getBoundingClientRect();
|
||||||
const parentRect = boundaryRect; // alias
|
const parentRect = boundaryRect; // alias
|
||||||
|
|
||||||
// Convert trigger center to parent coordinates
|
// Convert trigger center to parent coordinates
|
||||||
const triggerCenterX = triggerRect.left + triggerRect.width / 2 - parentRect.left;
|
const triggerCenterX =
|
||||||
|
triggerRect.left + triggerRect.width / 2 - parentRect.left;
|
||||||
|
|
||||||
// preferred left so popup center aligns to trigger center:
|
// preferred left so popup center aligns to trigger center:
|
||||||
const preferredLeft = triggerCenterX - popupRect.width / 2;
|
const preferredLeft = triggerCenterX - popupRect.width / 2;
|
||||||
@ -111,10 +113,19 @@ const HoverPopup = ({
|
|||||||
setRight(0);
|
setRight(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (align === "center") {
|
||||||
|
popup.style.left = "50%";
|
||||||
|
popup.style.right = "auto";
|
||||||
|
popup.style.transform = "translateX(-50%)";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// align === "auto": try preferred centered position, but flip fully if overflow
|
// align === "auto": try preferred centered position, but flip fully if overflow
|
||||||
// clamp preferredLeft to boundaries so it doesn't render partially outside
|
// clamp preferredLeft to boundaries so it doesn't render partially outside
|
||||||
const leftIfCentered = Math.max(0, Math.min(preferredLeft, parentRect.width - popupRect.width));
|
const leftIfCentered = Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(preferredLeft, parentRect.width - popupRect.width)
|
||||||
|
);
|
||||||
|
|
||||||
// if centered fits, use it
|
// if centered fits, use it
|
||||||
if (leftIfCentered === preferredLeft) {
|
if (leftIfCentered === preferredLeft) {
|
||||||
@ -142,8 +153,17 @@ const HoverPopup = ({
|
|||||||
}, [visible, align, boundaryRef]);
|
}, [visible, align, boundaryRef]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="d-inline-block position-relative">
|
|
||||||
<div
|
<div
|
||||||
|
className="d-inline-block "
|
||||||
|
style={{
|
||||||
|
maxWidth: "calc(700px - 100px)",
|
||||||
|
width: "100%",
|
||||||
|
wordWrap: "break-word",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="d-inline-block"
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { useState, useEffect } from "react";
|
|||||||
import GlobalRepository from "../repositories/GlobalRepository";
|
import GlobalRepository from "../repositories/GlobalRepository";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
|
||||||
export const useDashboard_Data = ({ days, FromDate, projectId }) => {
|
export const useDashboard_Data = ({ days, FromDate, projectId }) => {
|
||||||
const [dashboard_data, setDashboard_Data] = useState([]);
|
const [dashboard_data, setDashboard_Data] = useState([]);
|
||||||
const [isLineChartLoading, setLoading] = useState(false);
|
const [isLineChartLoading, setLoading] = useState(false);
|
||||||
@ -18,11 +17,13 @@ export const useDashboard_Data = ({ days, FromDate, projectId }) => {
|
|||||||
try {
|
try {
|
||||||
const payload = {
|
const payload = {
|
||||||
days,
|
days,
|
||||||
FromDate: FromDate || '',
|
FromDate: FromDate || "",
|
||||||
projectId: projectId || null,
|
projectId: projectId || null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await GlobalRepository.getDashboardProgressionData(payload);
|
const response = await GlobalRepository.getDashboardProgressionData(
|
||||||
|
payload
|
||||||
|
);
|
||||||
setDashboard_Data(response.data);
|
setDashboard_Data(response.data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError("Failed to fetch dashboard data.");
|
setError("Failed to fetch dashboard data.");
|
||||||
@ -38,123 +39,6 @@ export const useDashboard_Data = ({ days, FromDate, projectId }) => {
|
|||||||
return { dashboard_data, loading: isLineChartLoading, error };
|
return { dashboard_data, loading: isLineChartLoading, error };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// export const useDashboard_AttendanceData = (date, projectId) => {
|
|
||||||
// const [dashboard_Attendancedata, setDashboard_AttendanceData] = useState([]);
|
|
||||||
// const [isLineChartLoading, setLoading] = useState(false);
|
|
||||||
// const [error, setError] = useState("");
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// const fetchData = async () => {
|
|
||||||
// setLoading(true);
|
|
||||||
// setError("");
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// const response = await GlobalRepository.getDashboardAttendanceData(date, projectId); // date in 2nd param
|
|
||||||
// setDashboard_AttendanceData(response.data);
|
|
||||||
// } catch (err) {
|
|
||||||
// setError("Failed to fetch dashboard data.");
|
|
||||||
// console.error(err);
|
|
||||||
// } finally {
|
|
||||||
// setLoading(false);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// if (date && projectId !== null) {
|
|
||||||
// fetchData();
|
|
||||||
// }
|
|
||||||
// }, [date, projectId]);
|
|
||||||
|
|
||||||
// return { dashboard_Attendancedata, isLineChartLoading: isLineChartLoading, error };
|
|
||||||
// };
|
|
||||||
|
|
||||||
|
|
||||||
// 🔹 Dashboard Projects Card Data Hook
|
|
||||||
// export const useDashboardProjectsCardData = () => {
|
|
||||||
// const [projectsCardData, setProjectsData] = useState([]);
|
|
||||||
// const [loading, setLoading] = useState(false);
|
|
||||||
// const [error, setError] = useState("");
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// const fetchProjectsData = async () => {
|
|
||||||
// setLoading(true);
|
|
||||||
// setError("");
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// const response = await GlobalRepository.getDashboardProjectsCardData();
|
|
||||||
// setProjectsData(response.data);
|
|
||||||
// } catch (err) {
|
|
||||||
// setError("Failed to fetch projects card data.");
|
|
||||||
// console.error(err);
|
|
||||||
// } finally {
|
|
||||||
// setLoading(false);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// fetchProjectsData();
|
|
||||||
// }, []);
|
|
||||||
|
|
||||||
// return { projectsCardData, loading, error };
|
|
||||||
// };
|
|
||||||
|
|
||||||
// 🔹 Dashboard Teams Card Data Hook
|
|
||||||
// export const useDashboardTeamsCardData = (projectId) => {
|
|
||||||
// const [teamsCardData, setTeamsData] = useState({});
|
|
||||||
// const [loading, setLoading] = useState(false);
|
|
||||||
// const [error, setError] = useState("");
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// const fetchTeamsData = async () => {
|
|
||||||
// setLoading(true);
|
|
||||||
// setError("");
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// const response = await GlobalRepository.getDashboardTeamsCardData(projectId);
|
|
||||||
// setTeamsData(response.data || {});
|
|
||||||
// } catch (err) {
|
|
||||||
// setError("Failed to fetch teams card data.");
|
|
||||||
// console.error("Error fetching teams card data:", err);
|
|
||||||
// setTeamsData({});
|
|
||||||
// } finally {
|
|
||||||
// setLoading(false);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// fetchTeamsData();
|
|
||||||
// }, [projectId]);
|
|
||||||
|
|
||||||
// return { teamsCardData, loading, error };
|
|
||||||
// };
|
|
||||||
|
|
||||||
// export const useDashboardTasksCardData = (projectId) => {
|
|
||||||
// const [tasksCardData, setTasksData] = useState({});
|
|
||||||
// const [loading, setLoading] = useState(false);
|
|
||||||
// const [error, setError] = useState("");
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// const fetchTasksData = async () => {
|
|
||||||
// setLoading(true);
|
|
||||||
// setError("");
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// const response = await GlobalRepository.getDashboardTasksCardData(projectId);
|
|
||||||
// setTasksData(response.data);
|
|
||||||
// } catch (err) {
|
|
||||||
// setError("Failed to fetch tasks card data.");
|
|
||||||
// console.error(err);
|
|
||||||
// setTasksData({});
|
|
||||||
// } finally {
|
|
||||||
// setLoading(false);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// fetchTasksData();
|
|
||||||
// }, [projectId]);
|
|
||||||
|
|
||||||
// return { tasksCardData, loading, error };
|
|
||||||
// };
|
|
||||||
|
|
||||||
|
|
||||||
export const useAttendanceOverviewData = (projectId, days) => {
|
export const useAttendanceOverviewData = (projectId, days) => {
|
||||||
const [attendanceOverviewData, setAttendanceOverviewData] = useState([]);
|
const [attendanceOverviewData, setAttendanceOverviewData] = useState([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@ -167,7 +51,10 @@ export const useAttendanceOverviewData = (projectId, days) => {
|
|||||||
setError("");
|
setError("");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await GlobalRepository.getAttendanceOverview(projectId, days);
|
const response = await GlobalRepository.getAttendanceOverview(
|
||||||
|
projectId,
|
||||||
|
days
|
||||||
|
);
|
||||||
setAttendanceOverviewData(response.data);
|
setAttendanceOverviewData(response.data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError("Failed to fetch attendance overview data.");
|
setError("Failed to fetch attendance overview data.");
|
||||||
@ -182,7 +69,6 @@ export const useAttendanceOverviewData = (projectId, days) => {
|
|||||||
return { attendanceOverviewData, loading, error };
|
return { attendanceOverviewData, loading, error };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// -------------------Query----------------------------
|
// -------------------Query----------------------------
|
||||||
|
|
||||||
// export const useDashboard_Data = (days, FromDate, projectId)=>{
|
// export const useDashboard_Data = (days, FromDate, projectId)=>{
|
||||||
@ -199,39 +85,47 @@ export const useAttendanceOverviewData = (projectId, days) => {
|
|||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
// }
|
// }
|
||||||
|
export const useProjectCompletionStatus = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["projectCompletionStatus"],
|
||||||
|
queryFn: async () => {
|
||||||
|
const resp = await await GlobalRepository.getProjectCompletionStatus();
|
||||||
|
return resp.data;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
export const useDashboard_AttendanceData = (date, projectId) => {
|
export const useDashboard_AttendanceData = (date, projectId) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["dashboardAttendances", date, projectId],
|
queryKey: ["dashboardAttendances", date, projectId],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
|
const resp = await await GlobalRepository.getDashboardAttendanceData(
|
||||||
const resp = await await GlobalRepository.getDashboardAttendanceData(date, projectId)
|
date,
|
||||||
|
projectId
|
||||||
|
);
|
||||||
return resp.data;
|
return resp.data;
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export const useDashboardTeamsCardData = (projectId) => {
|
export const useDashboardTeamsCardData = (projectId) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["dashboardTeams", projectId],
|
queryKey: ["dashboardTeams", projectId],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
|
const resp = await GlobalRepository.getDashboardTeamsCardData(projectId);
|
||||||
const resp = await GlobalRepository.getDashboardTeamsCardData(projectId)
|
|
||||||
return resp.data;
|
return resp.data;
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export const useDashboardTasksCardData = (projectId) => {
|
export const useDashboardTasksCardData = (projectId) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["dashboardTasks", projectId],
|
queryKey: ["dashboardTasks", projectId],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
|
const resp = await GlobalRepository.getDashboardTasksCardData(projectId);
|
||||||
const resp = await GlobalRepository.getDashboardTasksCardData(projectId)
|
|
||||||
return resp.data;
|
return resp.data;
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
// export const useAttendanceOverviewData = (projectId, days) => {
|
// export const useAttendanceOverviewData = (projectId, days) => {
|
||||||
// return useQuery({
|
// return useQuery({
|
||||||
// queryKey:["dashboardAttendanceOverView",projectId],
|
// queryKey:["dashboardAttendanceOverView",projectId],
|
||||||
@ -247,24 +141,25 @@ export const useDashboardProjectsCardData = () => {
|
|||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["dashboardProjects"],
|
queryKey: ["dashboardProjects"],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
|
|
||||||
const resp = await GlobalRepository.getDashboardProjectsCardData();
|
const resp = await GlobalRepository.getDashboardProjectsCardData();
|
||||||
return resp.data;
|
return resp.data;
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export const useExpenseAnalysis = (projectId, startDate, endDate) => {
|
export const useExpenseAnalysis = (projectId, startDate, endDate) => {
|
||||||
const hasBothDates = !!startDate && !!endDate;
|
const hasBothDates = !!startDate && !!endDate;
|
||||||
const noDatesSelected = !startDate && !endDate;
|
const noDatesSelected = !startDate && !endDate;
|
||||||
|
|
||||||
const shouldFetch =
|
const shouldFetch = noDatesSelected || hasBothDates;
|
||||||
noDatesSelected ||
|
|
||||||
hasBothDates;
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["expenseAnalysis", projectId, startDate, endDate],
|
queryKey: ["expenseAnalysis", projectId, startDate, endDate],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const resp = await GlobalRepository.getExpenseData(projectId, startDate, endDate);
|
const resp = await GlobalRepository.getExpenseData(
|
||||||
|
projectId,
|
||||||
|
startDate,
|
||||||
|
endDate
|
||||||
|
);
|
||||||
return resp.data;
|
return resp.data;
|
||||||
},
|
},
|
||||||
enabled: shouldFetch,
|
enabled: shouldFetch,
|
||||||
@ -280,17 +175,20 @@ export const useExpenseStatus = (projectId) => {
|
|||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const resp = await GlobalRepository.getExpenseStatus(projectId);
|
const resp = await GlobalRepository.getExpenseStatus(projectId);
|
||||||
return resp.data;
|
return resp.data;
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export const useExpenseDataByProject = (projectId, categoryId, months) => {
|
export const useExpenseDataByProject = (projectId, categoryId, months) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["expenseByProject", projectId, categoryId, months],
|
queryKey: ["expenseByProject", projectId, categoryId, months],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const resp = await GlobalRepository.getExpenseDataByProject(projectId, categoryId, months);
|
const resp = await GlobalRepository.getExpenseDataByProject(
|
||||||
|
projectId,
|
||||||
|
categoryId,
|
||||||
|
months
|
||||||
|
);
|
||||||
return resp.data;
|
return resp.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -18,6 +18,8 @@ const GlobalRepository = {
|
|||||||
|
|
||||||
return api.get(`/api/Dashboard/Progression?${params.toString()}`);
|
return api.get(`/api/Dashboard/Progression?${params.toString()}`);
|
||||||
},
|
},
|
||||||
|
getProjectCompletionStatus:()=>api.get(`/api/Dashboard/project-completion-status`),
|
||||||
|
|
||||||
|
|
||||||
getDashboardAttendanceData: (date, projectId) => {
|
getDashboardAttendanceData: (date, projectId) => {
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { api } from "../utils/axiosClient";
|
import { api } from "../utils/axiosClient";
|
||||||
|
|
||||||
const ProjectRepository = {
|
const ProjectRepository = {
|
||||||
|
|
||||||
getProjectList: (pageSize, pageNumber) =>
|
getProjectList: (pageSize, pageNumber) =>
|
||||||
api.get(`/api/project/list?pageSize=${pageSize}&pageNumber=${pageNumber}`),
|
api.get(`/api/project/list?pageSize=${pageSize}&pageNumber=${pageNumber}`),
|
||||||
getProjectByprojectId: (projetid) =>
|
getProjectByprojectId: (projetid) =>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user