added branch details

This commit is contained in:
pramod.mahajan 2025-11-19 22:58:41 +05:30
parent e7fddd41c2
commit 0210e17170
5 changed files with 149 additions and 47 deletions

View File

@ -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]}

View File

@ -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 && (
<span className="fw-semibold"> <div className="d-flex flex-row gap-3 my-2">
{" "} <span className="fw-semibold">
<i className="bx bx-buildings me-1"></i>Branch Name : {" "}
</span> <i className="bx bx-buildings me-1"></i>Branch Name :
</div> </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 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">

View File

@ -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

View File

@ -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

View File

@ -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;
const popup = popupRef.current; // run in next frame so DOM/layout settles
const rect = popup.getBoundingClientRect(); requestAnimationFrame(() => {
const popup = popupRef.current;
popup.style.left = "50%"; // choose boundary: provided boundaryRef or nearest positioned parent (popup.parentElement)
popup.style.right = "auto"; const boundaryEl = (boundaryRef && boundaryRef.current) || popup.parentElement;
popup.style.transform = "translateX(-50%)"; if (!boundaryEl) return;
if (rect.right > window.innerWidth) { const boundaryRect = boundaryEl.getBoundingClientRect();
popup.style.left = "auto"; const triggerRect = triggerRef.current.getBoundingClientRect();
popup.style.right = "0";
popup.style.transform = "none";
}
if (rect.left < 0) { // reset styles first
popup.style.left = "0"; popup.style.left = "";
popup.style.right = "auto"; popup.style.right = "";
popup.style.transform = "none"; popup.style.transform = "";
} popup.style.top = "";
}, [visible]);
// 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 ( 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>
)} )}