fetched assigned organozation to project

This commit is contained in:
pramod mahajan 2025-09-20 13:59:20 +05:30
parent 158c934a9f
commit 7dafd4a45f
10 changed files with 176 additions and 422 deletions

View File

@ -1,5 +1,4 @@
import React, { useEffect } from 'react'
// import ManageOrganization from './components/Organization/ManageOrganization'
import { useOrganizationModal } from './hooks/useOrganization';
import OrganizationModal from './components/Organization/OrganizationModal';

View File

@ -15,7 +15,7 @@ import {
} from "../../hooks/useOrganization";
const AssignOrg = ({ setStep }) => {
const { isOpen, orgData, startStep, onOpen, flowType, prevStep,onClose } =
const { isOpen, orgData, startStep, onOpen, flowType, prevStep, onClose } =
useOrganizationModal();
const selectedProject = useSelectedProject();
const { data: masterService, isLoading: isMasterserviceLoading } =
@ -24,7 +24,9 @@ const AssignOrg = ({ setStep }) => {
useProjectAssignedServices(selectedProject);
const { data: orgType, isLoading: orgLoading } = useOrganizationType();
const { mutate: AssignToProject, isPending } = useAssignOrgToProject(()=>onClose());
const { mutate: AssignToProject, isPending } = useAssignOrgToProject(() =>
onClose()
);
const mergedServices = useMemo(() => {
if (!masterService || !projectServices) return [];
@ -57,17 +59,17 @@ const AssignOrg = ({ setStep }) => {
AssignToProject(payload);
};
const handleEdit = () => {
onOpen({ startStep: 4 , orgData:orgData});
onOpen({ startStep: 4, orgData: orgData });
};
const handleBack =()=>{
if(prevStep == 1 && flowType == "assign"){
onOpen({ startStep: prevStep })
}else if(prevStep == 1 && flowType == "assign"){
onOpen({ startStep: 1 })
}else{
onOpen({ startStep: 2 })
const handleBack = () => {
if (prevStep == 1 && flowType == "assign") {
onOpen({ startStep: prevStep });
} else if (prevStep == 1 && flowType == "assign") {
onOpen({ startStep: 1 });
} else {
onOpen({ startStep: 2 });
}
}
};
if (isMasterserviceLoading || isLoading)
return <div className="text-center">Loading....</div>;
return (
@ -150,17 +152,32 @@ const AssignOrg = ({ setStep }) => {
<Label htmlFor="organizationTypeId" required>
Organization Type
</Label>
<select
{...register("organizationTypeId")}
className="form-select form-select-sm"
>
<option value="">Select type</option>
<div className="d-flex flex-wrap gap-3 mt-1">
{orgType?.data.map((type) => (
<option key={type.id} value={type.id}>
{type.name}
</option>
<div
key={type.id}
className="form-check d-flex align-items-center gap-2 p-0 m-0"
>
<input
type="radio"
id={`organizationType-${type.id}`}
value={type.id}
{...register("organizationTypeId", {
required: "Please select an organization type",
})}
className="form-check-input m-0"
/>
<label
className="form-check-label m-0"
htmlFor={`organizationType-${type.id}`}
>
{type.name}
</label>
</div>
))}
</select>
</div>
{errors.organizationTypeId && (
<span className="danger-text">
{errors.organizationTypeId.message}
@ -169,7 +186,9 @@ const AssignOrg = ({ setStep }) => {
</div>
<div className="mb-3">
<label className="form-label">Select Services</label>
<Label htmlFor="serviceId" required>
Select Services
</Label>
{mergedServices?.map((service) => (
<div key={service.id} className="form-check">
<input
@ -193,7 +212,7 @@ const AssignOrg = ({ setStep }) => {
<button
type="button"
className="btn btn-xs btn-outline-secondary"
onClick={ handleBack}
onClick={handleBack}
disabled={isPending}
>
<i className="bx bx-left-arrow-alt"></i> Back

View File

@ -1,371 +0,0 @@
import { zodResolver } from "@hookform/resolvers/zod";
import React, { useMemo, useState } from "react";
import { FormProvider, useForm } from "react-hook-form";
import {
defaultOrganizationValues,
organizationSchema,
} from "./OrganizationSchema";
import Modal from "../common/Modal";
import {
useCreateOrganization,
useOrganizationBySPRID,
useOrganizationModal,
useOrganizationsList,
} from "../../hooks/useOrganization";
import Label from "../common/Label";
import SelectMultiple from "../common/SelectMultiple";
import { useServices } from "../../hooks/masterHook/useMaster";
import AssignOrg from "./AssignOrg";
const ManageOrganization = ({
projectOrganizations ,
organizationId = null,
}) => {
const [step, setStep] = useState(1);
const orgModal = useOrganizationModal();
const { data: masterService, isLoading } = useServices();
const [searchText, setSearchText] = useState();
const [SPRID, setSPRID] = useState("");
const { data: orgList, isLoading: orgLoading } = useOrganizationsList(
20,
1,
true,
searchText
);
const { data: OrgListbySPRID, isLoading: isLoadingBySPRID } =
useOrganizationBySPRID(SPRID);
const [Organization, setOrganization] = useState({});
const method = useForm({
resolver: zodResolver(organizationSchema),
defaultValues: defaultOrganizationValues,
});
const {
handleSubmit,
register,
reset,
formState: { errors },
} = method;
const { mutate: CreateOrganization, isPending } = useCreateOrganization(
() => {
reset(defaultOrganizationValues);
orgModal.onClose();
setStep(1); // reset to first step
}
);
const onSubmit = (OrgPayload) => {
CreateOrganization(OrgPayload);
};
const RenderTitle = useMemo(() => {
if (organizationId) {
return "Update Organization";
}
if (step === 1) {
return projectOrganizations && projectOrganizations !== null
? "Add Organization"
: "Find Organization";
}
if (step === 2) {
return "Organization Details";
}
if (step === 3) {
return "Assign Organization";
}
return "Manage Organization"; // fallback
}, [step, orgModal?.orgData, organizationId]);
const contentBody = (
<div>
{/* ---------- STEP 1: Service Provider- Form Own Tenant list ---------- */}
{step === 1 && (
<div className="d-block">
<div className="text-start mb-1">
<Label className="text-secondary">Find Organization</Label>
<input
type="text"
value={SPRID}
className="form-control form-control-sm w-auto"
placeholder="Enter Organization"
aria-describedby="search-label"
/>
</div>
<div className="py-2 text-tiny text-center">
<div className="d-flex flex-column gap-2 border-0 bg-none">
{orgList?.map((org) => (
<div className="list-group-item list-group-item-action d-flex align-items-center cursor-pointer border-0">
<div className="d-flex align-items-center justify-content-center me-3">
<i className="bx bx-building-house bx-md text-primary"></i>
</div>
<div className="w-100">
<div className="d-flex justify-content-between">
<div className="user-info text-start">
<h6 className="mb-1 fw-normal">{org.name}</h6>
<small className="text-body-secondary">
{org.contactPerson}
</small>
<div className="user-status">
<small>In Meeting</small>
</div>
</div>
<div className="add-btn">
<button
className="btn btn-primary btn-xs"
onClick={() => {
setOrganization(org);
setStep(3);
}}
>
Add
</button>
</div>
</div>
</div>
</div>
))}
</div>
{orgModal.orgData && (
<p className="text-secondary">
Don't have required organization, Please find using{" "}
<span
className="text-mutes cursor-pointer text-decoration-underline"
onClick={() => setStep(2)}
>
SPRID
</span>
</p>
)}
</div>
<div
className={`d-flex ${
projectOrganizations
? "justify-content-end"
: "justify-content-between"
} text-secondary mt-3`}
>
{!projectOrganizations && (
<button
type="button"
className="btn btn-xs btn-outline-secondary"
onClick={() => setStep(1)}
>
<i className="bx bx-left-arrow-alt"></i> Back
</button>
)}
<button
type="button"
className="btn btn-xs btn-secondary"
onClick={() => setStep(4)}
>
<i className="bx bx-plus-circle me-2"></i>
Add New Organization
</button>
</div>
</div>
)}
{/* ---------- STEP 1: Service Provider From Own Other Tenant ---------- */}
{step === 2 && (
<div className="d-block">
<div className="text-start mb-1">
<Label className="text-secondary">Find Organization</Label>
<input
type="text"
className="form-control form-control-sm w-auto"
placeholder="Enter Servicee Provider Id"
aria-describedby="search-label"
/>
</div>
{/* ======== org list ======*/}
<div className="d-flex flex-column gap-2 border-0 bg-none">
{OrgListbySPRID?.map((org) => (
<div className="list-group-item list-group-item-action d-flex align-items-center cursor-pointer border-0">
<div className="d-flex align-items-center justify-content-center me-3">
<i className="bx bx-building-house bx-md text-primary"></i>
</div>
<div className="w-100">
<div className="d-flex justify-content-between">
<div className="user-info text-start">
<h6 className="mb-1 fw-normal">Icing sweet gummies</h6>
<small className="text-body-secondary">15 minutes</small>
<div className="user-status">
<small>In Meeting</small>
</div>
</div>
<div className="add-btn">
<button
className="btn btn-primary btn-xs"
onClick={() => setStep(3)}
>
Add
</button>
</div>
</div>
</div>
</div>
))}
</div>
<div className="d-flex justify-content-between gap-2 mt-3">
<button
type="button"
className="btn btn-xs btn-outline-secondary"
onClick={() => setStep(1)}
>
<i className="bx bx-left-arrow-alt"></i> Back
</button>
<button
type="button"
className="btn btn-xs btn-secondary"
onClick={() => setStep(4)}
>
<i className="bx bx-plus-circle me-2"></i>
Add New Organization
</button>
</div>
</div>
)}
{/* ---------- STEP 2: Existing Organization Details ---------- */}
{step === 3 && (
<AssignOrg Organization={Organization} setStep={setStep}/>
)}
{/* ---------- STEP 3: Add New Organization ---------- */}
{step === 4 && (
<FormProvider {...method}>
<form className="form" onSubmit={handleSubmit(onSubmit)}>
<div className="mb-1 text-start">
<Label htmlFor="name" required>
Organization Name
</Label>
<input
className="form-control form-control-sm"
{...register("name")}
/>
{errors.name && (
<span className="danger-text">{errors.name.message}</span>
)}
</div>
<div className="mb-1 text-start">
<Label htmlFor="contactPerson" required>
Contact Person
</Label>
<input
className="form-control form-control-sm"
{...register("contactPerson")}
/>
{errors.contactPerson && (
<span className="danger-text">
{errors.contactPerson.message}
</span>
)}
</div>
<div className="mb-1 text-start">
<Label htmlFor="contactNumber" required>
Contact Number
</Label>
<input
className="form-control form-control-sm"
{...register("contactNumber")}
/>
{errors.contactNumber && (
<span className="danger-text">
{errors.contactNumber.message}
</span>
)}
</div>
<div className="mb-1 text-start">
<Label htmlFor="email" required>
Email Address
</Label>
<input
className="form-control form-control-sm"
{...register("email")}
/>
{errors.email && (
<span className="danger-text">{errors.email.message}</span>
)}
</div>
<div className="mb-1 text-start">
<SelectMultiple
name="serviceIds"
label="Services"
required
valueKey="id"
options={masterService?.data || []}
/>
{errors.serviceIds && (
<span className="danger-text">{errors.serviceIds.message}</span>
)}
</div>
<div className="mb-1 text-start">
<Label htmlFor="address" required>
Address
</Label>
<textarea
className="form-control form-control-sm"
{...register("address")}
rows={2}
/>
{errors.address && (
<span className="danger-text">{errors.address.message}</span>
)}
</div>
<div className="d-flex justify-content-between gap-2 my-2">
<button
type="button"
className="btn btn-sm btn-outline-secondary"
onClick={() => setStep(1)}
>
Back
</button>
<div>
<button
type="submit"
className="btn btn-sm btn-primary"
disabled={isPending || isLoading}
>
{isPending ? "Please Wait..." : "Submit"}
</button>
</div>
</div>
</form>
</FormProvider>
)}
</div>
);
return (
<Modal
isOpen={orgModal.isOpen}
onClose={orgModal.onClose}
title={RenderTitle}
body={contentBody}
/>
);
};
export default ManageOrganization;

View File

@ -13,7 +13,7 @@ import { OrgCardSkeleton } from "./OrganizationSkeleton";
// Zod schema: only allow exactly 4 digits
const OrgPicker2 = ({ title, placeholder }) => {
const OrgPickerFromSPId = ({ title, placeholder }) => {
const {onClose, startStep, flowType, onOpen,prevStep } = useOrganizationModal();
const {
@ -156,4 +156,4 @@ const handleOrg=(orgId)=>{
);
};
export default OrgPicker2;
export default OrgPickerFromSPId;

View File

@ -7,7 +7,7 @@ import { ITEMS_PER_PAGE } from "../../utils/constants";
import Label from "../common/Label";
import Pagination from "../common/Pagination";
const OrgPicker = ({ title }) => {
const OrgPickerfromTenant = ({ title }) => {
const [searchText, setSearchText] = useState("");
const [currentPage, setCurrentPage] = useState(1);
const { data, isLoading } = useOrganizationsList(
@ -144,4 +144,4 @@ const OrgPicker = ({ title }) => {
);
};
export default OrgPicker;
export default OrgPickerfromTenant;

View File

@ -17,8 +17,8 @@ import SelectMultiple from "../common/SelectMultiple";
import { useServices } from "../../hooks/masterHook/useMaster";
import AssignOrg from "./AssignOrg";
import ManagOrg from "./ManagOrg";
import OrgPicker from "./OrgPicker";
import OrgPicker2 from "./OrgPicker2";
import OrgPickerFromSPId from "./OrgPickerFromSPId";
import OrgPickerfromTenant from "./OrgPickerfromTenant";
const OrganizationModal = () => {
const { isOpen, orgData, startStep, onOpen, onClose, onToggle } =
@ -79,7 +79,7 @@ const OrganizationModal = () => {
<div>
{/* ---------- STEP 1: Service Provider- Form Own Tenant list ---------- */}
{startStep === 1 && (
<OrgPicker
<OrgPickerfromTenant
title="Find Organization"
@ -87,7 +87,7 @@ const OrganizationModal = () => {
)}
{startStep === 2 && (
<OrgPicker2
<OrgPickerFromSPId
title="Find Organization"
placeholder="Enter Service Provider Id"
projectOrganizations={orgData}

View File

@ -0,0 +1,99 @@
import React from "react";
import { useProjectAssignedOrganizations } from "../../../hooks/useProjects";
import { useSelectedProject } from "../../../slices/apiDataManager";
const ProjectAssignedOrgs = () => {
const selectedProject = useSelectedProject();
const { data, isLoading, isError, error } =
useProjectAssignedOrganizations(selectedProject);
const contactList = [
{
key: "name",
label: "Organization Name",
getValue: (org) => (
<div className="d-flex gap-2 py-1 ">
<i class="bx bx-buildings"></i>
<span
className="text-truncate d-inline-block "
style={{ maxWidth: "150px" }}
>
{org?.name || "N/A"}
</span>
</div>
),
align: "text-start",
},
{
key: "sprid",
label: "Service Provider Id",
getValue: (org) => (
<span
className="text-truncate d-inline-block"
style={{ maxWidth: "200px" }}
>
{org?.sprid || "N/A"}
</span>
),
align: "text-center",
},
{
key: "isActive",
label: "Status",
getValue: (org) => (
<span
className={`text-truncate d-inline-block badge bg-label-${org.isActive ? "primary" :"secondary"}`}
style={{ maxWidth: "80px" }}
>
{org?.isActive ? "Active" : "Inactive"}
</span>
),
align: "text-start",
},
];
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>{error.message}</div>;
return (
<div>
<div className="dataTables_wrapper no-footer mx-5 pb-2">
<table className="table dataTable text-nowrap">
<thead>
<tr className="table_header_border">
{contactList.map((col) => (
<th key={col.key} className={col.align}>
{col.label}
</th>
))}
</tr>
</thead>
<tbody>
{Array.isArray(data) && data.length > 0 ? (
data.map((row, i) => (
<tr key={i}>
{contactList.map((col) => (
<td key={col.key} className={col.align}>
{col.getValue(row)}
</td>
))}
</tr>
))
) : (
<tr style={{ height: "200px" }}>
<td
colSpan={contactList.length}
className="text-center align-middle"
>
No contacts found
</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
);
};
export default ProjectAssignedOrgs;

View File

@ -1,29 +1,28 @@
import React from "react";
import { useOrganizationModal } from "../../hooks/useOrganization";
import { useSelectedProject } from "../../slices/apiDataManager";
import ProjectAssignedOrgs from "./ProjectOrganization/ProjectAssignedOrgs";
const ProjectOrganizations = () => {
const {onOpen,startStep,flowType} = useOrganizationModal();
const selectedProject = useSelectedProject()
const { onOpen, startStep, flowType } = useOrganizationModal();
const selectedProject = useSelectedProject();
return (
<div className="card">
<div className="card-header">
<div className="d-flex justify-content-end px-2">
<button
type="button"
className="link-button btn btn-xs rounded-md link-button-sm m-1 btn-primary"
onClick={() => onOpen({startStep:1,flowType:"assign"})}
>
<i className="bx bx-plus-circle me-2"></i>
Add Organization
</button>
<div className="card-header">
<div className="d-flex justify-content-end px-2">
<button
type="button"
className="link-button btn btn-xs rounded-md link-button-sm m-1 btn-primary"
onClick={() => onOpen({ startStep: 1, flowType: "assign" })}
>
<i className="bx bx-plus-circle me-2"></i>
Add Organization
</button>
</div>
</div>
</div>
<div className="card-body">
<p className="text-secondary">
Not found Organization connected with current Project
</p>
<div className="row">
<ProjectAssignedOrgs />
</div>
</div>
);

View File

@ -268,7 +268,16 @@ export const useProjectLevelEmployeePermission = (employeeId, projectId) => {
});
};
export const useProjectAssignedOrganizations =(projectId)=>{
return useQuery({
queryKey: ["projectAssignedServices", projectId],
queryFn: async () => {
const resp = await ProjectRepository.getProjectAssignedOrganizations(projectId);
return resp.data;
},
enabled:!!projectId,
});
}
export const useProjectAssignedServices =(projectId)=>{
return useQuery({
queryKey: ["projectAssignedServices", projectId],

View File

@ -49,8 +49,8 @@ const ProjectRepository = {
// Services
getProjectAssignedServices:(projectId)=>api.get(`/api/Project/get/assigned/services/${projectId}`)
getProjectAssignedServices:(projectId)=>api.get(`/api/Project/get/assigned/services/${projectId}`),
getProjectAssignedOrganizations:(projectId)=>api.get(`/api/Project/get/assigned/organization/${projectId}`)
};
export const TasksRepository = {