added offcanavs for create and update

This commit is contained in:
pramod.mahajan 2025-11-14 16:39:46 +05:30
parent dab3c5fe52
commit 7190a3c190
8 changed files with 202 additions and 144 deletions

View File

@ -94,7 +94,6 @@ const JobComments = ({ data }) => {
const newFiles = files.filter((_, i) => i !== index);
setValue("attachments", newFiles, { shouldValidate: true });
};
console.log(watch("attachments"))
return (
<div className="row">
<div className="sticky-section bg-white p-3">

View File

@ -9,9 +9,8 @@ import { useParams } from "react-router-dom";
import ProjectPage from "../../pages/project/ProjectPage";
import { useServiceProjectJobContext } from "./Jobs";
const JobList = ({ filterByProject }) => {
const {setSelectedJob} = useServiceProjectJobContext()
const { setSelectedJob, setManageJob } = useServiceProjectJobContext();
const { id } = useParams();
const { data, isLoading, isError, error } = useServiceProjectJobs(
ITEMS_PER_PAGE,
@ -112,12 +111,22 @@ const JobList = ({ filterByProject }) => {
</button>
<div className="dropdown-menu dropdown-menu-end">
{/* View always visible */}
<button className="dropdown-item py-1" onClick={()=>setSelectedJob({showCanvas:true,job:row?.id})}>
<button
className="dropdown-item py-1"
onClick={() =>
setSelectedJob({ showCanvas: true, job: row?.id })
}
>
<i className="bx bx-detail bx-sm"></i> View
</button>
<>
<button className="dropdown-item py-1">
<button
className="dropdown-item py-1"
onClick={() =>
setManageJob({ isOpen: true, jobId: row?.id })
}
>
<i className="bx bx-edit bx-sm"></i> Edit
</button>
</>

View File

@ -20,15 +20,21 @@ export const useServiceProjectJobContext = () => {
return context;
};
const Jobs = () => {
const [manageJob, setManageJob] = useState({ isOpen: false, jobId: null });
const [showCanvas, setShowCanvas] = useState(false);
const [selectedProject, setSelectedProject] = useState(null);
const [selectJob,setSelectedJob] = useState({showCanvas:false,job:null})
const [selectJob, setSelectedJob] = useState({
showCanvas: false,
job: null,
});
const navigate = useNavigate();
const { data } = useServiceProjects(ITEMS_PER_PAGE, 1);
const contextProvider = {
setSelectedJob
}
setSelectedJob,
setSelectedProject,
setManageJob,
};
return (
<>
<JonContext.Provider value={contextProvider}>
@ -41,6 +47,15 @@ const Jobs = () => {
>
<ManageJobTicket Job={selectJob} />
</OffcanvasComponent>
<OffcanvasComponent
id="customCanvas1"
title={`${manageJob.jobId ? "Update a Job" : "Create a new Job"} `}
placement="end"
show={manageJob.isOpen}
onClose={() => setManageJob({ isOpen: false, jobId: null })}
>
<ManageJob Job={manageJob.jobId} />
</OffcanvasComponent>
<div className="card page-min-h my-2 px-4">
<div className="row">
<div className="col-12 py-2 d-flex justify-content-between ">
@ -64,7 +79,7 @@ const Jobs = () => {
<div className="px-2">
<button
className="btn btn-sm btn-primary"
onClick={() => navigate("/service/job")}
onClick={() => setManageJob({ isOpen: true, jobId: null })}
>
<i className="bx bx-plus-circle bx-md me-2"></i>New Job
</button>
@ -74,7 +89,6 @@ const Jobs = () => {
<JobList filterByProject={selectedProject} />
</div>
</div>
</JonContext.Provider>
</>
);

View File

@ -1,10 +1,12 @@
import React from "react";
import React, { useEffect } from "react";
import Breadcrumb from "../common/Breadcrumb";
import Label from "../common/Label";
import { zodResolver } from "@hookform/resolvers/zod";
import { defaultJobValue, jobSchema } from "./ServiceProjectSchema";
import {
useCreateServiceProjectJob,
useJobTags,
useServiceProjectJobDetails,
useServiceProjects,
} from "../../hooks/useServiceProject";
import { ITEMS_PER_PAGE } from "../../utils/constants";
@ -19,7 +21,7 @@ import {
useAppForm,
} from "../../hooks/appHooks/useAppForm";
const ManageJob = () => {
const ManageJob = ({ Job }) => {
const methods = useAppForm({
resolver: zodResolver(jobSchema),
defaultValues: defaultJobValue,
@ -40,10 +42,23 @@ const ManageJob = () => {
isError: isProjectError,
error,
} = useServiceProjects(ITEMS_PER_PAGE, 1);
const {
data: JobTags,
isLoading: isTagLoading,
isError: isTagError,
error: tagError,
} = useJobTags();
const {
data: JobData,
isLoading: isJobLoading,
isError: isJobError,
error: jobError,
} = useServiceProjectJobDetails(Job);
const { mutate: CreateJob, isPending } = useCreateServiceProjectJob(() => {
reset();
});
const onSubmit = (formData) => {
formData.assignees = formData.assignees.map((emp) => ({
employeeId: emp,
@ -52,27 +67,27 @@ const ManageJob = () => {
formData.startDate = localToUtc(formData.startDate);
formData.dueDate = localToUtc(formData.dueDate);
// CreateJob(formData);
console.log(formData);
CreateJob(formData)
};
console.log(errors);
useEffect(() => {
if (!JobData || !Job) return;
const assignedEmployees = (JobData.assignees || []).map((e) => e.id);
reset({
title: JobData.title ?? "",
description: JobData.description ?? "",
projectId: JobData.project.id ?? "",
assignees: assignedEmployees,
startDate: JobData.startDate ?? null,
dueDate: JobData.dueDate ?? null,
tags: [],
});
}, [JobData]);
return (
<div className="container-fluid">
<Breadcrumb
data={[
{ label: "Home", link: "/dashboard" },
{ label: "Sevice Projects", link: "/projects" },
{ label: "" || "Jobs", link: null },
]}
/>
<div className="card m-auto page-min-h">
<div className="col-md-4 col-12 p-3"></div>
<div className="col-md-8 p-3">
<div className="container">
<AppFormProvider {...methods}>
<form className="row text-start" onSubmit={handleSubmit(onSubmit)}>
<div>
<p className="fs-5 fw-medium">Create Job</p>
</div>
<div className="col-12 col-md-6 mb-2 ">
<Label required>Title</Label>
<input
@ -127,7 +142,12 @@ const ManageJob = () => {
/>
</div>
<div className="col-12 col-md-6 mb-2 mb-md-4">
<TagInput name="tags" label="Tag" required />
<TagInput
options={JobTags?.data}
name="tags"
label="Tag"
required
/>
</div>
<div className="col-12">
<Label required>Description</Label>
@ -137,7 +157,7 @@ const ManageJob = () => {
rows={3}
/>
</div>
<div className="d-flex flex-row-reverse my-2">
<div className="d-flex flex-row-reverse my-4">
<button
type="submit"
className="btn btn-sm btn-primary"
@ -149,8 +169,6 @@ const ManageJob = () => {
</form>
</AppFormProvider>
</div>
</div>
</div>
);
};

View File

@ -1,4 +1,4 @@
import React from 'react'
import React from "react";
const ServiceProjectNav = ({ onPillClick, activePill }) => {
const ProjectTab = [
@ -16,8 +16,6 @@ const ServiceProjectNav = ({ onPillClick, activePill }) => {
icon: "bx bxs-contact",
label: "Directory",
},
];
return (
<div className="table-responsive">
@ -26,8 +24,8 @@ const ServiceProjectNav = ({ onPillClick, activePill }) => {
{ProjectTab?.filter((tab) => !tab.hidden)?.map((tab) => (
<li key={tab.key} className="nav-item cursor-pointer">
<a
className={`nav-link ${activePill === tab.key ? "active cursor-pointer" : ""
className={`nav-link ${
activePill === tab.key ? "active cursor-pointer" : ""
} fs-6`}
onClick={(e) => {
e.preventDefault();
@ -42,7 +40,7 @@ const ServiceProjectNav = ({ onPillClick, activePill }) => {
</ul>
</div>
</div>
)
}
);
};
export default ServiceProjectNav
export default ServiceProjectNav;

View File

@ -1,5 +1,6 @@
import { z } from "zod";
//#region Service Project
export const projectSchema = z.object({
name: z.string().min(1, "Name is required"),
shortName: z.string().min(1, "Short name is required"),
@ -31,12 +32,15 @@ export const defaultProjectValues = {
contactEmail: "",
};
//#endregion
//#region JobSchema
export const TagSchema = z.object({
name: z.string().min(1, "Tag name is required"),
isActive: z.boolean().default(true),
});
export const jobSchema = z.object({
title: z.string().min(1, "Title is required"),
description: z.string().min(1, "Description is required"),
@ -57,6 +61,7 @@ const ALLOWED_TYPES = [
"image/jpg",
"image/jpeg",
];
export const JobCommentSchema = z.object({
comment: z.string().min(1, { message: "Please leave comment" }),
attachments: z
@ -75,9 +80,8 @@ export const JobCommentSchema = z.object({
)
.optional()
.default([]),
});
export const defaultJobValue = {
title: "",
description: "",

View File

@ -1,4 +1,9 @@
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
useInfiniteQuery,
useMutation,
useQuery,
useQueryClient,
} from "@tanstack/react-query";
import { ServiceProjectRepository } from "../repositories/ServiceProjectRepository";
import showToast from "../services/toastService";
@ -126,7 +131,11 @@ export const useJobComments = (jobId, pageSize, pageNumber ) => {
queryKey: ["jobComments", jobId, pageSize],
queryFn: async ({ pageParam = pageNumber }) => {
const resp = await ServiceProjectRepository.GetJobComment(jobId, pageSize, pageParam);
const resp = await ServiceProjectRepository.GetJobComment(
jobId,
pageSize,
pageParam
);
return resp.data;
},
@ -139,6 +148,12 @@ export const useJobComments = (jobId, pageSize, pageNumber ) => {
},
});
};
export const useJobTags = () => {
return useQuery({
queryKey: ["Job_Tags"],
queryFn: async () => await ServiceProjectRepository.GetJobTags(),
});
};
export const useServiceProjectJobDetails = (job) => {
return useQuery({

View File

@ -25,4 +25,5 @@ export const ServiceProjectRepository = {
api.get(
`/api/ServiceProject/job/comment/list?jobTicketId=${jobTicketId}&pageSize=${pageSize}&pageNumber=${pageNumber}`
),
GetJobTags: () => api.get(`/api/ServiceProject/job/tag/list`),
};