initially setup of service management
This commit is contained in:
parent
69cc3b9383
commit
ca7b0cda13
@ -17,6 +17,7 @@ import ManageExpenseStatus from "./ManageExpenseStatus";
|
|||||||
import ManageDocumentCategory from "./ManageDocumentCategory";
|
import ManageDocumentCategory from "./ManageDocumentCategory";
|
||||||
import ManageDocumentType from "./ManageDocumentType";
|
import ManageDocumentType from "./ManageDocumentType";
|
||||||
import ManageServices from "./Services/ManageServices";
|
import ManageServices from "./Services/ManageServices";
|
||||||
|
import ServiceGroups from "./Services/ServicesGroups";
|
||||||
|
|
||||||
const MasterModal = ({ modaldata, closeModal }) => {
|
const MasterModal = ({ modaldata, closeModal }) => {
|
||||||
if (!modaldata?.modalType || modaldata.modalType === "delete") {
|
if (!modaldata?.modalType || modaldata.modalType === "delete") {
|
||||||
@ -67,6 +68,9 @@ const MasterModal = ({ modaldata, closeModal }) => {
|
|||||||
"Edit-Services": (
|
"Edit-Services": (
|
||||||
<ManageServices data={item} onClose={closeModal} />
|
<ManageServices data={item} onClose={closeModal} />
|
||||||
),
|
),
|
||||||
|
"Manage-Services": (
|
||||||
|
<ServiceGroups service={item} onClose={closeModal}/>
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
return modalComponents[modalType] || null;
|
return modalComponents[modalType] || null;
|
||||||
|
71
src/components/master/Services/ManageGroup.jsx
Normal file
71
src/components/master/Services/ManageGroup.jsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { useCreateActivityGroup } from "../../../hooks/masterHook/useMaster";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { ActivityGroupSchema } from "./ServicesSchema";
|
||||||
|
import Label from "../../common/Label";
|
||||||
|
|
||||||
|
const ManageGroup = ({ group = null, close }) => {
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm({
|
||||||
|
resolver: zodResolver(ActivityGroupSchema),
|
||||||
|
defaultValues: { name: "", description: "" },
|
||||||
|
});
|
||||||
|
const { mutate: createGroup, isPending } = useCreateActivityGroup();
|
||||||
|
|
||||||
|
const onSubmit = (payload) => {
|
||||||
|
console.log(payload);
|
||||||
|
// createGroup
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<form className="row px-2" onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<div className="col-12 col-md-12 text-start">
|
||||||
|
<Label className="form-label" required>
|
||||||
|
Group Name
|
||||||
|
</Label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
{...register("name")}
|
||||||
|
className={`form-control form-control-sm ${errors.name ? "is-invalids" : ""}`}
|
||||||
|
/>
|
||||||
|
{errors.name && <p className="danger-text m-0">{errors.name.message}</p>}
|
||||||
|
</div>
|
||||||
|
<div className="col-12 col-md-12 text-start mb-2">
|
||||||
|
<Label className="form-label" htmlFor="description" required>
|
||||||
|
Description
|
||||||
|
</Label>
|
||||||
|
<textarea
|
||||||
|
rows="3"
|
||||||
|
{...register("description")}
|
||||||
|
className={`form-control form-control-sm ${errors.description ? "is-invalids" : ""}`}
|
||||||
|
></textarea>
|
||||||
|
|
||||||
|
{errors.description && (
|
||||||
|
<p className="danger-text m-0">{errors.description.message}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-12 text-end">
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-label-secondary me-3"
|
||||||
|
aria-label="Close"
|
||||||
|
disabled={isPending}
|
||||||
|
onClick={close}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn btn-sm btn-primary"
|
||||||
|
disabled={isPending}
|
||||||
|
>
|
||||||
|
{isPending ? "Please Wait..." : group ? "Update" : "Submit"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ManageGroup;
|
133
src/components/master/Services/ServicesGroups.jsx
Normal file
133
src/components/master/Services/ServicesGroups.jsx
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
useActivitiesByGroups,
|
||||||
|
useGroups,
|
||||||
|
} from "../../../hooks/masterHook/useMaster";
|
||||||
|
import ManageGroup from "./ManageGroup";
|
||||||
|
|
||||||
|
const ServiceGroups = ({ service }) => {
|
||||||
|
const [openService, setOpenService] = useState(true);
|
||||||
|
const [activeGroupId, setActiveGroupId] = useState(null); // track selected group
|
||||||
|
const [isManageGroup,setManageGroup] = useState({isOpen:false,group:true})
|
||||||
|
|
||||||
|
const { data: groups, isLoading } = useGroups(service?.id);
|
||||||
|
const { data: activities, isLoading: actLoading } =
|
||||||
|
useActivitiesByGroups(activeGroupId);
|
||||||
|
|
||||||
|
if (isLoading) return <div>Loading groups...</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-100 my-2">
|
||||||
|
<p className="fs-5 fw-semibold">Manage Service</p>
|
||||||
|
<div className="accordion" id="accordionExample">
|
||||||
|
<div className="accordion-item active shadow-none">
|
||||||
|
{/* Service Header */}
|
||||||
|
<div className="d-flex justify-content-between text-start accordion-header ga">
|
||||||
|
<p className="m-0 fw-bold fs-6 ">{service.name}</p>
|
||||||
|
<span
|
||||||
|
onClick={() => setOpenService(!openService)}
|
||||||
|
className="text-end cursor-pointer"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#accordionOne"
|
||||||
|
aria-expanded={openService}
|
||||||
|
aria-controls="accordionOne"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className={`bx ${
|
||||||
|
openService ? "bx-chevron-up" : "bx-chevron-down"
|
||||||
|
}`}
|
||||||
|
></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Groups Section */}
|
||||||
|
<div
|
||||||
|
id="accordionOne"
|
||||||
|
className={`accordion-collapse collapse ${
|
||||||
|
openService ? "show" : ""
|
||||||
|
}`}
|
||||||
|
aria-labelledby="headingOne"
|
||||||
|
data-bs-parent="#accordionExample"
|
||||||
|
>
|
||||||
|
<div className="accordion-body text-start m-0 p-0">
|
||||||
|
<div className="dropdown-divider border"></div>
|
||||||
|
{groups?.data?.map((group) => {
|
||||||
|
const isOpen = activeGroupId === group.id;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="accordion-item shadow-none m-0 py-2 px-2"
|
||||||
|
key={group.id}
|
||||||
|
>
|
||||||
|
<div className="d-flex justify-content-between text-start accordion-header">
|
||||||
|
<p className="m-0 fw-bold ">{group.name}</p>
|
||||||
|
<div className="d-flex flex-row gap-3">
|
||||||
|
|
||||||
|
<div className="d-flex flex-row gap-2">
|
||||||
|
<i className="bx bx-plus-circle text-primary cursor-pointer" onClick={()=>setManageGroup({isOpen:true,group:null})}></i>
|
||||||
|
<i className="bx bx-edit text-secondary cursor-pointer"></i>
|
||||||
|
<i className="bx bx-trash text-danger cursor-pointer"></i>
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
onClick={
|
||||||
|
() => setActiveGroupId(isOpen ? null : group.id)
|
||||||
|
}
|
||||||
|
className="text-end cursor-pointer"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target={`#accordionGroup${group.id}`}
|
||||||
|
aria-expanded={isOpen}
|
||||||
|
aria-controls={`accordionGroup${group.id}`}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className={`bx bx-lg ${
|
||||||
|
isOpen ? "bx-chevron-up" : "bx-chevron-down"
|
||||||
|
}`}
|
||||||
|
></i>
|
||||||
|
</span></div>
|
||||||
|
</div>
|
||||||
|
{isManageGroup.isOpen ? <ManageGroup group={ManageGroup.group} close={()=>setManageGroup({isOpen:false,group:null})}/> : (
|
||||||
|
<div
|
||||||
|
id={`accordionGroup${group.id}`}
|
||||||
|
className={`accordion-collapse collapse ${
|
||||||
|
isOpen ? "show" : ""
|
||||||
|
}`}
|
||||||
|
aria-labelledby={group.id}
|
||||||
|
data-bs-parent="#accordionOne"
|
||||||
|
>
|
||||||
|
<div className="accordion-body">
|
||||||
|
{isOpen && actLoading && <p>Loading activities...</p>}
|
||||||
|
{isOpen && activities?.data?.length > 0 ? (
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
{activities.data.map((activity) => (
|
||||||
|
<div className="d-flex justify-content-between py-2">
|
||||||
|
<li className="list-group-item border-0">
|
||||||
|
{activity.name}
|
||||||
|
</li>{" "}
|
||||||
|
<div className="d-flex flex-row gap-2">
|
||||||
|
<i class="bx bx-sm bx-plus-circle text-primary cursor-pointer"></i>
|
||||||
|
<i class="bx bx-sm bx-edit text-secondary cursor-pointer"></i>
|
||||||
|
<i class="bx bx-sm bx-trash text-danger cursor-pointer"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
isOpen && <p>No activities found</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) }
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServiceGroups;
|
@ -6,4 +6,12 @@ const schema = z.object({
|
|||||||
.string()
|
.string()
|
||||||
.min(1, { message: "Description is required" })
|
.min(1, { message: "Description is required" })
|
||||||
.max(255, { message: "Description cannot exceed 255 characters" }),
|
.max(255, { message: "Description cannot exceed 255 characters" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ActivityGroupSchema = z.object({
|
||||||
|
name: z.string().min(1, { message: "Group Name is required" }),
|
||||||
|
description: z
|
||||||
|
.string()
|
||||||
|
.min(1, { message: "Description is required" })
|
||||||
|
.max(255, { message: "Description cannot exceed 255 characters" }),
|
||||||
});
|
});
|
File diff suppressed because it is too large
Load Diff
@ -158,6 +158,21 @@ const MasterTable = ({ data, columns, loading, handleModalData }) => {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
{selectedMaster === "Services" && (
|
||||||
|
<button
|
||||||
|
aria-label="View"
|
||||||
|
type="button"
|
||||||
|
className="btn p-0 dropdown-toggle hide-arrow"
|
||||||
|
onClick={() =>
|
||||||
|
handleModalData(`Manage-${selectedMaster}`, item, selectedMaster)
|
||||||
|
}
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#servicesActivityTreeModal"
|
||||||
|
>
|
||||||
|
<i className="bx bx-show me-2 text-primary"></i>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
aria-label="Modify"
|
aria-label="Modify"
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -118,6 +118,11 @@ export const MasterRespository = {
|
|||||||
|
|
||||||
getGlobalServices:()=>api.get("/api/Master/global-service/list"),
|
getGlobalServices:()=>api.get("/api/Master/global-service/list"),
|
||||||
getMasterServices:()=>api.get("/api/Master/service/list"),
|
getMasterServices:()=>api.get("/api/Master/service/list"),
|
||||||
|
getActivityGrops:(serviceId)=>api.get(`/api/Master/activity-group/list?serviceId=${serviceId}`),
|
||||||
|
createActivityGroup:(data)=>api.post(`/api/Master/activity-group/create`),
|
||||||
|
getActivitesByGroup: (serviceId) => api.get(`api/master/activities/activityGroupId=${activityGroupId}`),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
getOrganizationType:()=>api.get('/api/Master/organization-type/list')
|
getOrganizationType:()=>api.get('/api/Master/organization-type/list')
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user