added branch details
This commit is contained in:
parent
e7fddd41c2
commit
0210e17170
@ -165,7 +165,7 @@ const ManageJob = ({ Job }) => {
|
|||||||
dueDate: JobData.dueDate ?? null,
|
dueDate: JobData.dueDate ?? null,
|
||||||
tags: JobData.tags ?? [],
|
tags: JobData.tags ?? [],
|
||||||
statusId: JobData.status.id,
|
statusId: JobData.status.id,
|
||||||
branchId : JobData?.projectBranch?.id
|
projectBranchId : JobData?.projectBranch?.id
|
||||||
});
|
});
|
||||||
}, [JobData, Job, projectId]);
|
}, [JobData, Job, projectId]);
|
||||||
return (
|
return (
|
||||||
@ -248,8 +248,8 @@ const ManageJob = ({ Job }) => {
|
|||||||
<SelectFieldSearch
|
<SelectFieldSearch
|
||||||
label="Select Branch"
|
label="Select Branch"
|
||||||
placeholder="Select Branch"
|
placeholder="Select Branch"
|
||||||
value={watch("branchId")}
|
value={watch("projectBranchId")}
|
||||||
onChange={(val) => setValue("branchId", val)}
|
onChange={(val) => setValue("projectBranchId", val)}
|
||||||
valueKey="id"
|
valueKey="id"
|
||||||
labelKey="branchName"
|
labelKey="branchName"
|
||||||
hookParams={[projectId, true, 10, 1]}
|
hookParams={[projectId, true, 10, 1]}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import { useServiceProjectJobDetails } from "../../hooks/useServiceProject";
|
import { useServiceProjectJobDetails } from "../../hooks/useServiceProject";
|
||||||
import { SpinnerLoader } from "../common/Loader";
|
import { SpinnerLoader } from "../common/Loader";
|
||||||
import Error from "../common/Error";
|
import Error from "../common/Error";
|
||||||
@ -13,13 +13,14 @@ import ChangeStatus from "./ChangeStatus";
|
|||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { STATUS_JOB_CLOSED } from "../../utils/constants";
|
import { STATUS_JOB_CLOSED } from "../../utils/constants";
|
||||||
import Tooltip from "../common/Tooltip";
|
import Tooltip from "../common/Tooltip";
|
||||||
|
import BranchDetails from "./ServiceProjectBranch/BranchDetails";
|
||||||
|
|
||||||
const ManageJobTicket = ({ Job }) => {
|
const ManageJobTicket = ({ Job }) => {
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
const { data, isLoading, isError, error } = useServiceProjectJobDetails(
|
const { data, isLoading, isError, error } = useServiceProjectJobDetails(
|
||||||
Job?.job
|
Job?.job
|
||||||
);
|
);
|
||||||
|
const drawerRef = useRef();
|
||||||
const tabsData = [
|
const tabsData = [
|
||||||
{
|
{
|
||||||
id: "comment",
|
id: "comment",
|
||||||
@ -124,12 +125,18 @@ const ManageJobTicket = ({ Job }) => {
|
|||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex my-2">
|
{data?.projectBranch && (
|
||||||
|
<div className="d-flex flex-row gap-3 my-2">
|
||||||
<span className="fw-semibold">
|
<span className="fw-semibold">
|
||||||
{" "}
|
{" "}
|
||||||
<i className="bx bx-buildings me-1"></i>Branch Name :
|
<i className="bx bx-buildings me-1"></i>Branch Name :
|
||||||
</span>
|
</span>
|
||||||
|
<HoverPopup align="left" boundaryRef={drawerRef} id="BRANCH_DETAILS" Mode="click" content={ <BranchDetails branch={data?.projectBranch?.id}/>} >
|
||||||
|
<span className="text">{data?.projectBranch?.branchName}</span>
|
||||||
|
</HoverPopup>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="d-block mt-4 mb-3">
|
<div className="d-block mt-4 mb-3">
|
||||||
<div className="row align-items-start align-items-md-start gap-2 mb-1">
|
<div className="row align-items-start align-items-md-start gap-2 mb-1">
|
||||||
<div className="col-12 col-md-auto">
|
<div className="col-12 col-md-auto">
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { useBranch } from '../../../hooks/useServiceProject'
|
||||||
|
import { SpinnerLoader } from '../../common/Loader'
|
||||||
|
import Error from '../../common/Error'
|
||||||
|
|
||||||
|
const BranchDetails = ({branch}) => {
|
||||||
|
const {data,isLoading,isError,error} = useBranch(branch)
|
||||||
|
if(isLoading) return <div><SpinnerLoader/></div>
|
||||||
|
if(isError) return <div><Error error={error}/></div>
|
||||||
|
return (
|
||||||
|
<div className='row w-auto'>
|
||||||
|
<div className='col-12 d-flex flex-row gap-3'>
|
||||||
|
<span className='text-secondry'>Name:</span> <span>{data.branchName}</span>
|
||||||
|
</div>
|
||||||
|
<div className='col-12 d-flex flex-row gap-3'>
|
||||||
|
<span className='text-secondry'>Type:</span> <span>{data.branchType}</span>
|
||||||
|
</div>
|
||||||
|
<div className='col-12 d-flex flex-row gap-3'>
|
||||||
|
<span className='text-secondry'>Address:</span> <span>{data.address}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BranchDetails
|
||||||
@ -75,7 +75,7 @@ export const jobSchema = z.object({
|
|||||||
|
|
||||||
statusId: z.string().optional().nullable(),
|
statusId: z.string().optional().nullable(),
|
||||||
|
|
||||||
branchId: z.string().optional().nullable(),
|
projectBranchId: z.string().optional().nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
|
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import {
|
import { closePopup, openPopup, togglePopup } from "../../slices/localVariablesSlice";
|
||||||
closePopup,
|
|
||||||
openPopup,
|
|
||||||
togglePopup,
|
|
||||||
} from "../../slices/localVariablesSlice";
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* align: "auto" | "left" | "right"
|
||||||
|
* boundaryRef: optional ref to the drawer/container element to use as boundary
|
||||||
|
*/
|
||||||
const HoverPopup = ({
|
const HoverPopup = ({
|
||||||
id,
|
id,
|
||||||
title,
|
title,
|
||||||
@ -13,6 +13,8 @@ const HoverPopup = ({
|
|||||||
children,
|
children,
|
||||||
className = "",
|
className = "",
|
||||||
Mode = "hover",
|
Mode = "hover",
|
||||||
|
align = "auto",
|
||||||
|
boundaryRef = null,
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const visible = useSelector((s) => s.localVariables.popups[id] || false);
|
const visible = useSelector((s) => s.localVariables.popups[id] || false);
|
||||||
@ -23,11 +25,9 @@ const HoverPopup = ({
|
|||||||
const handleMouseEnter = () => {
|
const handleMouseEnter = () => {
|
||||||
if (Mode === "hover") dispatch(openPopup(id));
|
if (Mode === "hover") dispatch(openPopup(id));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseLeave = () => {
|
const handleMouseLeave = () => {
|
||||||
if (Mode === "hover") dispatch(closePopup(id));
|
if (Mode === "hover") dispatch(closePopup(id));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (e) => {
|
const handleClick = (e) => {
|
||||||
if (Mode === "click") {
|
if (Mode === "click") {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -35,43 +35,111 @@ const HoverPopup = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Close on outside click when using click mode
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (Mode !== "click" || !visible) return;
|
if (Mode !== "click" || !visible) return;
|
||||||
|
|
||||||
const handleOutside = (e) => {
|
const handler = (e) => {
|
||||||
if (
|
if (
|
||||||
!popupRef.current?.contains(e.target) &&
|
popupRef.current &&
|
||||||
!triggerRef.current?.contains(e.target)
|
!popupRef.current.contains(e.target) &&
|
||||||
|
triggerRef.current &&
|
||||||
|
!triggerRef.current.contains(e.target)
|
||||||
) {
|
) {
|
||||||
dispatch(closePopup(id));
|
dispatch(closePopup(id));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
document.addEventListener("click", handleOutside);
|
|
||||||
return () => document.removeEventListener("click", handleOutside);
|
|
||||||
}, [visible, Mode, id]);
|
|
||||||
|
|
||||||
|
document.addEventListener("click", handler);
|
||||||
|
return () => document.removeEventListener("click", handler);
|
||||||
|
}, [Mode, visible, dispatch, id]);
|
||||||
|
|
||||||
|
// Positioning effect: respects align prop and stays inside boundary (drawer)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!visible || !popupRef.current) return;
|
if (!visible || !popupRef.current || !triggerRef.current) return;
|
||||||
|
|
||||||
|
// run in next frame so DOM/layout settles
|
||||||
|
requestAnimationFrame(() => {
|
||||||
const popup = popupRef.current;
|
const popup = popupRef.current;
|
||||||
const rect = popup.getBoundingClientRect();
|
|
||||||
|
|
||||||
popup.style.left = "50%";
|
// choose boundary: provided boundaryRef or nearest positioned parent (popup.parentElement)
|
||||||
|
const boundaryEl = (boundaryRef && boundaryRef.current) || popup.parentElement;
|
||||||
|
if (!boundaryEl) return;
|
||||||
|
|
||||||
|
const boundaryRect = boundaryEl.getBoundingClientRect();
|
||||||
|
const triggerRect = triggerRef.current.getBoundingClientRect();
|
||||||
|
|
||||||
|
// reset styles first
|
||||||
|
popup.style.left = "";
|
||||||
|
popup.style.right = "";
|
||||||
|
popup.style.transform = "";
|
||||||
|
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 parentRect = boundaryRect; // alias
|
||||||
|
|
||||||
|
// Convert trigger center to parent coordinates
|
||||||
|
const triggerCenterX = triggerRect.left + triggerRect.width / 2 - parentRect.left;
|
||||||
|
|
||||||
|
// preferred left so popup center aligns to trigger center:
|
||||||
|
const preferredLeft = triggerCenterX - popupRect.width / 2;
|
||||||
|
|
||||||
|
// Helpers to set styles in parent's coordinate system:
|
||||||
|
const setLeft = (leftPx) => {
|
||||||
|
popup.style.left = `${leftPx}px`;
|
||||||
popup.style.right = "auto";
|
popup.style.right = "auto";
|
||||||
popup.style.transform = "translateX(-50%)";
|
popup.style.transform = "none";
|
||||||
|
};
|
||||||
if (rect.right > window.innerWidth) {
|
const setRight = (rightPx) => {
|
||||||
popup.style.left = "auto";
|
popup.style.left = "auto";
|
||||||
popup.style.right = "0";
|
popup.style.right = `${rightPx}px`;
|
||||||
popup.style.transform = "none";
|
popup.style.transform = "none";
|
||||||
|
};
|
||||||
|
|
||||||
|
// If user forced align:
|
||||||
|
if (align === "left") {
|
||||||
|
// align popup's left to parent's left (0)
|
||||||
|
setLeft(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (align === "right") {
|
||||||
|
// align popup's right to parent's right (0)
|
||||||
|
setRight(0);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rect.left < 0) {
|
// align === "auto": try preferred centered position, but flip fully if overflow
|
||||||
popup.style.left = "0";
|
// clamp preferredLeft to boundaries so it doesn't render partially outside
|
||||||
popup.style.right = "auto";
|
const leftIfCentered = Math.max(0, Math.min(preferredLeft, parentRect.width - popupRect.width));
|
||||||
popup.style.transform = "none";
|
|
||||||
|
// if centered fits, use it
|
||||||
|
if (leftIfCentered === preferredLeft) {
|
||||||
|
setLeft(leftIfCentered);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}, [visible]);
|
|
||||||
|
// if centering would overflow right -> stick popup fully to left (left=0)
|
||||||
|
if (preferredLeft > parentRect.width - popupRect.width) {
|
||||||
|
// place popup so its right aligns to parent's right
|
||||||
|
// i.e., left = parent width - popup width
|
||||||
|
setLeft(parentRect.width - popupRect.width);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if centering would overflow left -> stick popup fully to left=0
|
||||||
|
if (preferredLeft < 0) {
|
||||||
|
setLeft(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback center
|
||||||
|
setLeft(leftIfCentered);
|
||||||
|
});
|
||||||
|
}, [visible, align, boundaryRef]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="d-inline-block position-relative">
|
<div className="d-inline-block position-relative">
|
||||||
@ -88,16 +156,18 @@ const HoverPopup = ({
|
|||||||
{visible && (
|
{visible && (
|
||||||
<div
|
<div
|
||||||
ref={popupRef}
|
ref={popupRef}
|
||||||
className={`bg-white border rounded shadow-sm p-3 w-max position-absolute top-100 mt-2 ${className}`}
|
// position absolute; it should be inside a positioned parent (the drawer)
|
||||||
|
className={`hover-popup bg-white border rounded shadow-sm p-3 position-absolute mt-2 ${className}`}
|
||||||
style={{
|
style={{
|
||||||
zIndex: 2000,
|
zIndex: 2000,
|
||||||
left: "50%",
|
top: "100%", // open below trigger
|
||||||
transform: "translateX(-50%)",
|
// left/right will be set by effect in parent coordinates
|
||||||
|
width: "max-content",
|
||||||
|
minWidth: "120px",
|
||||||
}}
|
}}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
{title && <h6 className="fw-semibold mb-2">{title}</h6>}
|
{title && <h6 className="fw-semibold mb-2">{title}</h6>}
|
||||||
|
|
||||||
<div>{content}</div>
|
<div>{content}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user