From 0210e1717058e91238b48891c6b6d99659d5a533 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Wed, 19 Nov 2025 22:58:41 +0530 Subject: [PATCH] added branch details --- src/components/ServiceProject/ManageJob.jsx | 6 +- .../ServiceProject/ManageJobTicket.jsx | 23 ++- .../ServiceProjectBranch/BranchDetails.jsx | 25 ++++ .../ServiceProject/ServiceProjectSchema.jsx | 2 +- src/components/common/HoverPopup.jsx | 140 +++++++++++++----- 5 files changed, 149 insertions(+), 47 deletions(-) create mode 100644 src/components/ServiceProject/ServiceProjectBranch/BranchDetails.jsx diff --git a/src/components/ServiceProject/ManageJob.jsx b/src/components/ServiceProject/ManageJob.jsx index 6ec96e3b..1ba3a04a 100644 --- a/src/components/ServiceProject/ManageJob.jsx +++ b/src/components/ServiceProject/ManageJob.jsx @@ -165,7 +165,7 @@ const ManageJob = ({ Job }) => { dueDate: JobData.dueDate ?? null, tags: JobData.tags ?? [], statusId: JobData.status.id, - branchId : JobData?.projectBranch?.id + projectBranchId : JobData?.projectBranch?.id }); }, [JobData, Job, projectId]); return ( @@ -248,8 +248,8 @@ const ManageJob = ({ Job }) => { setValue("branchId", val)} + value={watch("projectBranchId")} + onChange={(val) => setValue("projectBranchId", val)} valueKey="id" labelKey="branchName" hookParams={[projectId, true, 10, 1]} diff --git a/src/components/ServiceProject/ManageJobTicket.jsx b/src/components/ServiceProject/ManageJobTicket.jsx index c589509c..4399d490 100644 --- a/src/components/ServiceProject/ManageJobTicket.jsx +++ b/src/components/ServiceProject/ManageJobTicket.jsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useRef } from "react"; import { useServiceProjectJobDetails } from "../../hooks/useServiceProject"; import { SpinnerLoader } from "../common/Loader"; import Error from "../common/Error"; @@ -13,13 +13,14 @@ 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"; const ManageJobTicket = ({ Job }) => { const { projectId } = useParams(); const { data, isLoading, isError, error } = useServiceProjectJobDetails( Job?.job ); - +const drawerRef = useRef(); const tabsData = [ { id: "comment", @@ -124,12 +125,18 @@ const ManageJobTicket = ({ Job }) => { ); })()} -
- - {" "} - Branch Name : - -
+ {data?.projectBranch && ( +
+ + {" "} + Branch Name : + + } > + {data?.projectBranch?.branchName} + +
+ )} +
diff --git a/src/components/ServiceProject/ServiceProjectBranch/BranchDetails.jsx b/src/components/ServiceProject/ServiceProjectBranch/BranchDetails.jsx new file mode 100644 index 00000000..1160ccfe --- /dev/null +++ b/src/components/ServiceProject/ServiceProjectBranch/BranchDetails.jsx @@ -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
+ if(isError) return
+ return ( +
+
+ Name: {data.branchName} +
+
+ Type: {data.branchType} +
+
+ Address: {data.address} +
+
+ ) +} + +export default BranchDetails diff --git a/src/components/ServiceProject/ServiceProjectSchema.jsx b/src/components/ServiceProject/ServiceProjectSchema.jsx index 38013cae..dc8a6713 100644 --- a/src/components/ServiceProject/ServiceProjectSchema.jsx +++ b/src/components/ServiceProject/ServiceProjectSchema.jsx @@ -75,7 +75,7 @@ export const jobSchema = z.object({ statusId: z.string().optional().nullable(), - branchId: z.string().optional().nullable(), + projectBranchId: z.string().optional().nullable(), }); const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB diff --git a/src/components/common/HoverPopup.jsx b/src/components/common/HoverPopup.jsx index e1c2dd4d..2093c25a 100644 --- a/src/components/common/HoverPopup.jsx +++ b/src/components/common/HoverPopup.jsx @@ -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 { - closePopup, - openPopup, - togglePopup, -} from "../../slices/localVariablesSlice"; +import { closePopup, openPopup, togglePopup } from "../../slices/localVariablesSlice"; +/** + * align: "auto" | "left" | "right" + * boundaryRef: optional ref to the drawer/container element to use as boundary + */ const HoverPopup = ({ id, title, @@ -13,6 +13,8 @@ const HoverPopup = ({ children, className = "", Mode = "hover", + align = "auto", + boundaryRef = null, }) => { const dispatch = useDispatch(); const visible = useSelector((s) => s.localVariables.popups[id] || false); @@ -23,11 +25,9 @@ const HoverPopup = ({ const handleMouseEnter = () => { if (Mode === "hover") dispatch(openPopup(id)); }; - const handleMouseLeave = () => { if (Mode === "hover") dispatch(closePopup(id)); }; - const handleClick = (e) => { if (Mode === "click") { e.stopPropagation(); @@ -35,43 +35,111 @@ const HoverPopup = ({ } }; + // Close on outside click when using click mode useEffect(() => { if (Mode !== "click" || !visible) return; - const handleOutside = (e) => { + const handler = (e) => { if ( - !popupRef.current?.contains(e.target) && - !triggerRef.current?.contains(e.target) + popupRef.current && + !popupRef.current.contains(e.target) && + triggerRef.current && + !triggerRef.current.contains(e.target) ) { 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(() => { - if (!visible || !popupRef.current) return; + if (!visible || !popupRef.current || !triggerRef.current) return; - const popup = popupRef.current; - const rect = popup.getBoundingClientRect(); + // run in next frame so DOM/layout settles + requestAnimationFrame(() => { + const popup = popupRef.current; - popup.style.left = "50%"; - popup.style.right = "auto"; - popup.style.transform = "translateX(-50%)"; + // choose boundary: provided boundaryRef or nearest positioned parent (popup.parentElement) + const boundaryEl = (boundaryRef && boundaryRef.current) || popup.parentElement; + if (!boundaryEl) return; - if (rect.right > window.innerWidth) { - popup.style.left = "auto"; - popup.style.right = "0"; - popup.style.transform = "none"; - } + const boundaryRect = boundaryEl.getBoundingClientRect(); + const triggerRect = triggerRef.current.getBoundingClientRect(); - if (rect.left < 0) { - popup.style.left = "0"; - popup.style.right = "auto"; - popup.style.transform = "none"; - } - }, [visible]); + // 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.transform = "none"; + }; + const setRight = (rightPx) => { + popup.style.left = "auto"; + popup.style.right = `${rightPx}px`; + 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; + } + + // align === "auto": try preferred centered position, but flip fully if overflow + // clamp preferredLeft to boundaries so it doesn't render partially outside + const leftIfCentered = Math.max(0, Math.min(preferredLeft, parentRect.width - popupRect.width)); + + // if centered fits, use it + if (leftIfCentered === preferredLeft) { + setLeft(leftIfCentered); + return; + } + + // 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 (
@@ -88,16 +156,18 @@ const HoverPopup = ({ {visible && (
e.stopPropagation()} > {title &&
{title}
} -
{content}
)}