initially setup service provider form
This commit is contained in:
parent
7fa2ca9227
commit
1452e77bc5
@ -1,5 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import React from "react";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { FormProvider, useForm } from "react-hook-form";
|
||||
import {
|
||||
defaultOrganizationValues,
|
||||
@ -14,7 +14,11 @@ import Label from "../common/Label";
|
||||
import SelectMultiple from "../common/SelectMultiple";
|
||||
import { useServices } from "../../hooks/masterHook/useMaster";
|
||||
|
||||
const ManageOrganization = () => {
|
||||
const ManageOrganization = ({
|
||||
projectOrganizations = ["ee"],
|
||||
organizationId = null,
|
||||
}) => {
|
||||
const [step, setStep] = useState(1); // 1 = Service Provider, 2 = Org Details, 3 = Add New Org
|
||||
const orgModal = useOrganizationModal();
|
||||
const { data: services, isLoading } = useServices();
|
||||
|
||||
@ -34,123 +38,319 @@ const ManageOrganization = () => {
|
||||
() => {
|
||||
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.length > 0
|
||||
? "Add Organization"
|
||||
: "Find Organization";
|
||||
}
|
||||
|
||||
if (step === 2) {
|
||||
return "Organization Details";
|
||||
}
|
||||
|
||||
if (step === 3) {
|
||||
return "Create Organization";
|
||||
}
|
||||
|
||||
return "Manage Organization"; // fallback
|
||||
}, [step, orgModal?.orgData, organizationId]);
|
||||
|
||||
const contentBody = (
|
||||
<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>
|
||||
{/* ---------- STEP 1: Service Provider- Form Own Tenant list ---------- */}
|
||||
{step === 1 && (
|
||||
<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"
|
||||
aria-describedby="search-label"
|
||||
/>
|
||||
</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="py-2 text-start">
|
||||
{orgModal.orgData && (<p className="text-secondary">Don't have Service provder id, Select Service Provider <span className="text-primary text-decoration-underline cursor-pointer" onClick={()=>orgModal.orgData ? setStep(2):setStep(3)}>Choose Provider</span></p>
|
||||
)}
|
||||
</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>
|
||||
{/* ======== org list ======*/}
|
||||
{/* <div className="list-group mt-3">
|
||||
<div
|
||||
className="list-group-item list-group-item-action cursor-pointer"
|
||||
onClick={() => setStep(2)}
|
||||
>
|
||||
<i className="bx bx-building-house me-2"></i>
|
||||
Sample Organization 1
|
||||
</div>
|
||||
</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 className="d-flex justify-content-center text-secondary mt-3">
|
||||
<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>
|
||||
)}
|
||||
|
||||
<div className="mb-1 text-start">
|
||||
<SelectMultiple
|
||||
name="serviceIds"
|
||||
label="Services"
|
||||
required={true}
|
||||
valueKey="id"
|
||||
options={services?.data || []}
|
||||
/>
|
||||
{errors.serviceIds && (
|
||||
<span className="danger-text">{errors.serviceIds.message}</span>
|
||||
)}
|
||||
</div>
|
||||
{/* ---------- STEP 1: Service Provider From Own Other Tenant ---------- */}
|
||||
{step === 2 && (
|
||||
<div className="d-block">
|
||||
{/* Optional: dropdown if projectOrganizations exist */}
|
||||
{/* Optional: dropdown if projectOrganizations exist */}
|
||||
<p className="text-secondary">Select Tags</p>
|
||||
{/* ======== org list ======*/}
|
||||
<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 1
|
||||
</div>
|
||||
</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 className="d-flex justify-content-center text-secondary mt-3">
|
||||
<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>
|
||||
)}
|
||||
|
||||
<div className="d-flex justify-content-end gap-2 my-2">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-secondary"
|
||||
onClick={orgModal.onClose}
|
||||
disabled={isPending || isLoading}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-sm btn-primary"
|
||||
disabled={isPending || isLoading}
|
||||
>
|
||||
{isPending ? "Please Wait..." : "Submit"}
|
||||
</button>
|
||||
{/* ---------- STEP 2: Existing Organization Details ---------- */}
|
||||
{step === 3 && (
|
||||
<div>
|
||||
<p className="text-muted small">
|
||||
Show organization details here (from SPR 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>
|
||||
</form>
|
||||
</FormProvider>
|
||||
)}
|
||||
|
||||
{/* ---------- 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={services?.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="button"
|
||||
className="btn btn-sm btn-secondary me-2"
|
||||
onClick={orgModal.onClose}
|
||||
disabled={isPending || isLoading}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<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="Manage Organization"
|
||||
title={RenderTitle}
|
||||
body={contentBody}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManageOrganization;
|
||||
|
||||
// <div className="d-flex flex-column gap-2 border-0 bg-none">
|
||||
// <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-sm">Add</button>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// {/* Icon item */}
|
||||
// <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-sm">Add</button>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
|
@ -43,6 +43,7 @@ const ProjectNav = ({ onPillClick, activePill }) => {
|
||||
},
|
||||
{ key: "documents", icon: "bx bx-folder-open", label: "Documents",hidden:!(isViewDocuments || isModifyDocument || isUploadDocument) },
|
||||
{ key: "setting", icon: "bx bxs-cog", label: "Setting",hidden:!isManageTeam },
|
||||
{ key: "organization", icon: "bx bx-buildings", label: "Organization"},
|
||||
];
|
||||
return (
|
||||
<div className="nav-align-top">
|
||||
|
30
src/components/Project/ProjectOrganizations.jsx
Normal file
30
src/components/Project/ProjectOrganizations.jsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React from "react";
|
||||
import { useOrganizationModal } from "../../hooks/useOrganization";
|
||||
import { useSelectedProject } from "../../slices/apiDataManager";
|
||||
|
||||
const ProjectOrganizations = () => {
|
||||
const orgModal = useOrganizationModal();
|
||||
const selectedProject = useSelectedProject()
|
||||
return (
|
||||
<div className="card">
|
||||
<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={() => orgModal.onOpen(selectedProject)}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
Add Organization
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="card-body">
|
||||
<p className="text-secondary">
|
||||
Not found Organization connected with current Project
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProjectOrganizations;
|
@ -10,23 +10,44 @@ import showToast from "../services/toastService";
|
||||
|
||||
export const useOrganizationModal = () => {
|
||||
const dispatch = useDispatch();
|
||||
const isOpen = useSelector(
|
||||
(state) => state.localVariables.OrganizationModal.isOpen
|
||||
const { isOpen, orgData } = useSelector(
|
||||
(state) => state.localVariables.OrganizationModal
|
||||
);
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
onOpen: () => dispatch(openOrgModal()),
|
||||
orgData,
|
||||
onOpen: (dat) =>
|
||||
dispatch(openOrgModal({ isOpen: true, orgData: dat || null })),
|
||||
onClose: () => dispatch(closeOrgModal()),
|
||||
Togggle: () => dispatch(toggleOrgModal(isOpen)),
|
||||
onToggle: () => dispatch(toggleOrgModal()),
|
||||
};
|
||||
};
|
||||
|
||||
export const useOrganizationsList = (pageSize, pageNumber, active, sprid, searchString="") => {
|
||||
export const useOrganizationsList = (
|
||||
pageSize,
|
||||
pageNumber,
|
||||
active,
|
||||
sprid,
|
||||
searchString = ""
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: ["organizationList", pageSize, pageNumber, active, sprid, searchString],
|
||||
queryFn: async() => {
|
||||
const resp = await OrganizationRepository.getOrganizationList(pageSize, pageNumber, active, sprid, searchString);
|
||||
queryKey: [
|
||||
"organizationList",
|
||||
pageSize,
|
||||
pageNumber,
|
||||
active,
|
||||
sprid,
|
||||
searchString,
|
||||
],
|
||||
queryFn: async () => {
|
||||
const resp = await OrganizationRepository.getOrganizationList(
|
||||
pageSize,
|
||||
pageNumber,
|
||||
active,
|
||||
sprid,
|
||||
searchString
|
||||
);
|
||||
return resp.data;
|
||||
},
|
||||
keepPreviousData: true,
|
||||
@ -34,12 +55,12 @@ export const useOrganizationsList = (pageSize, pageNumber, active, sprid, search
|
||||
};
|
||||
|
||||
export const useCreateOrganization = (onSuccessCallback) => {
|
||||
const useClient = useQueryClient()
|
||||
const useClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (OrgPayload) =>
|
||||
await OrganizationRepository.createOrganization(OrgPayload),
|
||||
onSuccess: (_, variables) => {
|
||||
useClient.invalidateQueries({queryKey:["organizationList"]})
|
||||
useClient.invalidateQueries({ queryKey: ["organizationList"] });
|
||||
showToast("Organization created successfully", "success");
|
||||
if (onSuccessCallback) onSuccessCallback();
|
||||
},
|
||||
|
@ -69,7 +69,7 @@ const ContactsPage = ({ projectId, searchText, onExport }) => {
|
||||
};
|
||||
|
||||
if (isError) return <div>{error.message}</div>;
|
||||
if (isLoading) return gridView ? <CardViewContactSkeleton /> : <ListViewContactSkeleton />;
|
||||
// if (isLoading) return gridView ? <CardViewContactSkeleton /> : <ListViewContactSkeleton />;
|
||||
|
||||
return (
|
||||
<div className="row mt-5">
|
||||
@ -94,7 +94,7 @@ const ContactsPage = ({ projectId, searchText, onExport }) => {
|
||||
) : (
|
||||
<div className="col-12">
|
||||
<ListViewContact
|
||||
data={data.data}
|
||||
data={data?.data}
|
||||
Pagination={
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
|
@ -23,6 +23,7 @@ import DirectoryPage from "../Directory/DirectoryPage";
|
||||
import { useProjectAccess } from "../../hooks/useProjectAccess"; // ✅ new
|
||||
|
||||
import "./ProjectDetails.css";
|
||||
import ProjectOrganizations from "../../components/Project/ProjectOrganizations";
|
||||
|
||||
const ProjectDetails = () => {
|
||||
const projectId = useSelectedProject();
|
||||
@ -96,6 +97,8 @@ const ProjectDetails = () => {
|
||||
return <ProjectDocuments />;
|
||||
case "setting":
|
||||
return <ProjectSetting />;
|
||||
case "organization":
|
||||
return <ProjectOrganizations />;
|
||||
default:
|
||||
return <ComingSoonPage />;
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ const OrganizationRepository = {
|
||||
}searchString=${searchString}`
|
||||
);
|
||||
},
|
||||
|
||||
assignOrganizationToProject:(data)=>api.post(`/api/Organization/assign/project`,data)
|
||||
};
|
||||
|
||||
export default OrganizationRepository;
|
||||
|
@ -13,7 +13,8 @@ const localVariablesSlice = createSlice({
|
||||
reload:false,
|
||||
|
||||
OrganizationModal:{
|
||||
isOpen:false
|
||||
isOpen:false,
|
||||
orgData:null,
|
||||
}
|
||||
|
||||
},
|
||||
@ -37,15 +38,32 @@ const localVariablesSlice = createSlice({
|
||||
state.defaultDateRange = action.payload;
|
||||
},
|
||||
|
||||
openOrgModal: (state) => {
|
||||
state.OrganizationModal.isOpen = true;
|
||||
},
|
||||
openOrgModal: (state, action) => {
|
||||
debugger;
|
||||
|
||||
if (typeof action.payload === "boolean") {
|
||||
state.OrganizationModal.isOpen = action.payload;
|
||||
if (!action.payload) {
|
||||
state.OrganizationModal.orgData = null;
|
||||
}
|
||||
} else if (typeof action.payload === "object") {
|
||||
const { isOpen, orgData } = action.payload;
|
||||
state.OrganizationModal.isOpen =
|
||||
typeof isOpen === "boolean" ? isOpen : state.OrganizationModal.isOpen;
|
||||
state.OrganizationModal.orgData =
|
||||
orgData !== undefined ? orgData : state.OrganizationModal.orgData;
|
||||
}
|
||||
}
|
||||
,
|
||||
closeOrgModal: (state) => {
|
||||
state.OrganizationModal.isOpen = false;
|
||||
},
|
||||
toggleOrgModal: (state) => {
|
||||
state.OrganizationModal.isOpen = !state.OrganizationModal.isOpen;
|
||||
},
|
||||
addedOrgModal:(state,action)=>{
|
||||
state.OrganizationModal.orgData = action.payload;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user