added changes status fun
This commit is contained in:
commit
d95ba4c800
@ -23,7 +23,7 @@ import Label from "../common/Label";
|
|||||||
const ManageContact = ({ contactId, closeModal }) => {
|
const ManageContact = ({ contactId, closeModal }) => {
|
||||||
// fetch master data
|
// fetch master data
|
||||||
const { buckets, loading: bucketsLoaging } = useBuckets();
|
const { buckets, loading: bucketsLoaging } = useBuckets();
|
||||||
const { data:projects, loading: projectLoading } = useProjects();
|
const { data: projects, loading: projectLoading } = useProjects();
|
||||||
const { contactCategory, loading: contactCategoryLoading } =
|
const { contactCategory, loading: contactCategoryLoading } =
|
||||||
useContactCategory();
|
useContactCategory();
|
||||||
const { organizationList } = useOrganization();
|
const { organizationList } = useOrganization();
|
||||||
@ -210,7 +210,7 @@ const ManageContact = ({ contactId, closeModal }) => {
|
|||||||
value={watch("organization") || ""}
|
value={watch("organization") || ""}
|
||||||
onChange={(val) => setValue("organization", val, { shouldValidate: true })}
|
onChange={(val) => setValue("organization", val, { shouldValidate: true })}
|
||||||
error={errors.organization?.message}
|
error={errors.organization?.message}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -408,6 +408,7 @@ const ManageContact = ({ contactId, closeModal }) => {
|
|||||||
label="Tags"
|
label="Tags"
|
||||||
options={contactTags}
|
options={contactTags}
|
||||||
isRequired={true}
|
isRequired={true}
|
||||||
|
placeholder="Enter Tag"
|
||||||
/>
|
/>
|
||||||
{errors.tags && (
|
{errors.tags && (
|
||||||
<small className="danger-text">{errors.tags.message}</small>
|
<small className="danger-text">{errors.tags.message}</small>
|
||||||
|
|||||||
@ -104,7 +104,6 @@ const hasChanges =
|
|||||||
permission: payloadPermissions,
|
permission: payloadPermissions,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("Final payload:", payload);
|
|
||||||
updatePermission(payload);
|
updatePermission(payload);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -134,7 +134,6 @@ const ManageRecurringExpense = ({ closeModal, requestToEdit = null }) => {
|
|||||||
const StrikeDate = watch("strikeDate")
|
const StrikeDate = watch("strikeDate")
|
||||||
|
|
||||||
const onSubmit = (fromdata) => {
|
const onSubmit = (fromdata) => {
|
||||||
console.log(fromdata);
|
|
||||||
let payload = {
|
let payload = {
|
||||||
...fromdata,
|
...fromdata,
|
||||||
strikeDate: fromdata.strikeDate
|
strikeDate: fromdata.strikeDate
|
||||||
|
|||||||
100
src/components/ServiceProject/ChangeStatus.jsx
Normal file
100
src/components/ServiceProject/ChangeStatus.jsx
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import SelectField from "../common/Forms/SelectField";
|
||||||
|
import { useJobStatus } from "../../hooks/masterHook/useMaster";
|
||||||
|
import { SpinnerLoader } from "../common/Loader";
|
||||||
|
import Error from "../common/Error";
|
||||||
|
import { z } from "zod";
|
||||||
|
import {
|
||||||
|
AppFormController,
|
||||||
|
AppFormProvider,
|
||||||
|
useAppForm,
|
||||||
|
} from "../../hooks/appHooks/useAppForm";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { closePopup } from "../../slices/localVariablesSlice";
|
||||||
|
import { useUpdateServiceProjectJob } from "../../hooks/useServiceProject";
|
||||||
|
|
||||||
|
export const ChangeStatusSchema = z.object({
|
||||||
|
statusId: z.string().min(1, { message: "Please select status" }),
|
||||||
|
});
|
||||||
|
const ChangeStatus = ({ statusId, projectId, jobId, popUpId }) => {
|
||||||
|
const { data, isLoading, isError, error } = useJobStatus(statusId, projectId);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const methods = useAppForm({
|
||||||
|
resolver: zodResolver(ChangeStatusSchema),
|
||||||
|
defaultValues: { statusId: "" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
reset,
|
||||||
|
formState: { errors },
|
||||||
|
} = methods;
|
||||||
|
|
||||||
|
const { mutate: UpdateStatus, isPending } = useUpdateServiceProjectJob(() => {
|
||||||
|
// handleClose();
|
||||||
|
});
|
||||||
|
const onSubmit = (formData) => {
|
||||||
|
const payload =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
op: "replace",
|
||||||
|
path: "/statusId",
|
||||||
|
value: formData.statusId,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
UpdateStatus({ id: jobId, payload });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
dispatch(closePopup(popUpId));
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<AppFormProvider {...methods}>
|
||||||
|
<form className="row text-start" onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<div className="mb-2">
|
||||||
|
<AppFormController
|
||||||
|
name="statusId"
|
||||||
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<SelectField
|
||||||
|
label="Select Status"
|
||||||
|
options={data ?? []}
|
||||||
|
placeholder="Choose a Status"
|
||||||
|
required
|
||||||
|
labelKeyKey="name"
|
||||||
|
valueKeyKey="id"
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
isLoading={isLoading}
|
||||||
|
className="m-0"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{errors.statusId && (
|
||||||
|
<small className="danger-text">{errors.statusId.message}</small>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="d-flex flex-row justify-content-end gap-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleClose}
|
||||||
|
className="btn btn-label-secondary btn-sm"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="submit" className="btn btn-primary btn-sm">
|
||||||
|
{isPending ? "Please wait" : "Save"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</AppFormProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ChangeStatus;
|
||||||
@ -2,7 +2,6 @@ import React from "react";
|
|||||||
import Avatar from "../common/Avatar";
|
import Avatar from "../common/Avatar";
|
||||||
|
|
||||||
const JobStatusLog = ({ data }) => {
|
const JobStatusLog = ({ data }) => {
|
||||||
console.log(data);
|
|
||||||
return (
|
return (
|
||||||
<div className="card shadow-none p-0">
|
<div className="card shadow-none p-0">
|
||||||
<div className="card-body p-0">
|
<div className="card-body p-0">
|
||||||
|
|||||||
@ -20,10 +20,11 @@ import {
|
|||||||
AppFormProvider,
|
AppFormProvider,
|
||||||
useAppForm,
|
useAppForm,
|
||||||
} from "../../hooks/appHooks/useAppForm";
|
} from "../../hooks/appHooks/useAppForm";
|
||||||
import { useServiceProjectJobContext } from "./Jobs";
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
const ManageJob = ({ Job }) => {
|
const ManageJob = ({ Job }) => {
|
||||||
const { manageJob, setManageJob } = useServiceProjectJobContext();
|
const { projectId } = useParams();
|
||||||
|
|
||||||
const methods = useAppForm({
|
const methods = useAppForm({
|
||||||
resolver: zodResolver(jobSchema),
|
resolver: zodResolver(jobSchema),
|
||||||
defaultValues: defaultJobValue,
|
defaultValues: defaultJobValue,
|
||||||
@ -69,27 +70,33 @@ const ManageJob = ({ Job }) => {
|
|||||||
|
|
||||||
formData.startDate = localToUtc(formData.startDate);
|
formData.startDate = localToUtc(formData.startDate);
|
||||||
formData.dueDate = localToUtc(formData.dueDate);
|
formData.dueDate = localToUtc(formData.dueDate);
|
||||||
|
formData.projectId = projectId;
|
||||||
CreateJob(formData);
|
CreateJob(formData);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (manageJob.jobId && JobData) {
|
if (!JobData && !Job) {
|
||||||
console.log("freshed data");
|
reset({
|
||||||
|
...defaultJobValue,
|
||||||
|
projectId: projectId,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!JobData || !Job) return;
|
||||||
|
|
||||||
const assignedEmployees = (JobData.assignees || []).map((e) => e.id);
|
const assignedEmployees = (JobData.assignees || []).map((e) => e.id);
|
||||||
|
|
||||||
reset({
|
reset({
|
||||||
title: JobData.title ?? "",
|
title: JobData.title ?? "",
|
||||||
description: JobData.description ?? "",
|
description: JobData.description ?? "",
|
||||||
projectId: JobData.project.id ?? "",
|
projectId: JobData.project?.id ?? projectId,
|
||||||
assignees: assignedEmployees,
|
assignees: assignedEmployees,
|
||||||
startDate: JobData.startDate ?? null,
|
startDate: JobData.startDate ?? null,
|
||||||
dueDate: JobData.dueDate ?? null,
|
dueDate: JobData.dueDate ?? null,
|
||||||
tags: JobData.tags ?? [],
|
tags: JobData.tags ?? [],
|
||||||
});
|
});
|
||||||
}
|
}, [JobData, Job, projectId]);
|
||||||
if (!manageJob.jobId) {
|
|
||||||
reset(defaultJobValue);
|
|
||||||
}
|
|
||||||
}, [JobData, manageJob]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container">
|
<div className="container">
|
||||||
@ -101,27 +108,10 @@ const ManageJob = ({ Job }) => {
|
|||||||
type="text"
|
type="text"
|
||||||
{...register("title")}
|
{...register("title")}
|
||||||
className="form-control form-control"
|
className="form-control form-control"
|
||||||
|
placeholder="Enter Title"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-12 col-md-6 mb-2">
|
|
||||||
<AppFormController
|
|
||||||
name="projectId"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<SelectField
|
|
||||||
label="Status"
|
|
||||||
options={data?.data}
|
|
||||||
placeholder="Choose a Status"
|
|
||||||
required
|
|
||||||
labelKeyKey="name"
|
|
||||||
valueKeyKey="id"
|
|
||||||
value={field.value}
|
|
||||||
onChange={field.onChange}
|
|
||||||
isLoading={isProjectLoading}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="col-12 col-md-6 mb-2 mb-md-4">
|
<div className="col-12 col-md-6 mb-2 mb-md-4">
|
||||||
<Label required>Start Date</Label>
|
<Label required>Start Date</Label>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
@ -155,6 +145,7 @@ const ManageJob = ({ Job }) => {
|
|||||||
options={JobTags?.data}
|
options={JobTags?.data}
|
||||||
name="tags"
|
name="tags"
|
||||||
label="Tag"
|
label="Tag"
|
||||||
|
placeholder="Enter Tag"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,8 +8,12 @@ import EmployeeAvatarGroup from "../common/EmployeeAvatarGroup";
|
|||||||
import JobStatusLog from "./JobStatusLog";
|
import JobStatusLog from "./JobStatusLog";
|
||||||
import JobComments from "./JobComments";
|
import JobComments from "./JobComments";
|
||||||
import { daysLeft } from "../../utils/appUtils";
|
import { daysLeft } from "../../utils/appUtils";
|
||||||
|
import HoverPopup from "../common/HoverPopup";
|
||||||
|
import ChangeStatus from "./ChangeStatus";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
const ManageJobTicket = ({ Job }) => {
|
const ManageJobTicket = ({ Job }) => {
|
||||||
|
const { projectId } = useParams();
|
||||||
const { data, isLoading, isError, error } = useServiceProjectJobDetails(
|
const { data, isLoading, isError, error } = useServiceProjectJobDetails(
|
||||||
Job?.job
|
Job?.job
|
||||||
);
|
);
|
||||||
@ -31,7 +35,6 @@ const ManageJobTicket = ({ Job }) => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
if (isLoading) return <SpinnerLoader />;
|
if (isLoading) return <SpinnerLoader />;
|
||||||
if (isError)
|
if (isError)
|
||||||
return (
|
return (
|
||||||
@ -43,8 +46,26 @@ const ManageJobTicket = ({ Job }) => {
|
|||||||
<div className="row text-start">
|
<div className="row text-start">
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<div className="d-flex justify-content-between align-items-center mb-3">
|
<div className="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<div className="d-flex flex-row gap-2">
|
||||||
<span className="badge bg-label-primary">{data?.status?.name}</span>
|
<span className="badge bg-label-primary">{data?.status?.name}</span>
|
||||||
{data?.dueDate && (() => {
|
<HoverPopup
|
||||||
|
id="STATUS_CHANEG"
|
||||||
|
title="Change Status"
|
||||||
|
Mode="click"
|
||||||
|
content={
|
||||||
|
<ChangeStatus
|
||||||
|
statusId={data?.status?.id}
|
||||||
|
projectId={projectId}
|
||||||
|
jobId={Job?.job}
|
||||||
|
popUpId={"STATUS_CHANEG"}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<i className="bx bx-edit bx-sm"></i>
|
||||||
|
</HoverPopup>
|
||||||
|
</div>
|
||||||
|
{data?.dueDate &&
|
||||||
|
(() => {
|
||||||
const { days, color } = daysLeft(data?.startDate, data?.dueDate);
|
const { days, color } = daysLeft(data?.startDate, data?.dueDate);
|
||||||
return (
|
return (
|
||||||
<span style={{ fontSize: "12px" }}>
|
<span style={{ fontSize: "12px" }}>
|
||||||
@ -55,7 +76,6 @@ const ManageJobTicket = ({ Job }) => {
|
|||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<h6 className="fs-5 fw-semibold">{data?.title}</h6>
|
<h6 className="fs-5 fw-semibold">{data?.title}</h6>
|
||||||
|
|
||||||
@ -66,30 +86,31 @@ const ManageJobTicket = ({ Job }) => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div className="d-flex justify-content-between mb-4">
|
<div className="d-flex justify-content-between mb-4">
|
||||||
<div className="d-flex flex-row gap-1 fw-medium">
|
<div className="d-flex flex-row gap-1 fw-medium">
|
||||||
<i className="bx bx-calendar"></i>{" "}
|
<i className="bx bx-calendar"></i>{" "}
|
||||||
<span>Created Date : {formatUTCToLocalTime(data?.createdAt, true)}</span>
|
<span>
|
||||||
|
Created Date : {formatUTCToLocalTime(data?.createdAt, true)}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className="d-flex flex-row gap-5">
|
<div className="d-flex flex-row gap-5">
|
||||||
<span className="fw-medium">
|
<span className="fw-medium">
|
||||||
<i className="bx bx-calendar"></i> Start Date : {formatUTCToLocalTime(data?.startDate)}
|
<i className="bx bx-calendar"></i> Start Date :{" "}
|
||||||
|
{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="fw-medium">
|
||||||
<i className="bx bx-calendar"></i> Due on : {formatUTCToLocalTime(data?.startDate)}
|
<i className="bx bx-calendar"></i> Due on :{" "}
|
||||||
|
{formatUTCToLocalTime(data?.startDate)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-block mt-4 mb-3">
|
<div className="d-block mt-4 mb-3">
|
||||||
<div className="d-flex align-items-center mb-2">
|
<div className="d-flex align-items-center mb-2">
|
||||||
<span className="fs-6 fw-medium me-5">
|
<span className="fs-6 fw-medium me-5">Created By</span>{" "}
|
||||||
Created By
|
|
||||||
</span>{" "}
|
|
||||||
<Avatar
|
<Avatar
|
||||||
size="xs"
|
size="xs"
|
||||||
firstName={data?.createdBy?.firstName}
|
firstName={data?.createdBy?.firstName}
|
||||||
@ -97,20 +118,26 @@ const ManageJobTicket = ({ Job }) => {
|
|||||||
/>{" "}
|
/>{" "}
|
||||||
<div className="d-flex flex-row align-items-center">
|
<div className="d-flex flex-row align-items-center">
|
||||||
<p className="m-0">{`${data?.createdBy?.firstName} ${data?.createdBy?.lastName}`}</p>
|
<p className="m-0">{`${data?.createdBy?.firstName} ${data?.createdBy?.lastName}`}</p>
|
||||||
<small className="text-secondary ms-1">({data?.createdBy?.jobRoleName})</small>
|
<small className="text-secondary ms-1">
|
||||||
|
({data?.createdBy?.jobRoleName})
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex align-items-center">
|
<div className="d-flex align-items-center">
|
||||||
<small className="fs-6 fw-medium me-3">
|
<small className="fs-6 fw-medium me-3">Assigned By</small>
|
||||||
Assigned By
|
|
||||||
</small>
|
|
||||||
<div className="d-flex flex-row gap-3">
|
<div className="d-flex flex-row gap-3">
|
||||||
{data?.assignees?.map((emp) => (
|
{data?.assignees?.map((emp) => (
|
||||||
<div className="d-flex flex-row ">
|
<div className="d-flex flex-row ">
|
||||||
<Avatar size="xs" firstName={emp.firstName} lastName={emp.lastName} />
|
<Avatar
|
||||||
|
size="xs"
|
||||||
|
firstName={emp.firstName}
|
||||||
|
lastName={emp.lastName}
|
||||||
|
/>
|
||||||
<div className="d-flex flex-row align-items-center">
|
<div className="d-flex flex-row align-items-center">
|
||||||
<p className="m-0">{`${emp.firstName} ${emp.lastName}`}</p>
|
<p className="m-0">{`${emp.firstName} ${emp.lastName}`}</p>
|
||||||
<small className="text-secondary ms-1">({emp.jobRoleName})</small>
|
<small className="text-secondary ms-1">
|
||||||
|
({emp.jobRoleName})
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -1,54 +1,74 @@
|
|||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { closePopup, openPopup, togglePopup } from "../../slices/localVariablesSlice";
|
||||||
|
|
||||||
|
const HoverPopup = ({ id, title, content, children, Mode = "hover" }) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const visible = useSelector((s) => s.localVariables.popups[id] || false);
|
||||||
|
|
||||||
const HoverPopup = ({ title, content, children }) => {
|
|
||||||
const [visible, setVisible] = useState(false);
|
|
||||||
const triggerRef = useRef(null);
|
const triggerRef = useRef(null);
|
||||||
const popupRef = useRef(null);
|
const popupRef = useRef(null);
|
||||||
|
|
||||||
// Toggle popup on hover or click
|
// Hover mode
|
||||||
const handleMouseEnter = () => setVisible(true);
|
const handleMouseEnter = () => {
|
||||||
const handleClick = () => setVisible((prev) => !prev);
|
if (Mode === "hover") dispatch(openPopup(id));
|
||||||
|
};
|
||||||
|
|
||||||
// Hide popup on outside click
|
const handleMouseLeave = () => {
|
||||||
|
if (Mode === "hover") dispatch(closePopup(id));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Click mode
|
||||||
|
const handleClick = (e) => {
|
||||||
|
if (Mode === "click") {
|
||||||
|
e.stopPropagation();
|
||||||
|
dispatch(togglePopup(id));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Outside click handling
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleDocumentClick = (e) => {
|
if (Mode !== "click" || !visible) return;
|
||||||
|
|
||||||
|
const handleOutside = (e) => {
|
||||||
if (
|
if (
|
||||||
!popupRef.current?.contains(e.target) &&
|
!popupRef.current?.contains(e.target) &&
|
||||||
!triggerRef.current?.contains(e.target)
|
!triggerRef.current?.contains(e.target)
|
||||||
) {
|
) {
|
||||||
setVisible(false);
|
dispatch(closePopup(id));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (visible) document.addEventListener("click", handleDocumentClick);
|
document.addEventListener("click", handleOutside);
|
||||||
return () => document.removeEventListener("click", handleDocumentClick);
|
return () => document.removeEventListener("click", handleOutside);
|
||||||
}, [visible]);
|
}, [visible, Mode, id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className="d-inline-block position-relative">
|
||||||
|
{/* Trigger element */}
|
||||||
<div
|
<div
|
||||||
className="d-inline-block position-relative text-capitalize"
|
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={() => setVisible(false)}
|
onMouseLeave={handleMouseLeave}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
style={{ cursor: "pointer" }}
|
style={{ cursor: "pointer" }}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Popup */}
|
||||||
{visible && (
|
{visible && (
|
||||||
<div
|
<div
|
||||||
ref={popupRef}
|
ref={popupRef}
|
||||||
className="bg-white border rounded shadow-sm p-3 text-start position-absolute top-100 start-500 translate-middle mt-12"
|
className="bg-white border rounded shadow-sm p-3 position-absolute top-100 start-0 mt-2"
|
||||||
|
style={{ zIndex: 1000, width: "240px" }}
|
||||||
|
onClick={(e) => e.stopPropagation()} // prevents closing when clicking inside
|
||||||
>
|
>
|
||||||
{title && (
|
{title && <h6 className="fw-semibold mb-2">{title}</h6>}
|
||||||
<h6 className="mb-2 fw-semibold text-dark" style={{ fontSize: "0.9rem" }}>
|
|
||||||
{title}
|
<div>{content}</div>
|
||||||
</h6>
|
|
||||||
)}
|
|
||||||
<div className="text-muted" style={{ fontSize: "0.85rem", lineHeight: "1.4" }}>
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useFormContext, useWatch } from "react-hook-form";
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Label from "./Label";
|
import Label from "./Label";
|
||||||
|
|
||||||
const TagInput = ({ label, name, placeholder, color = "#e9ecef", required=false, options = [] }) => {
|
const TagInput = ({ label, name, placeholder, color = "#e9ecef", required = false, options = [] }) => {
|
||||||
const { setValue, watch } = useFormContext();
|
const { setValue, watch } = useFormContext();
|
||||||
const tags = watch(name) || [];
|
const tags = watch(name) || [];
|
||||||
const [input, setInput] = useState("");
|
const [input, setInput] = useState("");
|
||||||
@ -33,7 +33,7 @@ const TagInput = ({ label, name, placeholder, color = "#e9ecef", required=false,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (e) => {
|
const handleChange = (e) => {
|
||||||
const val = e.target.value;
|
const val = e.target.value;
|
||||||
setInput(val);
|
setInput(val);
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ const handleChange = (e) => {
|
|||||||
} else {
|
} else {
|
||||||
setSuggestions([]);
|
setSuggestions([]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSuggestionClick = (sugg) => {
|
const handleSuggestionClick = (sugg) => {
|
||||||
handleAdd(sugg);
|
handleAdd(sugg);
|
||||||
@ -105,6 +105,9 @@ const handleChange = (e) => {
|
|||||||
outline: "none",
|
outline: "none",
|
||||||
flex: 1,
|
flex: 1,
|
||||||
minWidth: "120px",
|
minWidth: "120px",
|
||||||
|
backgroundColor: "white",
|
||||||
|
color: "black"
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -296,13 +296,14 @@ export const useOrganizationType = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useJobStatus=()=>{
|
export const useJobStatus=(statusId,projectId)=>{
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey:["Job_Staus"],
|
queryKey:["Job_Staus",statusId,projectId],
|
||||||
queryFn:async()=>{
|
queryFn:async()=>{
|
||||||
const resp = await MasterRespository.getJobStatus();
|
const resp = await MasterRespository.getJobStatus(statusId,projectId);
|
||||||
return resp.data;
|
return resp.data;
|
||||||
}
|
},
|
||||||
|
enabled:!!statusId && !!projectId
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -253,4 +253,35 @@ export const useCreateServiceProjectJob = (onSuccessCallback) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useUpdateServiceProjectJob = (onSuccessCallback) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async ({ id, payload }) => {
|
||||||
|
|
||||||
|
// Call the repository patch
|
||||||
|
const resp = await ServiceProjectRepository.UpdateJob(
|
||||||
|
id,
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["serviceProjectJobs"] });
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["service-job"] });
|
||||||
|
if (onSuccessCallback) onSuccessCallback();
|
||||||
|
showToast("Job Updated successfully", "success");
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
showToast(
|
||||||
|
error?.response?.data?.message ||
|
||||||
|
error.message ||
|
||||||
|
"Failed to update project",
|
||||||
|
"error"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|||||||
@ -5,8 +5,18 @@ import { ComingSoonPage } from "../Misc/ComingSoonPage";
|
|||||||
import ServiceProjectProfile from "../../components/ServiceProject/ServiceProjectProfile";
|
import ServiceProjectProfile from "../../components/ServiceProject/ServiceProjectProfile";
|
||||||
import Jobs from "../../components/ServiceProject/Jobs";
|
import Jobs from "../../components/ServiceProject/Jobs";
|
||||||
import ProjectTeam from "../../components/ServiceProject/ServiceProjectTeam/ProjectTeam";
|
import ProjectTeam from "../../components/ServiceProject/ServiceProjectTeam/ProjectTeam";
|
||||||
|
import { useSelectedProject } from "../../slices/apiDataManager";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { useServiceProject } from "../../hooks/useServiceProject";
|
||||||
|
|
||||||
const ServiceProjectDetail = () => {
|
const ServiceProjectDetail = () => {
|
||||||
|
const { projectId } = useParams();
|
||||||
|
const {
|
||||||
|
data: projectdata,
|
||||||
|
isLoading: isProjectLoading,
|
||||||
|
isProjectError,
|
||||||
|
} = useServiceProject(projectId);
|
||||||
|
|
||||||
const [activePill, setActivePill] = useState(
|
const [activePill, setActivePill] = useState(
|
||||||
sessionStorage.getItem("servicePrjectTab") || "profile"
|
sessionStorage.getItem("servicePrjectTab") || "profile"
|
||||||
);
|
);
|
||||||
@ -26,13 +36,14 @@ const ServiceProjectDetail = () => {
|
|||||||
return <ComingSoonPage />;
|
return <ComingSoonPage />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
<Breadcrumb
|
<Breadcrumb
|
||||||
data={[
|
data={[
|
||||||
{ label: "Home", link: "/dashboard" },
|
{ label: "Home", link: "/dashboard" },
|
||||||
{ label: "Sevice Projects", link: "/projects" },
|
{ label: "Sevice Projects", link: "/projects" },
|
||||||
// { label: projects_Details?.name || "Project", link: null },
|
{ label: projectdata?.name || "Project", link: null }
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -150,8 +150,11 @@ export const MasterRespository = {
|
|||||||
getCurrencies: () => api.get(`/api/Master/currencies/list`),
|
getCurrencies: () => api.get(`/api/Master/currencies/list`),
|
||||||
|
|
||||||
getRecurringStatus: () => api.get(`/api/Master/recurring-status/list`),
|
getRecurringStatus: () => api.get(`/api/Master/recurring-status/list`),
|
||||||
|
// Service Job JobTickets Status
|
||||||
|
getJobStatus: (statusId,projectId) =>
|
||||||
|
api.get(
|
||||||
|
`/api/Master/job-status/list?statusId=${statusId}&projectId=${projectId}`
|
||||||
|
),
|
||||||
|
|
||||||
getJobStatus: () => api.get("/api/Master/job-status/list"),
|
getTeamRole: () => api.get(`/api/Master/team-roles/list`),
|
||||||
|
|
||||||
getTeamRole:()=> api.get(`/api/Master/team-roles/list`),
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -31,4 +31,8 @@ export const ServiceProjectRepository = {
|
|||||||
`/api/ServiceProject/job/comment/list?jobTicketId=${jobTicketId}&pageSize=${pageSize}&pageNumber=${pageNumber}`
|
`/api/ServiceProject/job/comment/list?jobTicketId=${jobTicketId}&pageSize=${pageSize}&pageNumber=${pageNumber}`
|
||||||
),
|
),
|
||||||
GetJobTags: () => api.get(`/api/ServiceProject/job/tag/list`),
|
GetJobTags: () => api.get(`/api/ServiceProject/job/tag/list`),
|
||||||
|
UpdateJob: (id, patchData) =>
|
||||||
|
api.patch(`/api/ServiceProject/job/edit/${id}`, patchData, {
|
||||||
|
"Content-Type": "application/json-patch+json",
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -24,7 +24,6 @@ const validateToken = async () => {
|
|||||||
sessionStorage.getItem("refreshToken");
|
sessionStorage.getItem("refreshToken");
|
||||||
|
|
||||||
if (!refreshTokenStored){
|
if (!refreshTokenStored){
|
||||||
console.log("no refrh tokem");
|
|
||||||
removeSession()
|
removeSession()
|
||||||
return false
|
return false
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,6 +11,9 @@ const localVariablesSlice = createSlice({
|
|||||||
SelectedOrg: "",
|
SelectedOrg: "",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// PopUp
|
||||||
|
popups: {},
|
||||||
|
|
||||||
// Modal for all simple pass Name
|
// Modal for all simple pass Name
|
||||||
|
|
||||||
modals: {
|
modals: {
|
||||||
@ -127,6 +130,19 @@ const localVariablesSlice = createSlice({
|
|||||||
state.selfTenant.paymentDetailId =
|
state.selfTenant.paymentDetailId =
|
||||||
action.payload.paymentDetailId ?? state.selfTenant.paymentDetailId;
|
action.payload.paymentDetailId ?? state.selfTenant.paymentDetailId;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
openPopup: (state, action) => {
|
||||||
|
const id = action.payload;
|
||||||
|
state.popups[id] = true;
|
||||||
|
},
|
||||||
|
closePopup: (state, action) => {
|
||||||
|
const id = action.payload;
|
||||||
|
state.popups[id] = false;
|
||||||
|
},
|
||||||
|
togglePopup: (state, action) => {
|
||||||
|
const id = action.payload;
|
||||||
|
state.popups[id] = !state.popups[id];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -145,6 +161,6 @@ export const {
|
|||||||
openModal,
|
openModal,
|
||||||
closeModal,
|
closeModal,
|
||||||
toggleModal,
|
toggleModal,
|
||||||
setSelfTenant,
|
setSelfTenant,openPopup, closePopup, togglePopup
|
||||||
} = localVariablesSlice.actions;
|
} = localVariablesSlice.actions;
|
||||||
export default localVariablesSlice.reducer;
|
export default localVariablesSlice.reducer;
|
||||||
|
|||||||
@ -178,6 +178,12 @@ export const api = {
|
|||||||
headers: { ...customHeaders },
|
headers: { ...customHeaders },
|
||||||
authRequired: true,
|
authRequired: true,
|
||||||
}),
|
}),
|
||||||
|
patch: (url, data = {}, customHeaders = {}) =>
|
||||||
|
apiRequest("patch", url, data, {
|
||||||
|
headers: { ...customHeaders },
|
||||||
|
authRequired: true,
|
||||||
|
}),
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Redirect helper
|
// Redirect helper
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user