assign to project and Tenant flow is integrated with api

This commit is contained in:
pramod mahajan 2025-09-20 12:14:34 +05:30
parent 005fdb3490
commit f6d864d42e
12 changed files with 335 additions and 379 deletions

View File

@ -15,7 +15,7 @@ import {
} from "../../hooks/useOrganization"; } from "../../hooks/useOrganization";
const AssignOrg = ({ setStep }) => { const AssignOrg = ({ setStep }) => {
const { isOpen, orgData, startStep, onOpen, onClose, prevStep } = const { isOpen, orgData, startStep, onOpen, flowType, prevStep,onClose } =
useOrganizationModal(); useOrganizationModal();
const selectedProject = useSelectedProject(); const selectedProject = useSelectedProject();
const { data: masterService, isLoading: isMasterserviceLoading } = const { data: masterService, isLoading: isMasterserviceLoading } =
@ -24,9 +24,7 @@ const AssignOrg = ({ setStep }) => {
useProjectAssignedServices(selectedProject); useProjectAssignedServices(selectedProject);
const { data: orgType, isLoading: orgLoading } = useOrganizationType(); const { data: orgType, isLoading: orgLoading } = useOrganizationType();
const { mutate: AssignToProject, isPending } = useAssignOrgToProject( const { mutate: AssignToProject, isPending } = useAssignOrgToProject(()=>onClose());
() => {}
);
const mergedServices = useMemo(() => { const mergedServices = useMemo(() => {
if (!masterService || !projectServices) return []; if (!masterService || !projectServices) return [];
@ -61,6 +59,15 @@ const AssignOrg = ({ setStep }) => {
const handleEdit = () => { 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 })
}
}
if (isMasterserviceLoading || isLoading) if (isMasterserviceLoading || isLoading)
return <div className="text-center">Loading....</div>; return <div className="text-center">Loading....</div>;
return ( return (
@ -186,7 +193,7 @@ const AssignOrg = ({ setStep }) => {
<button <button
type="button" type="button"
className="btn btn-xs btn-outline-secondary" className="btn btn-xs btn-outline-secondary"
onClick={() => onOpen({ startStep: prevStep })} onClick={ handleBack}
disabled={isPending} disabled={isPending}
> >
<i className="bx bx-left-arrow-alt"></i> Back <i className="bx bx-left-arrow-alt"></i> Back

View File

@ -16,7 +16,7 @@ import SelectMultiple from "../common/SelectMultiple";
const ManagOrg = () => { const ManagOrg = () => {
const { data: service, isLoading } = useGlobalServices(); const { data: service, isLoading } = useGlobalServices();
const { isOpen, orgData, startStep, onOpen, onClose, prevStep } = const { flowType, orgData, startStep, onOpen, onClose, prevStep } =
useOrganizationModal(); useOrganizationModal();
const method = useForm({ const method = useForm({
@ -32,28 +32,30 @@ const ManagOrg = () => {
} = method; } = method;
// Create & Update mutations // Create & Update mutations
const { mutate: createOrganization, isPending: isCreating } = useCreateOrganization(() => { const { mutate: createOrganization, isPending: isCreating } =
reset(defaultOrganizationValues); useCreateOrganization(() => {
onOpen({ startStep: 1 }); reset(defaultOrganizationValues);
onClose(); onOpen({ startStep: 1 });
}); onClose();
});
const { mutate: updateOrganization, isPending: isUpdating } = useUpdateOrganization(() => { const { mutate: updateOrganization, isPending: isUpdating } =
reset(defaultOrganizationValues); useUpdateOrganization(() => {
onOpen({ startStep: 1 }); reset(defaultOrganizationValues);
onClose(); onOpen({ startStep: 1 });
}); onClose();
});
// Prefill form if editing // Prefill form if editing
useEffect(() => { useEffect(() => {
if (orgData) { if (orgData) {
console.log(orgData) console.log(orgData);
reset({ reset({
name: orgData.name || "", name: orgData.name || "",
contactPerson: orgData.contactPerson || "", contactPerson: orgData.contactPerson || "",
contactNumber: orgData.contactNumber || "", contactNumber: orgData.contactNumber || "",
email: orgData.email || "", email: orgData.email || "",
serviceIds: orgData.services?.map(s => s.id) || [], serviceIds: orgData.services?.map((s) => s.id) || [],
address: orgData.address || "", address: orgData.address || "",
}); });
} }
@ -66,32 +68,75 @@ const ManagOrg = () => {
createOrganization(payload); createOrganization(payload);
} }
}; };
const handleBack = () => {
if (flowType === "edit") {
onClose();
return;
}
if (flowType === "assign") {
if (prevStep === 1) {
onOpen({ startStep: 1 });
} else {
onOpen({ startStep: prevStep ?? 2 });
}
return;
}
onOpen({ startStep: 2 });
};
return ( return (
<FormProvider {...method}> <FormProvider {...method}>
<form className="form" onSubmit={handleSubmit(onSubmit)}> <form className="form" onSubmit={handleSubmit(onSubmit)}>
<div className="mb-1 text-start"> <div className="mb-1 text-start">
<Label htmlFor="name" required>Organization Name</Label> <Label htmlFor="name" required>
<input className="form-control form-control-sm" {...register("name")} /> Organization Name
{errors.name && <span className="danger-text">{errors.name.message}</span>} </Label>
<input
className="form-control form-control-sm"
{...register("name")}
/>
{errors.name && (
<span className="danger-text">{errors.name.message}</span>
)}
</div> </div>
<div className="mb-1 text-start"> <div className="mb-1 text-start">
<Label htmlFor="contactPerson" required>Contact Person</Label> <Label htmlFor="contactPerson" required>
<input className="form-control form-control-sm" {...register("contactPerson")} /> Contact Person
{errors.contactPerson && <span className="danger-text">{errors.contactPerson.message}</span>} </Label>
<input
className="form-control form-control-sm"
{...register("contactPerson")}
/>
{errors.contactPerson && (
<span className="danger-text">{errors.contactPerson.message}</span>
)}
</div> </div>
<div className="mb-1 text-start"> <div className="mb-1 text-start">
<Label htmlFor="contactNumber" required>Contact Number</Label> <Label htmlFor="contactNumber" required>
<input className="form-control form-control-sm" {...register("contactNumber")} /> Contact Number
{errors.contactNumber && <span className="danger-text">{errors.contactNumber.message}</span>} </Label>
<input
className="form-control form-control-sm"
{...register("contactNumber")}
/>
{errors.contactNumber && (
<span className="danger-text">{errors.contactNumber.message}</span>
)}
</div> </div>
<div className="mb-1 text-start"> <div className="mb-1 text-start">
<Label htmlFor="email" required>Email Address</Label> <Label htmlFor="email" required>
<input className="form-control form-control-sm" {...register("email")} /> Email Address
{errors.email && <span className="danger-text">{errors.email.message}</span>} </Label>
<input
className="form-control form-control-sm"
{...register("email")}
/>
{errors.email && (
<span className="danger-text">{errors.email.message}</span>
)}
</div> </div>
<div className="mb-1 text-start"> <div className="mb-1 text-start">
@ -102,26 +147,38 @@ const ManagOrg = () => {
valueKey="id" valueKey="id"
options={service?.data || []} options={service?.data || []}
/> />
{errors.serviceIds && <span className="danger-text">{errors.serviceIds.message}</span>} {errors.serviceIds && (
<span className="danger-text">{errors.serviceIds.message}</span>
)}
</div> </div>
<div className="mb-1 text-start"> <div className="mb-1 text-start">
<Label htmlFor="address" required>Address</Label> <Label htmlFor="address" required>
Address
</Label>
<textarea <textarea
className="form-control form-control-sm" className="form-control form-control-sm"
{...register("address")} {...register("address")}
rows={2} rows={2}
/> />
{errors.address && <span className="danger-text">{errors.address.message}</span>} {errors.address && (
<span className="danger-text">{errors.address.message}</span>
)}
</div> </div>
<div className="d-flex justify-content-between gap-2 my-2"> <div className="d-flex justify-content-between gap-2 my-2">
<button <button
type="button" type="button"
className="btn btn-sm btn-outline-secondary" className="btn btn-sm btn-outline-secondary"
onClick={() => onOpen({ startStep: prevStep })} onClick={handleBack}
> >
--Back {flowType === "edit" ? (
"Close"
) : (
<>
<i className="bx bx-chevron-left"></i>Back
</>
)}
</button> </button>
<div> <div>
<button <button
@ -129,7 +186,11 @@ const ManagOrg = () => {
className="btn btn-sm btn-primary" className="btn btn-sm btn-primary"
disabled={isCreating || isUpdating || isLoading} disabled={isCreating || isUpdating || isLoading}
> >
{isCreating || isUpdating ? "Please Wait..." : orgData ? "Update" : "Submit"} {isCreating || isUpdating
? "Please Wait..."
: orgData
? "Update"
: "Submit"}
</button> </button>
</div> </div>
</div> </div>
@ -138,5 +199,4 @@ const ManagOrg = () => {
); );
}; };
export default ManagOrg; export default ManagOrg;

View File

@ -1,193 +0,0 @@
const ManageOrganization1 = ({
projectOrganizations = [],
organizationId = null,
}) => {
const [step, setStep] = useState(1); // default = scenario decision
const orgModal = useOrganizationModal();
const { data: services, isLoading } = useServices();
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
}
);
// 🔹 Decide first step when modal opens
useEffect(() => {
if (orgModal.isOpen) {
if (organizationId) {
setStep(3); // update flow show org details directly
} else if (projectOrganizations && projectOrganizations.length > 0) {
setStep(1); // Scenario 1 from current tenant list
} else {
setStep(2); // Scenario 2 search with SPRID
}
}
}, [orgModal.isOpen, organizationId, projectOrganizations]);
const onSubmit = (OrgPayload) => {
CreateOrganization(OrgPayload);
};
const RenderTitle = useMemo(() => {
if (organizationId) return "Update Organization";
if (step === 1) return "Add Organization"; // current tenant
if (step === 2) return "Find Organization"; // search with SPRID
if (step === 3) return "Organization Details";
if (step === 4) return "Create Organization";
return "Manage Organization";
}, [step, organizationId]);
const contentBody = (
<div>
{/* ---------- STEP 1: From Current Tenant Organizations ---------- */}
{step === 1 && (
<div className="d-block">
<div className="list-group mt-3">
{projectOrganizations.map((org, idx) => (
<div
key={idx}
className="list-group-item list-group-item-action cursor-pointer"
onClick={() => setStep(3)}
>
<i className="bx bx-building-house me-2"></i>
{org}
</div>
))}
</div>
<div className="d-flex justify-content-between text-secondary mt-3">
<button
type="button"
className="btn btn-xs btn-outline-secondary"
onClick={() => setStep(2)} // jump to SPRID search
>
<i className="bx bx-search-alt"></i> Find with SPRID
</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: Search by Service Provider ID ---------- */}
{step === 2 && (
<div className="d-block">
<div className="text-start mb-1">
<Label className="text-secondary">Enter Service Provider ID</Label>
<input
type="text"
className="form-control form-control-sm w-auto"
placeholder="SPR - ID"
/>
</div>
{/* Example SPR results */}
<div className="list-group mt-3">
<div
className="list-group-item list-group-item-action cursor-pointer"
onClick={() => setStep(3)}
>
<i className="bx bx-building-house me-2"></i>
Sample Organization (SPRID)
</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 3: Organization Details ---------- */}
{step === 3 && (
<div>
<p className="text-muted small">
Show organization details here (from SPR or tenant list). User
selects services and clicks Add.
</p>
<div className="mb-2">
<Label>Services Offered</Label>
<ul className="list-group">
<li className="list-group-item">
<input type="checkbox" className="form-check-input me-2" />
Service 1
</li>
<li className="list-group-item">
<input type="checkbox" className="form-check-input me-2" />
Service 2
</li>
</ul>
</div>
<div className="d-flex justify-content-between mt-3">
<button
type="button"
className="btn btn-sm btn-outline-secondary"
onClick={() => setStep(1)}
>
Back
</button>
<button type="button" className="btn btn-sm btn-primary">
Add
</button>
</div>
</div>
)}
{/* ---------- STEP 4: Create New Organization ---------- */}
{step === 4 && (
<FormProvider {...method}>
<form className="form" onSubmit={handleSubmit(onSubmit)}>
{/* same form as your code, unchanged */}
{/* ... */}
</form>
</FormProvider>
)}
</div>
);
return (
<Modal
isOpen={orgModal.isOpen}
onClose={orgModal.onClose}
title={RenderTitle}
body={contentBody}
/>
);
};
export default ManageOrganization;

View File

@ -1,102 +1,139 @@
import { useOrganizationModal } from "../../hooks/useOrganization"; import { useState } from "react";
import {
useOrganizationModal,
useOrganizationsList,
} from "../../hooks/useOrganization";
import { ITEMS_PER_PAGE } from "../../utils/constants";
import Label from "../common/Label"; import Label from "../common/Label";
import Pagination from "../common/Pagination";
const OrgPicker = ({ const OrgPicker = ({ title }) => {
title, const [searchText, setSearchText] = useState("");
placeholder, const [currentPage, setCurrentPage] = useState(1);
orgs = [], const { data, isLoading } = useOrganizationsList(
searchValue, ITEMS_PER_PAGE-10,
setSearchValue, 1,
}) => { true,
const { isOpen, orgData, startStep, onOpen, onClose, prevStep } = null,
searchText
);
const paginate = (page) => {
if (page >= 1 && page <= (data?.totalPages ?? 1)) {
setCurrentPage(page);
}
};
const { isOpen, orgData, startStep, onOpen, flowType, prevStep } =
useOrganizationModal(); useOrganizationModal();
const handleBack = () => { const handleBack = () => {
if (startStep === prevStep) { if (prevStep == 1 && flowType == "assign") {
onOpen({ startStep: 2,prevStep:1 }); onOpen({ startStep: prevStep });
} else if (prevStep == 1 && flowType == "assign") {
onOpen({ startStep: 1 });
} else { } else {
onOpen({ startStep: 2 }); onOpen({ startStep: 2 });
} }
}; };
return ( return (
<div className="d-block"> <div className="d-block">
<div className="text-start mb-1"> <div className="text-start mb-1">
<Label className="text-secondary">{title}1</Label> <Label className="text-secondary">{title}</Label>
<input <input
type="text" type="text"
value={searchValue} value={searchText}
onChange={(e) => setSearchValue?.(e.target.value)} onChange={(e) => setSearchText?.(e.target.value)}
className="form-control form-control-sm w-auto" className="form-control form-control-sm w-auto"
placeholder={placeholder} placeholder="Enter Organization Name"
/> />
</div> </div>
{/* ---- Organization list ---- */} {/* ---- Organization list ---- */}
<div className="py-2 text-tiny text-center"> {isLoading ? (
<div className="d-flex flex-column gap-2 border-0 bg-none"> <div>Loading....</div>
{orgs?.map((org) => ( ) : data && data?.data?.length > 0 ? (
<div <div className="py-2 text-tiny text-center">
key={org.id} <div className="d-flex flex-column gap-2 border-0 bg-none">
className="list-group-item list-group-item-action d-flex align-items-center cursor-pointer border-0 hover-overlay border-bottom rounded-none pb-1 shadow-1-strong rounded" {data?.data?.map((org) => (
> <div
<div className="d-flex align-items-center justify-content-center me-3"> key={org.id}
<i className="bx bx-building-house bx-md text-primary"></i> className="list-group-item list-group-item-action d-flex align-items-center cursor-pointer border-0 hover-overlay border-bottom rounded-none pb-1 shadow-1-strong rounded"
</div> >
<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="w-100">
<div className="d-flex justify-content-between cursor-pointer"> <div className="d-flex justify-content-between cursor-pointer">
<div className="user-info text-start"> <div className="user-info text-start">
<h6 className="mb-1 fw-normal">{org.name}</h6> <h6 className="mb-1 fw-normal">{org.name}</h6>
</div> </div>
<div className="add-btn"> <div className="add-btn">
<button <button
className="btn btn-primary btn-xs" className="btn btn-primary btn-sm"
onClick={onOpen.bind(null, { startStep: 3, orgData: org })} onClick={() => onOpen({ startStep: 3, orgData: org })}
> >
select select
</button> </button>
</div>
</div>
</div> </div>
</div> </div>
))} </div>
</div> </div>
))}
{startStep !== 2 && ( <div className="d-flex text-end">
<div className="mt-4"> {data?.data?.length > 0 && (
<p className="text-secondary"> <Pagination
currentPage={currentPage}
totalPages={data.totalPages}
onPageChange={paginate}
/>
)}
</div>
</div>
{startStep !== 2 && (
<div className="mt-4">
<p className="text-secondary">
Don't have required organization, Please find using{" "} Don't have required organization, Please find using{" "}
<span <span
className="text-mutes cursor-pointer text-decoration-underline" className="text-mutes cursor-pointer text-decoration-underline"
onClick={() => { onClick={() => {
onOpen({ startStep: 2, }); onOpen({ startStep: 2 });
}} }}
> >
SPRID SPRID
</span> </span>
</p> </p>
</div>
)}
</div> </div>
)}
</div>
) : (
<div>Not found Organization</div>
)}
{/* ---- Footer buttons ---- */} {/* ---- Footer buttons ---- */}
<div <div
className={`d-flex justify-content-end className={`d-flex justify-content-end
text-secondary mt-3`} text-secondary mt-3`}
> >
{startStep > 1 && prevStep < startStep && ( {flowType == "default" && (
<button <button
type="button" type="button"
className="btn btn-xs btn-outline-secondary" className="btn btn-sm btn-outline-secondary"
onClick={handleBack} onClick={handleBack}
> >
<i className="bx bx-left-arrow-alt"></i> Back <i className="bx bx-left-arrow-alt"></i> Back
</button> </button>
)} )}
<button <button
type="button" type="button"
className="btn btn-xs btn-secondary" className="btn btn-sm btn-secondary"
onClick={() => onOpen({ startStep: 4 })} onClick={() => onOpen({ startStep: 4 })}
> >
<i className="bx bx-plus-circle me-2"></i> <i className="bx bx-plus-circle me-2"></i>

View File

@ -1,5 +1,6 @@
import { useState } from "react"; import { useState } from "react";
import { import {
useAssignOrgToTenant,
useOrganizationBySPRID, useOrganizationBySPRID,
useOrganizationModal, useOrganizationModal,
} from "../../hooks/useOrganization"; } from "../../hooks/useOrganization";
@ -13,7 +14,7 @@ import { OrgCardSkeleton } from "./OrganizationSkeleton";
// Zod schema: only allow exactly 4 digits // Zod schema: only allow exactly 4 digits
const OrgPicker2 = ({ title, placeholder }) => { const OrgPicker2 = ({ title, placeholder }) => {
const { startStep, prevStep, onOpen } = useOrganizationModal(); const {onClose, startStep, flowType, onOpen,prevStep } = useOrganizationModal();
const { const {
register, register,
@ -33,8 +34,20 @@ const OrgPicker2 = ({ title, placeholder }) => {
const onSubmit = (formdata) => { const onSubmit = (formdata) => {
setSPRID(formdata.spridSearchText); setSPRID(formdata.spridSearchText);
}; };
console.log(data)
const SP = watch("spridSearchText") const {mutate:AssignToTenant,isPending} = useAssignOrgToTenant(()=>{
onClose()
})
const handleOrg=(orgId)=>{
if(flowType == "default"){
AssignToTenant(orgId)
}else{
debugger
onOpen({ startStep: 3, orgData: org })
}
}
const SP = watch("spridSearchText");
return ( return (
<div className="d-block"> <div className="d-block">
<form <form
@ -45,7 +58,6 @@ const SP = watch("spridSearchText")
<Label className="text-secondary">{title}</Label> <Label className="text-secondary">{title}</Label>
<input <input
type="search" type="search"
{...register("spridSearchText")} {...register("spridSearchText")}
className="form-control form-control-sm w-auto" className="form-control form-control-sm w-auto"
placeholder={placeholder || "Enter Service Provider ID"} placeholder={placeholder || "Enter Service Provider ID"}
@ -69,75 +81,77 @@ const SP = watch("spridSearchText")
</div> </div>
{/* ---- Organization list ---- */} {/* ---- Organization list ---- */}
{isLoading ? ( {isLoading ? (
<OrgCardSkeleton /> <OrgCardSkeleton />
) : data && data.length > 0 ? ( ) : data && data?.data.length > 0 ? (
<div className="py-2 text-tiny text-center"> <div className="py-2 text-tiny text-center">
<div className="d-flex flex-column gap-2 border-0 bg-none"> <div className="d-flex flex-column gap-2 border-0 bg-none">
{data.map((org) => ( {data.data.map((org) => (
<div <div
key={org.id} key={org.id}
className="list-group-item list-group-item-action d-flex align-items-center cursor-pointer border-0 hover-overlay shadow-1-strong rounded" className="list-group-item list-group-item-action d-flex align-items-center cursor-pointer border-0 hover-overlay shadow-1-strong rounded"
> >
<div className="d-flex align-items-center justify-content-center me-3"> <div className="d-flex align-items-center justify-content-center me-3">
<i className="bx bx-building-house bx-md text-primary"></i> <i className="bx bx-building-house bx-md text-primary"></i>
</div> </div>
<div className="w-100"> <div className="w-100">
<div className="d-flex justify-content-between cursor-pointer"> <div className="d-flex justify-content-between cursor-pointer">
<div className="user-info text-start"> <div className="user-info text-start">
<h6 className="mb-1 fw-normal">{org.name}</h6> <h6 className="mb-1 fw-normal">{org.name}</h6>
</div>
<div className="add-btn">
<button
type="button"
className="btn btn-primary btn-sm"
onClick={()=>handleOrg(org.id)}
>
{isPending ? "Please Wait..." : flowType === "assign" ? "Select":`Add`}
</button>
</div>
</div>
</div>
</div> </div>
<div className="add-btn"> ))}
<button
type="button"
className="btn btn-primary btn-sm"
onClick={() => onOpen({ startStep: 3, orgData: org })}
>
select
</button>
</div>
</div>
</div> </div>
</div> </div>
))} ) : SPRID ? (
</div> <div className="py-3 text-center text-secondary">
</div> No organization found for "{SPRID}"
) : SPRID ? ( </div>
<div className="py-3 text-center text-secondary"> ) : (
No organization found for "{SPRID}" <div className="py-12 text-center text-tiny text-black">
</div> Type a Service Provider ID to find an organization.
) : ( </div>
<div className="py-12 text-center text-tiny text-black"> )}
Type a Service Provider ID to find an organization.
</div>
)}
{/* ---- Footer buttons ---- */} {/* ---- Footer buttons ---- */}
{/* ---- Footer buttons ---- */} <div
<div className="d-flex justify-content-between text-secondary mt-3"> className={`d-flex ${
{startStep > 1 && prevStep !== startStep && ( flowType !== "default"
<button ? "justify-content-between"
type="button" : "justify-content-end"
className="btn btn-xs btn-outline-secondary" } text-secondary mt-3`}
onClick={() => onOpen({ startStep: prevStep })} >
> {flowType !== "default" && (
<i className="bx bx-left-arrow-alt"></i> Back <button
</button> type="button"
)} className="btn btn-xs btn-outline-secondary"
onClick={() => onOpen({ startStep: prevStep })}
<button >
type="button" <i className='bx bx-chevron-left'></i> Back
className="btn btn-xs btn-secondary" </button>
onClick={() => onOpen({ startStep: 4 })} )}
>
<i className="bx bx-plus-circle me-2"></i>
Add New Organization
</button>
</div>
<button
type="button"
className="btn btn-sm btn-secondary"
onClick={() => onOpen({ startStep: 4 })}
>
<i className="bx bx-plus-circle me-2"></i>
Add New Organization
</button>
</div>
</div> </div>
); );
}; };

View File

@ -26,12 +26,7 @@ const OrganizationModal = () => {
const { data: masterService, isLoading } = useServices(); const { data: masterService, isLoading } = useServices();
const [searchText, setSearchText] = useState(); const [searchText, setSearchText] = useState();
const [SPRID, setSPRID] = useState(""); const [SPRID, setSPRID] = useState("");
const { data: orgList, isLoading: orgLoading } = useOrganizationsList(
20,
1,
true,
searchText
);
const [Organization, setOrganization] = useState({}); const [Organization, setOrganization] = useState({});
@ -86,11 +81,7 @@ const OrganizationModal = () => {
{startStep === 1 && ( {startStep === 1 && (
<OrgPicker <OrgPicker
title="Find Organization" title="Find Organization"
placeholder="Enter Organization"
orgs={orgList}
projectOrganizations={orgData}
searchValue={SPRID}
setSearchValue={setSPRID}
/> />
)} )}

View File

@ -1,10 +1,12 @@
import React from "react"; import React, { useState } from "react";
import { useOrganizationsList } from "../../hooks/useOrganization"; import { useOrganizationModal, useOrganizationsList } from "../../hooks/useOrganization";
import { ITEMS_PER_PAGE } from "../../utils/constants"; import { ITEMS_PER_PAGE } from "../../utils/constants";
import Avatar from "../common/Avatar"; import Avatar from "../common/Avatar";
import { useDebounce } from "../../utils/appUtils"; import { useDebounce } from "../../utils/appUtils";
import Pagination from "../common/Pagination";
const OrganizationsList = ({searchText}) => { const OrganizationsList = ({searchText}) => {
const [currentPage, setCurrentPage] = useState(1);
const searchString = useDebounce(searchText,500) const searchString = useDebounce(searchText,500)
const { const {
data = [], data = [],
@ -13,6 +15,7 @@ const OrganizationsList = ({searchText}) => {
isError, isError,
error, error,
} = useOrganizationsList(ITEMS_PER_PAGE, 1, true,null,searchString); } = useOrganizationsList(ITEMS_PER_PAGE, 1, true,null,searchString);
const {onClose, startStep, flowType, onOpen,orgData } = useOrganizationModal();
const organizationsColumns = [ const organizationsColumns = [
{ {
@ -81,6 +84,11 @@ const OrganizationsList = ({searchText}) => {
}, },
]; ];
const paginate = (page) => {
if (page >= 1 && page <= (data?.totalPages ?? 1)) {
setCurrentPage(page);
}
};
if (isFetching && !isFetching) return <div>Loading...</div>; if (isFetching && !isFetching) return <div>Loading...</div>;
if (isError) return <div>{error?.message || "Something went wrong"}</div>; if (isError) return <div>{error?.message || "Something went wrong"}</div>;
@ -106,8 +114,8 @@ const OrganizationsList = ({searchText}) => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{data.length > 0 ? ( {data?.data?.length > 0 ? (
data.map((org) => ( data?.data?.map((org) => (
<tr key={org.id}> <tr key={org.id}>
{organizationsColumns.map((col) => ( {organizationsColumns.map((col) => (
<td <td
@ -120,9 +128,9 @@ const OrganizationsList = ({searchText}) => {
</td> </td>
))} ))}
<td className="sticky-action-column "> <td className="sticky-action-column ">
<div className="d-flex justify-content-center gap-2 "> <div className="d-flex justify-content-center gap-2">
<i className="bx bx-show text-primary cursor-pointer"></i> <i className="bx bx-show text-primary cursor-pointer" ></i>
<i className="bx bx-edit text-secondary cursor-pointer"></i> <i className="bx bx-edit text-secondary cursor-pointer" onClick={()=>onOpen({startStep:4,orgData:org,flowType:"edit"})}></i>
<i className="bx bx-trash text-danger cursor-pointer"></i> <i className="bx bx-trash text-danger cursor-pointer"></i>
</div> </div>
</td> </td>
@ -140,6 +148,13 @@ const OrganizationsList = ({searchText}) => {
)} )}
</tbody> </tbody>
</table> </table>
{data?.data?.length > 0 && (
<Pagination
currentPage={currentPage}
totalPages={data.totalPages}
onPageChange={paginate}
/>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -3,7 +3,7 @@ import { useOrganizationModal } from "../../hooks/useOrganization";
import { useSelectedProject } from "../../slices/apiDataManager"; import { useSelectedProject } from "../../slices/apiDataManager";
const ProjectOrganizations = () => { const ProjectOrganizations = () => {
const {onOpen,startStep} = useOrganizationModal(); const {onOpen,startStep,flowType} = useOrganizationModal();
const selectedProject = useSelectedProject() const selectedProject = useSelectedProject()
return ( return (
<div className="card"> <div className="card">
@ -12,7 +12,7 @@ const ProjectOrganizations = () => {
<button <button
type="button" type="button"
className="link-button btn btn-xs rounded-md link-button-sm m-1 btn-primary" className="link-button btn btn-xs rounded-md link-button-sm m-1 btn-primary"
onClick={() => onOpen({startStep:1})} onClick={() => onOpen({startStep:1,flowType:"assign"})}
> >
<i className="bx bx-plus-circle me-2"></i> <i className="bx bx-plus-circle me-2"></i>
Add Organization Add Organization

View File

@ -10,7 +10,7 @@ import showToast from "../services/toastService";
export const useOrganizationModal = () => { export const useOrganizationModal = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { isOpen, orgData, startStep, prevStep } = useSelector( const { isOpen, orgData, startStep, prevStep, flowType } = useSelector(
(state) => state.localVariables.OrganizationModal (state) => state.localVariables.OrganizationModal
); );
@ -19,14 +19,15 @@ export const useOrganizationModal = () => {
orgData, orgData,
startStep, startStep,
prevStep, prevStep,
flowType,
onOpen: (options = {}) => onOpen: (options = {}) =>
dispatch( dispatch(
openOrgModal({ openOrgModal({
isOpen: true, isOpen: true,
orgData: options.orgData ?? orgData ?? null, orgData: options.orgData ?? orgData ?? null,
startStep: options.startStep ?? startStep ?? 1, startStep: options.startStep ?? startStep ?? 1,
// only override prevStep if explicitly passed
prevStep: options.prevStep ?? prevStep ?? 1, prevStep: options.prevStep ?? prevStep ?? 1,
flowType: options.flowType ?? flowType ?? "default",
}) })
), ),
onClose: () => dispatch(closeOrgModal()), onClose: () => dispatch(closeOrgModal()),
@ -36,6 +37,7 @@ export const useOrganizationModal = () => {
export const useOrganizationBySPRID =(sprid)=>{ export const useOrganizationBySPRID =(sprid)=>{
return useQuery({ return useQuery({
queryKey:["organization by",sprid], queryKey:["organization by",sprid],
@ -100,7 +102,7 @@ export const useCreateOrganization = (onSuccessCallback) => {
}); });
}; };
export const useAssignOrgToProject=()=>{ export const useAssignOrgToProject=(onSuccessCallback)=>{
const useClient = useQueryClient(); const useClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async (payload) => mutationFn: async (payload) =>
@ -120,7 +122,26 @@ export const useAssignOrgToProject=()=>{
}, },
}); });
} }
export const useAssignOrgToTenant =(onSuccessCallback)=>{
const useClient = useQueryClient();
return useMutation({
mutationFn: async (payload) =>
await OrganizationRepository.assignOrganizationToTenanat(payload),
onSuccess: (_, variables) => {
useClient.invalidateQueries({ queryKey: ["organizationList"] });
showToast("Organization added successfully", "success");
if (onSuccessCallback) onSuccessCallback();
},
onError: (error) => {
showToast(
error.response.data.message ||
error.message ||
"Something went wrong please try again !",
"error"
);
},
});
}
export const useUpdateOrganization=()=>{ export const useUpdateOrganization=()=>{
const useClient = useQueryClient(); const useClient = useQueryClient();
return useMutation({ return useMutation({

View File

@ -4,7 +4,7 @@ import { useOrganizationModal } from "../../hooks/useOrganization";
import OrganizationsList from "../../components/Organization/OrganizationsList"; import OrganizationsList from "../../components/Organization/OrganizationsList";
const OrganizationPage = () => { const OrganizationPage = () => {
const { isOpen, orgData, startStep, onOpen, } = const { isOpen, orgData, startStep, onOpen, flowType } =
useOrganizationModal(); useOrganizationModal();
const [searchText,setSearchText] = useState("") const [searchText,setSearchText] = useState("")
@ -34,7 +34,7 @@ const OrganizationPage = () => {
type="button" type="button"
className="p-1 me-1 m-sm-0 bg-primary rounded-circle" className="p-1 me-1 m-sm-0 bg-primary rounded-circle"
title="Add New Organization" title="Add New Organization"
onClick={()=>onOpen({ startStep: 1, })} onClick={()=>onOpen({ startStep: 2,flowType:"default" })}
> >
<i className="bx bx-plus fs-4 text-white"></i> <i className="bx bx-plus fs-4 text-white"></i>
</button> </button>

View File

@ -12,7 +12,9 @@ const OrganizationRepository = {
getOrganizationBySPRID :(sprid)=>api.get(`/api/Organization/list?sprid=${sprid}`), getOrganizationBySPRID :(sprid)=>api.get(`/api/Organization/list?sprid=${sprid}`),
assignOrganizationToProject:(data)=>api.post(`/api/Organization/assign/project`,data) assignOrganizationToProject:(data)=>api.post(`/api/Organization/assign/project`,data),
assignOrganizationToTenanat:(organizationId)=>api.post(`/api/Organization/assign/tenant/${organizationId}`)
}; };
export default OrganizationRepository; export default OrganizationRepository;

View File

@ -17,6 +17,7 @@ const localVariablesSlice = createSlice({
orgData: null, orgData: null,
prevStep:null, prevStep:null,
startStep: 1, startStep: 1,
flowType: "default",
} }
}, },
@ -49,6 +50,7 @@ state.OrganizationModal.isOpen = true;
} }
state.OrganizationModal.startStep = action.payload?.startStep || 1; state.OrganizationModal.startStep = action.payload?.startStep || 1;
state.OrganizationModal.flowType = action.payload?.flowType || "default";
}, },
closeOrgModal: (state) => { closeOrgModal: (state) => {
state.OrganizationModal.isOpen = false; state.OrganizationModal.isOpen = false;