added changes status fun
This commit is contained in:
commit
d95ba4c800
@ -23,7 +23,7 @@ import Label from "../common/Label";
|
||||
const ManageContact = ({ contactId, closeModal }) => {
|
||||
// fetch master data
|
||||
const { buckets, loading: bucketsLoaging } = useBuckets();
|
||||
const { data:projects, loading: projectLoading } = useProjects();
|
||||
const { data: projects, loading: projectLoading } = useProjects();
|
||||
const { contactCategory, loading: contactCategoryLoading } =
|
||||
useContactCategory();
|
||||
const { organizationList } = useOrganization();
|
||||
@ -205,12 +205,12 @@ const ManageContact = ({ contactId, closeModal }) => {
|
||||
<Label htmlFor={"organization"} required>
|
||||
Organization
|
||||
</Label>
|
||||
<InputSuggestions
|
||||
organizationList={organizationList}
|
||||
value={watch("organization") || ""}
|
||||
onChange={(val) => setValue("organization", val, { shouldValidate: true })}
|
||||
error={errors.organization?.message}
|
||||
/>
|
||||
<InputSuggestions
|
||||
organizationList={organizationList}
|
||||
value={watch("organization") || ""}
|
||||
onChange={(val) => setValue("organization", val, { shouldValidate: true })}
|
||||
error={errors.organization?.message}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@ -408,6 +408,7 @@ const ManageContact = ({ contactId, closeModal }) => {
|
||||
label="Tags"
|
||||
options={contactTags}
|
||||
isRequired={true}
|
||||
placeholder="Enter Tag"
|
||||
/>
|
||||
{errors.tags && (
|
||||
<small className="danger-text">{errors.tags.message}</small>
|
||||
@ -482,7 +483,7 @@ const ManageContact = ({ contactId, closeModal }) => {
|
||||
<button className="btn btn-sm btn-primary" type="submit" disabled={isPending}>
|
||||
{isPending ? "Please Wait..." : "Submit"}
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</FormProvider>
|
||||
|
||||
@ -104,7 +104,6 @@ const hasChanges =
|
||||
permission: payloadPermissions,
|
||||
};
|
||||
|
||||
console.log("Final payload:", payload);
|
||||
updatePermission(payload);
|
||||
};
|
||||
|
||||
|
||||
@ -134,7 +134,6 @@ const ManageRecurringExpense = ({ closeModal, requestToEdit = null }) => {
|
||||
const StrikeDate = watch("strikeDate")
|
||||
|
||||
const onSubmit = (fromdata) => {
|
||||
console.log(fromdata);
|
||||
let payload = {
|
||||
...fromdata,
|
||||
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";
|
||||
|
||||
const JobStatusLog = ({ data }) => {
|
||||
console.log(data);
|
||||
return (
|
||||
<div className="card shadow-none p-0">
|
||||
<div className="card-body p-0">
|
||||
|
||||
@ -20,10 +20,11 @@ import {
|
||||
AppFormProvider,
|
||||
useAppForm,
|
||||
} from "../../hooks/appHooks/useAppForm";
|
||||
import { useServiceProjectJobContext } from "./Jobs";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const ManageJob = ({ Job }) => {
|
||||
const { manageJob, setManageJob } = useServiceProjectJobContext();
|
||||
const { projectId } = useParams();
|
||||
|
||||
const methods = useAppForm({
|
||||
resolver: zodResolver(jobSchema),
|
||||
defaultValues: defaultJobValue,
|
||||
@ -69,27 +70,33 @@ const ManageJob = ({ Job }) => {
|
||||
|
||||
formData.startDate = localToUtc(formData.startDate);
|
||||
formData.dueDate = localToUtc(formData.dueDate);
|
||||
formData.projectId = projectId;
|
||||
CreateJob(formData);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (manageJob.jobId && JobData) {
|
||||
console.log("freshed data");
|
||||
const assignedEmployees = (JobData.assignees || []).map((e) => e.id);
|
||||
if (!JobData && !Job) {
|
||||
reset({
|
||||
title: JobData.title ?? "",
|
||||
description: JobData.description ?? "",
|
||||
projectId: JobData.project.id ?? "",
|
||||
assignees: assignedEmployees,
|
||||
startDate: JobData.startDate ?? null,
|
||||
dueDate: JobData.dueDate ?? null,
|
||||
tags: JobData.tags ?? [],
|
||||
...defaultJobValue,
|
||||
projectId: projectId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!manageJob.jobId) {
|
||||
reset(defaultJobValue);
|
||||
}
|
||||
}, [JobData, manageJob]);
|
||||
|
||||
if (!JobData || !Job) return;
|
||||
|
||||
const assignedEmployees = (JobData.assignees || []).map((e) => e.id);
|
||||
|
||||
reset({
|
||||
title: JobData.title ?? "",
|
||||
description: JobData.description ?? "",
|
||||
projectId: JobData.project?.id ?? projectId,
|
||||
assignees: assignedEmployees,
|
||||
startDate: JobData.startDate ?? null,
|
||||
dueDate: JobData.dueDate ?? null,
|
||||
tags: JobData.tags ?? [],
|
||||
});
|
||||
}, [JobData, Job, projectId]);
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
@ -101,27 +108,10 @@ const ManageJob = ({ Job }) => {
|
||||
type="text"
|
||||
{...register("title")}
|
||||
className="form-control form-control"
|
||||
placeholder="Enter Title"
|
||||
/>
|
||||
</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">
|
||||
<Label required>Start Date</Label>
|
||||
<DatePicker
|
||||
@ -155,6 +145,7 @@ const ManageJob = ({ Job }) => {
|
||||
options={JobTags?.data}
|
||||
name="tags"
|
||||
label="Tag"
|
||||
placeholder="Enter Tag"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -8,8 +8,12 @@ import EmployeeAvatarGroup from "../common/EmployeeAvatarGroup";
|
||||
import JobStatusLog from "./JobStatusLog";
|
||||
import JobComments from "./JobComments";
|
||||
import { daysLeft } from "../../utils/appUtils";
|
||||
import HoverPopup from "../common/HoverPopup";
|
||||
import ChangeStatus from "./ChangeStatus";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const ManageJobTicket = ({ Job }) => {
|
||||
const { projectId } = useParams();
|
||||
const { data, isLoading, isError, error } = useServiceProjectJobDetails(
|
||||
Job?.job
|
||||
);
|
||||
@ -31,7 +35,6 @@ const ManageJobTicket = ({ Job }) => {
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
if (isLoading) return <SpinnerLoader />;
|
||||
if (isError)
|
||||
return (
|
||||
@ -43,19 +46,36 @@ const ManageJobTicket = ({ Job }) => {
|
||||
<div className="row text-start">
|
||||
<div className="col-12">
|
||||
<div className="d-flex justify-content-between align-items-center mb-3">
|
||||
<span className="badge bg-label-primary">{data?.status?.name}</span>
|
||||
{data?.dueDate && (() => {
|
||||
const { days, color } = daysLeft(data?.startDate, data?.dueDate);
|
||||
return (
|
||||
<span style={{ fontSize: "12px" }}>
|
||||
<span className="fw-medium me-1">Days Left :</span>
|
||||
<span className={`badge bg-${color}`}>
|
||||
{days !== null ? `${days} days` : "N/A"}
|
||||
<div className="d-flex flex-row gap-2">
|
||||
<span className="badge bg-label-primary">{data?.status?.name}</span>
|
||||
<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);
|
||||
return (
|
||||
<span style={{ fontSize: "12px" }}>
|
||||
<span className="fw-medium me-1">Days Left :</span>
|
||||
<span className={`badge bg-${color}`}>
|
||||
{days !== null ? `${days} days` : "N/A"}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
})()}
|
||||
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
<h6 className="fs-5 fw-semibold">{data?.title}</h6>
|
||||
|
||||
@ -66,30 +86,31 @@ const ManageJobTicket = ({ Job }) => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="d-flex justify-content-between mb-4">
|
||||
<div className="d-flex flex-row gap-1 fw-medium">
|
||||
<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 className="d-flex flex-row gap-5">
|
||||
<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>{" "}
|
||||
<i className="bx bx-right-arrow-alt"></i>{" "}
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-block mt-4 mb-3">
|
||||
<div className="d-flex align-items-center mb-2">
|
||||
<span className="fs-6 fw-medium me-5">
|
||||
Created By
|
||||
</span>{" "}
|
||||
<span className="fs-6 fw-medium me-5">Created By</span>{" "}
|
||||
<Avatar
|
||||
size="xs"
|
||||
firstName={data?.createdBy?.firstName}
|
||||
@ -97,20 +118,26 @@ const ManageJobTicket = ({ Job }) => {
|
||||
/>{" "}
|
||||
<div className="d-flex flex-row align-items-center">
|
||||
<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 className="d-flex align-items-center">
|
||||
<small className="fs-6 fw-medium me-3">
|
||||
Assigned By
|
||||
</small>
|
||||
<small className="fs-6 fw-medium me-3">Assigned By</small>
|
||||
<div className="d-flex flex-row gap-3">
|
||||
{data?.assignees?.map((emp) => (
|
||||
<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">
|
||||
<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>
|
||||
))}
|
||||
|
||||
@ -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 popupRef = useRef(null);
|
||||
|
||||
// Toggle popup on hover or click
|
||||
const handleMouseEnter = () => setVisible(true);
|
||||
const handleClick = () => setVisible((prev) => !prev);
|
||||
// Hover mode
|
||||
const handleMouseEnter = () => {
|
||||
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(() => {
|
||||
const handleDocumentClick = (e) => {
|
||||
if (Mode !== "click" || !visible) return;
|
||||
|
||||
const handleOutside = (e) => {
|
||||
if (
|
||||
!popupRef.current?.contains(e.target) &&
|
||||
!triggerRef.current?.contains(e.target)
|
||||
) {
|
||||
setVisible(false);
|
||||
dispatch(closePopup(id));
|
||||
}
|
||||
};
|
||||
|
||||
if (visible) document.addEventListener("click", handleDocumentClick);
|
||||
return () => document.removeEventListener("click", handleDocumentClick);
|
||||
}, [visible]);
|
||||
document.addEventListener("click", handleOutside);
|
||||
return () => document.removeEventListener("click", handleOutside);
|
||||
}, [visible, Mode, id]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="d-inline-block position-relative text-capitalize"
|
||||
ref={triggerRef}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={() => setVisible(false)}
|
||||
onClick={handleClick}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
{children}
|
||||
<div className="d-inline-block position-relative">
|
||||
{/* Trigger element */}
|
||||
<div
|
||||
ref={triggerRef}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onClick={handleClick}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
{/* Popup */}
|
||||
{visible && (
|
||||
<div
|
||||
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 && (
|
||||
<h6 className="mb-2 fw-semibold text-dark" style={{ fontSize: "0.9rem" }}>
|
||||
{title}
|
||||
</h6>
|
||||
)}
|
||||
<div className="text-muted" style={{ fontSize: "0.85rem", lineHeight: "1.4" }}>
|
||||
{content}
|
||||
</div>
|
||||
{title && <h6 className="fw-semibold mb-2">{title}</h6>}
|
||||
|
||||
<div>{content}</div>
|
||||
|
||||
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,7 @@ import { useFormContext, useWatch } from "react-hook-form";
|
||||
import React, { useEffect, useState } from "react";
|
||||
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 tags = watch(name) || [];
|
||||
const [input, setInput] = useState("");
|
||||
@ -33,29 +33,29 @@ const TagInput = ({ label, name, placeholder, color = "#e9ecef", required=false,
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
const val = e.target.value;
|
||||
setInput(val);
|
||||
const handleChange = (e) => {
|
||||
const val = e.target.value;
|
||||
setInput(val);
|
||||
|
||||
if (val) {
|
||||
setSuggestions(
|
||||
options
|
||||
.filter((opt) => {
|
||||
const label = typeof opt === "string" ? opt : opt.name;
|
||||
return (
|
||||
label.toLowerCase().includes(val.toLowerCase()) &&
|
||||
!tags.some((t) => t.name === label)
|
||||
);
|
||||
})
|
||||
.map((opt) => ({
|
||||
name: typeof opt === "string" ? opt : opt.name,
|
||||
isActive: true,
|
||||
}))
|
||||
);
|
||||
} else {
|
||||
setSuggestions([]);
|
||||
}
|
||||
};
|
||||
if (val) {
|
||||
setSuggestions(
|
||||
options
|
||||
.filter((opt) => {
|
||||
const label = typeof opt === "string" ? opt : opt.name;
|
||||
return (
|
||||
label.toLowerCase().includes(val.toLowerCase()) &&
|
||||
!tags.some((t) => t.name === label)
|
||||
);
|
||||
})
|
||||
.map((opt) => ({
|
||||
name: typeof opt === "string" ? opt : opt.name,
|
||||
isActive: true,
|
||||
}))
|
||||
);
|
||||
} else {
|
||||
setSuggestions([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSuggestionClick = (sugg) => {
|
||||
handleAdd(sugg);
|
||||
@ -105,6 +105,9 @@ const handleChange = (e) => {
|
||||
outline: "none",
|
||||
flex: 1,
|
||||
minWidth: "120px",
|
||||
backgroundColor: "white",
|
||||
color: "black"
|
||||
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -296,13 +296,14 @@ export const useOrganizationType = () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const useJobStatus=()=>{
|
||||
export const useJobStatus=(statusId,projectId)=>{
|
||||
return useQuery({
|
||||
queryKey:["Job_Staus"],
|
||||
queryKey:["Job_Staus",statusId,projectId],
|
||||
queryFn:async()=>{
|
||||
const resp = await MasterRespository.getJobStatus();
|
||||
const resp = await MasterRespository.getJobStatus(statusId,projectId);
|
||||
return resp.data;
|
||||
}
|
||||
},
|
||||
enabled:!!statusId && !!projectId
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -190,7 +190,7 @@ export const useJobComments = (jobId, pageSize, pageNumber) => {
|
||||
return allPages.length + pageNumber;
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
export const useJobTags = () => {
|
||||
return useQuery({
|
||||
queryKey: ["Job_Tags"],
|
||||
@ -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
|
||||
|
||||
@ -5,8 +5,18 @@ import { ComingSoonPage } from "../Misc/ComingSoonPage";
|
||||
import ServiceProjectProfile from "../../components/ServiceProject/ServiceProjectProfile";
|
||||
import Jobs from "../../components/ServiceProject/Jobs";
|
||||
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 { projectId } = useParams();
|
||||
const {
|
||||
data: projectdata,
|
||||
isLoading: isProjectLoading,
|
||||
isProjectError,
|
||||
} = useServiceProject(projectId);
|
||||
|
||||
const [activePill, setActivePill] = useState(
|
||||
sessionStorage.getItem("servicePrjectTab") || "profile"
|
||||
);
|
||||
@ -18,21 +28,22 @@ const ServiceProjectDetail = () => {
|
||||
switch (activePill) {
|
||||
case "profile":
|
||||
return <ServiceProjectProfile />;
|
||||
case "teams":
|
||||
case "teams":
|
||||
return <ProjectTeam />;
|
||||
case "jobs":
|
||||
case "jobs":
|
||||
return <Jobs />;
|
||||
default:
|
||||
return <ComingSoonPage />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
<Breadcrumb
|
||||
data={[
|
||||
{ label: "Home", link: "/dashboard" },
|
||||
{ label: "Sevice Projects", link: "/projects" },
|
||||
// { label: projects_Details?.name || "Project", link: null },
|
||||
{ label: projectdata?.name || "Project", link: null }
|
||||
]}
|
||||
/>
|
||||
<div>
|
||||
|
||||
@ -150,8 +150,11 @@ export const MasterRespository = {
|
||||
getCurrencies: () => api.get(`/api/Master/currencies/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}`
|
||||
),
|
||||
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");
|
||||
|
||||
if (!refreshTokenStored){
|
||||
console.log("no refrh tokem");
|
||||
removeSession()
|
||||
return false
|
||||
};
|
||||
|
||||
@ -11,6 +11,9 @@ const localVariablesSlice = createSlice({
|
||||
SelectedOrg: "",
|
||||
},
|
||||
|
||||
// PopUp
|
||||
popups: {},
|
||||
|
||||
// Modal for all simple pass Name
|
||||
|
||||
modals: {
|
||||
@ -127,6 +130,19 @@ const localVariablesSlice = createSlice({
|
||||
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,
|
||||
closeModal,
|
||||
toggleModal,
|
||||
setSelfTenant,
|
||||
setSelfTenant,openPopup, closePopup, togglePopup
|
||||
} = localVariablesSlice.actions;
|
||||
export default localVariablesSlice.reducer;
|
||||
|
||||
@ -178,6 +178,12 @@ export const api = {
|
||||
headers: { ...customHeaders },
|
||||
authRequired: true,
|
||||
}),
|
||||
patch: (url, data = {}, customHeaders = {}) =>
|
||||
apiRequest("patch", url, data, {
|
||||
headers: { ...customHeaders },
|
||||
authRequired: true,
|
||||
}),
|
||||
|
||||
};
|
||||
|
||||
// Redirect helper
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user