Merge pull request 'pramod_Task-#357 : Added Admin Update and Delete Functionality in Manage Buckets Modal' (#155) from pramod_Task-#357 into Feature_Directory
Reviewed-on: #155
This commit is contained in:
commit
16301def0c
3
public/assets/vendor/css/core.css
vendored
3
public/assets/vendor/css/core.css
vendored
@ -5069,6 +5069,9 @@ fieldset:disabled .btn {
|
||||
.card-group > .card {
|
||||
margin-bottom: var(--bs-card-group-margin);
|
||||
}
|
||||
.card-minHeight{
|
||||
min-height: 430px;
|
||||
}
|
||||
@media (min-width: 576px) {
|
||||
.card-group {
|
||||
display: flex;
|
||||
|
15
src/Context/FabContext.jsx
Normal file
15
src/Context/FabContext.jsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React, { createContext, useContext, useState } from "react";
|
||||
|
||||
const FabContext = createContext();
|
||||
|
||||
export const FabProvider = ({ children }) => {
|
||||
const [actions, setActions] = useState([]);
|
||||
|
||||
return (
|
||||
<FabContext.Provider value={{ actions, setActions }}>
|
||||
{children}
|
||||
</FabContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useFab = () => useContext(FabContext);
|
@ -30,7 +30,7 @@ const CardViewDirectory = ({
|
||||
(contact?.name || "").trim().split(" ")[1]?.charAt(0) || ""
|
||||
}
|
||||
/>{" "}
|
||||
<p className="m-0">{contact.name}</p>
|
||||
<span className="text-heading fs-6"> {contact.name}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className={`dropdown z-2 ${!IsActive && "d-none"}`}>
|
||||
@ -55,20 +55,19 @@ const CardViewDirectory = ({
|
||||
setSelectedContact(contact);
|
||||
setIsOpenModal(true);
|
||||
}}
|
||||
|
||||
>
|
||||
<a className="dropdown-item px-2 py-0">
|
||||
<i className="bx bx-pencil bx-xs me-2"></i>
|
||||
<span className="align-left small-text">Modify</span>
|
||||
<a className="dropdown-item px-2 cursor-pointer py-1">
|
||||
<i className="bx bx-edit bx-xs text-primary me-2"></i>
|
||||
<span className="align-left ">Modify</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="dropdown-item px-2 py-0"
|
||||
className="dropdown-item px-2 cursor-pointer py-1"
|
||||
onClick={() => IsDeleted(contact.id)}
|
||||
>
|
||||
<i className="bx bx-trash bx-xs me-2"></i>
|
||||
<span className="align-left small-text">Delete</span>
|
||||
<i className="bx bx-trash text-danger bx-xs me-2"></i>
|
||||
<span className="align-left">Delete</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@ -80,7 +79,7 @@ const CardViewDirectory = ({
|
||||
<li className="list-inline-item me-1" style={{ fontSize: "10px" }}>
|
||||
<i className="bx bx-building bx-xs"></i>
|
||||
</li>
|
||||
<li className="list-inline-item" style={{ fontSize: "10px" }}>
|
||||
<li className="list-inline-item text-small">
|
||||
{contact.organization}
|
||||
</li>
|
||||
</ul>
|
||||
@ -96,52 +95,54 @@ const CardViewDirectory = ({
|
||||
>
|
||||
<hr className="my-0" />
|
||||
{contact.contactEmails[0] && (
|
||||
<ul className="list-inline my-1 ">
|
||||
<ul className="list-inline my-1 ">
|
||||
<li className="list-inline-item me-2">
|
||||
<i className="bx bx-envelope bx-xs"></i>
|
||||
<i className="bx bx-envelope bx-xs"></i>
|
||||
</li>
|
||||
<li className="list-inline-item small-text">
|
||||
<li className="list-inline-item text-small">
|
||||
{contact.contactEmails[0]?.emailAddress}
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{contact.contactPhones[0] && (
|
||||
<ul className="list-inline m-0">
|
||||
<li className="list-inline-item me-2">
|
||||
<ul className="list-inline m-0 ">
|
||||
<li className="list-inline-item me-1">
|
||||
<i
|
||||
className={` ${getPhoneIcon(
|
||||
contact.contactPhones[0].label
|
||||
)} bx-xs`}
|
||||
></i>
|
||||
</li>
|
||||
<li className="list-inline-item small-text">
|
||||
<li className="list-inline-item text-small">
|
||||
{contact.contactPhones[0]?.phoneNumber}
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
|
||||
<ul className="list-inline m-0">
|
||||
<li className="list-inline-item me-2">
|
||||
<i className="bx bx-merge bx-xs"></i>
|
||||
<li className="list-inline-item me-2 my-1">
|
||||
<i className="fa-solid fa-tag fs-6"></i>
|
||||
|
||||
</li>
|
||||
<li className="list-inline-item small-text">
|
||||
<li className="list-inline-item text-small active">
|
||||
{contact.contactCategory.name}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul className="list-inline m-0">
|
||||
{contact.bucketIds.map((bucketId) => (
|
||||
<React.Fragment key={bucketId}>
|
||||
<li className="list-inline-item me-1">
|
||||
<i className="bx bx-pin bx-xs"></i>
|
||||
</li>
|
||||
<li className="list-inline-item small-text">
|
||||
{getBucketNameById(buckets, bucketId)}
|
||||
</li>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</ul>
|
||||
<ul className="list-inline m-0">
|
||||
{contact.bucketIds?.map((bucketId) => (
|
||||
<li key={bucketId} className="list-inline-item me-1">
|
||||
<span className="badge bg-label-primary rounded-pill d-flex align-items-center gap-1" style={{padding:'0.1rem 0.3rem'}}>
|
||||
<i className="bx bx-pin bx-xs"></i>
|
||||
<span className="small-text">
|
||||
{getBucketNameById(buckets, bucketId)}
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -6,7 +6,7 @@ export const ContactSchema = z
|
||||
contactCategoryId: z.string().nullable().optional(),
|
||||
address: z.string().optional(),
|
||||
description: z.string().min(1, { message: "Description is required" }),
|
||||
projectIds: z.array(z.string()).min(1, "Project is required"),
|
||||
projectIds: z.array(z.string()), // min(1, "Project is required")
|
||||
contactEmails: z
|
||||
.array(
|
||||
z.object({
|
||||
@ -39,7 +39,7 @@ export const ContactSchema = z
|
||||
})
|
||||
)
|
||||
.min(1, { message: "At least one tag is required" }),
|
||||
bucketIds: z.array(z.string()).optional(),
|
||||
bucketIds: z.array(z.string()).min(1,{message:"At least one Label required"}),
|
||||
})
|
||||
|
||||
// .refine((data) => {
|
||||
|
147
src/components/Directory/EmployeeList.jsx
Normal file
147
src/components/Directory/EmployeeList.jsx
Normal file
@ -0,0 +1,147 @@
|
||||
import React, { useState } from "react";
|
||||
import { useSortableData } from "../../hooks/useSortableData";
|
||||
|
||||
const EmployeeList = ({ employees }) => {
|
||||
const [selectedIds, setSelectedIds] = useState([]);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
|
||||
const handleCheckboxChange = (id) => {
|
||||
setSelectedIds((prev) =>
|
||||
prev.includes(id) ? prev?.filter((empId) => empId !== id) : [...prev, id]
|
||||
);
|
||||
};
|
||||
|
||||
const getSelectedEmployees = () => {
|
||||
console.log("Selected Employee IDs:", selectedIds);
|
||||
};
|
||||
|
||||
const {
|
||||
items: sortedEmployees,
|
||||
requestSort,
|
||||
sortConfig,
|
||||
} = useSortableData(employees, {
|
||||
key: (e) => `${e?.firstName} ${e?.lastName}`,
|
||||
direction: "asc",
|
||||
});
|
||||
|
||||
const getSortIcon = () => {
|
||||
if (!sortConfig) return null;
|
||||
return sortConfig.direction === "asc" ? (
|
||||
<i className="bx bx-caret-up text-secondary"></i>
|
||||
) : (
|
||||
<i className="bx bx-caret-down text-secondary"></i>
|
||||
);
|
||||
};
|
||||
|
||||
const filteredEmployees = sortedEmployees?.filter((employee) => {
|
||||
const fullName =
|
||||
`${employee?.firstName} ${employee?.lastName}`?.toLowerCase();
|
||||
// const email = employee.email.toLowerCase();
|
||||
// const role = employee.jobRole.toLowerCase();
|
||||
const term = searchTerm?.toLowerCase();
|
||||
|
||||
return fullName.includes(term);
|
||||
// email.includes(term) ||
|
||||
// role.includes(term)
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="d-flex justify-content-between mt-2">
|
||||
<p className="m-0 fs-6 fw-normal">Add Employee</p>
|
||||
<div className="px-1">
|
||||
<input
|
||||
type="search"
|
||||
className="form-control form-control-sm"
|
||||
placeholder="Search Employee..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="table-responsive px-1 my-1 px-sm-0">
|
||||
<table className="table align-middle mb-0">
|
||||
<thead className="table-light">
|
||||
<tr>
|
||||
<th
|
||||
onClick={() =>
|
||||
requestSort((e) => `${e.firstName} ${e.lastName}`)
|
||||
}
|
||||
className="text-start cursor-pointer"
|
||||
>
|
||||
<span className="ps-2">Name {getSortIcon()}</span>
|
||||
</th>
|
||||
<th className="text-start">Role</th>
|
||||
<th scope="col">Status</th>
|
||||
<th className="text-start">Bucket</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{employees.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={4} >
|
||||
<div className="d-flex justify-content-center align-items-center py-5">
|
||||
No Employee Available
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
) : filteredEmployees.length === 0 ? (
|
||||
<tr className="my-4">
|
||||
<td colSpan={4}>
|
||||
<div className="d-flex justify-content-center align-items-center py-5">
|
||||
No Matchinng Employee Found.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
filteredEmployees?.map((employee) => (
|
||||
<tr key={employee.id}>
|
||||
<td>
|
||||
<div className="d-flex align-items-start text-start">
|
||||
<input
|
||||
className="form-check-input me-3 mt-1"
|
||||
type="checkbox"
|
||||
checked={selectedIds.includes(employee.id)}
|
||||
onChange={() => handleCheckboxChange(employee.id)}
|
||||
/>
|
||||
<div>
|
||||
<p className="fw-semibold mb-0">
|
||||
{`${employee.firstName} ${employee.lastName}`}
|
||||
</p>
|
||||
<small className="text-muted">{employee.email}</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-start">
|
||||
<small className="text-muted">{employee.jobRole}</small>
|
||||
</td>
|
||||
<td>
|
||||
<span
|
||||
className={`badge rounded-pill px-3 py-1 ${
|
||||
employee.isActive
|
||||
? "bg-success-subtle text-success"
|
||||
: "bg-danger-subtle text-danger"
|
||||
}`}
|
||||
>
|
||||
{employee.isActive ? "Active" : "Inactive"}
|
||||
</span>
|
||||
</td>
|
||||
<td className="text-start">
|
||||
<small className="text-muted">
|
||||
<i className="fa fa-hashtag" aria-hidden="true"></i>{" "}
|
||||
{employee.jobRole}
|
||||
</small>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmployeeList;
|
@ -8,15 +8,36 @@ import Directory from "../../pages/Directory/Directory";
|
||||
import { DirectoryRepository } from "../../repositories/DirectoryRepository";
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
import { useBuckets } from "../../hooks/useDirectory";
|
||||
import EmployeeList from "./EmployeeList";
|
||||
import { useAllEmployees, useEmployees } from "../../hooks/useEmployees";
|
||||
import { useSortableData } from "../../hooks/useSortableData";
|
||||
import ConfirmModal from "../common/ConfirmModal";
|
||||
|
||||
const ManageBucket = () =>
|
||||
{
|
||||
const ManageBucket = () => {
|
||||
const [bucketList, setBucketList] = useState([]);
|
||||
|
||||
const { buckets } = useBuckets();
|
||||
const { employeesList } = useAllEmployees(false);
|
||||
const { buckets, loading,refetch } = useBuckets();
|
||||
const [action_bucket, setAction_bucket] = useState(false);
|
||||
const [isSubmitting, setSubmitting] = useState(false);
|
||||
const [selected_bucket, select_bucket] = useState(null);
|
||||
const [deleteBucket, setDeleteBucket] = useState(null);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const {
|
||||
items: sortedBuckteList,
|
||||
requestSort,
|
||||
sortConfig,
|
||||
} = useSortableData(bucketList, {
|
||||
key: (e) => `${e.name}`,
|
||||
direction: "asc",
|
||||
});
|
||||
const getSortIcon = () => {
|
||||
if (!sortConfig) return null;
|
||||
return sortConfig.direction === "asc" ? (
|
||||
<i className="bx bx-caret-up text-secondary"></i>
|
||||
) : (
|
||||
<i className="bx bx-caret-down text-secondary"></i>
|
||||
);
|
||||
};
|
||||
|
||||
const {
|
||||
register,
|
||||
@ -29,197 +50,290 @@ const ManageBucket = () =>
|
||||
name: "",
|
||||
description: "",
|
||||
},
|
||||
} );
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
setSubmitting(true);
|
||||
try {
|
||||
let response;
|
||||
});
|
||||
|
||||
if ( selected_bucket )
|
||||
{
|
||||
let payload ={...data, id:selected_bucket.id}
|
||||
response = await DirectoryRepository.UpdateBuckets(selected_bucket.id, payload);
|
||||
const cache_buckets = getCachedData("buckets") || [];
|
||||
const updatedBuckets = cache_buckets.map((bucket) =>
|
||||
bucket.id === selected_bucket.id ? response?.data : bucket
|
||||
);
|
||||
cacheData( "buckets", updatedBuckets );
|
||||
setBucketList(updatedBuckets);
|
||||
showToast("Bucket Updated Successfully", "success");
|
||||
} else {
|
||||
response = await DirectoryRepository.CreateBuckets(data);
|
||||
const cache_buckets = getCachedData("buckets") || [];
|
||||
const updatedBuckets = [...cache_buckets, response?.data];
|
||||
cacheData( "buckets", updatedBuckets );
|
||||
setBucketList(updatedBuckets);
|
||||
showToast("Bucket Created Successfully", "success");
|
||||
const onSubmit = async (data) => {
|
||||
setSubmitting(true);
|
||||
try {
|
||||
let response;
|
||||
|
||||
if (selected_bucket) {
|
||||
let payload = { ...data, id: selected_bucket.id };
|
||||
response = await DirectoryRepository.UpdateBuckets(
|
||||
selected_bucket.id,
|
||||
payload
|
||||
);
|
||||
const cache_buckets = getCachedData("buckets") || [];
|
||||
const updatedBuckets = cache_buckets.map((bucket) =>
|
||||
bucket.id === selected_bucket.id ? response?.data : bucket
|
||||
);
|
||||
cacheData("buckets", updatedBuckets);
|
||||
setBucketList(updatedBuckets);
|
||||
showToast("Bucket Updated Successfully", "success");
|
||||
} else {
|
||||
response = await DirectoryRepository.CreateBuckets(data);
|
||||
const cache_buckets = getCachedData("buckets") || [];
|
||||
const updatedBuckets = [...cache_buckets, response?.data];
|
||||
cacheData("buckets", updatedBuckets);
|
||||
setBucketList(updatedBuckets);
|
||||
showToast("Bucket Created Successfully", "success");
|
||||
}
|
||||
|
||||
handleBack();
|
||||
} catch (error) {
|
||||
const message =
|
||||
error?.response?.data?.message ||
|
||||
error?.message ||
|
||||
"Error occurred during API call";
|
||||
showToast(message, "error");
|
||||
}
|
||||
};
|
||||
|
||||
handleBack();
|
||||
} catch (error) {
|
||||
const message =
|
||||
error?.response?.data?.message ||
|
||||
error?.message ||
|
||||
"Error occurred during API call";
|
||||
showToast(message, "error");
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteContact = async () => {
|
||||
try {
|
||||
const resp = await DirectoryRepository.DeleteBucket( deleteBucket );
|
||||
const cache_buckets = getCachedData("buckets") || [];
|
||||
const updatedBuckets = cache_buckets.filter((bucket) =>
|
||||
bucket.id != deleteBucket
|
||||
);
|
||||
cacheData("buckets", updatedBuckets);
|
||||
setBucketList(updatedBuckets);
|
||||
showToast("Bucket deleted successfully", "success");
|
||||
setDeleteBucket(null);
|
||||
} catch (error) {
|
||||
const message =
|
||||
error?.response?.data?.message ||
|
||||
error?.message ||
|
||||
"Error occurred during API call.";
|
||||
showToast(message, "error");
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
name: selected_bucket?.name || "",
|
||||
description: selected_bucket?.description || "",
|
||||
});
|
||||
}, [ selected_bucket ] );
|
||||
}, [selected_bucket]);
|
||||
|
||||
useEffect(() => {
|
||||
setBucketList(buckets);
|
||||
}, [buckets]);
|
||||
|
||||
useEffect( () =>
|
||||
{
|
||||
setBucketList( buckets )
|
||||
}, [ buckets ] )
|
||||
|
||||
const handleBack = () => {
|
||||
select_bucket(null);
|
||||
setAction_bucket(false);
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
const sortedBucktesList = sortedBuckteList?.filter((bucket) => {
|
||||
const term = searchTerm?.toLowerCase();
|
||||
const name = bucket.name?.toLowerCase();
|
||||
return name?.includes(term);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="container m-0 p-0" style={{ minHeight: "200px" }}>
|
||||
<div className="d-flex justify-content-center">
|
||||
<p className="fs-h6 fw-semibold m-0">Manage Buckets</p>
|
||||
</div>
|
||||
<div className="d-flex justify-content-between px-2 px-sm-0 mt-5 mt-sm-1 align-items-center">
|
||||
<i
|
||||
className={`fa-solid fa-arrow-left fs-5 cursor-pointer ${
|
||||
action_bucket ? "" : "d-none"
|
||||
}`}
|
||||
onClick={handleBack}
|
||||
></i>
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-xs btn-primary ms-auto ${
|
||||
action_bucket ? "d-none" : ""
|
||||
}`}
|
||||
onClick={() => setAction_bucket(true)}
|
||||
<>
|
||||
{deleteBucket && (
|
||||
<div
|
||||
className={`modal fade ${deleteBucket ? "show" : ""}`}
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{
|
||||
display: deleteBucket ? "block" : "none",
|
||||
backgroundColor: deleteBucket ? "rgba(0,0,0,0.5)" : "transparent",
|
||||
}}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
Add Bucket
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
{!action_bucket ? (
|
||||
<div className="table-responsive text-nowrap pt-1 px-2 px-sm-0">
|
||||
<table className="table px-2">
|
||||
<thead className="p-0">
|
||||
<tr className="p-0">
|
||||
<th colSpan={2}>
|
||||
<div className="d-flex justify-content-start align-items-center gap-1">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="fa-solid fa-bucket"
|
||||
color="info"
|
||||
/>
|
||||
<span>Name</span>
|
||||
</div>
|
||||
</th>
|
||||
<th className="text-start d-none d-sm-table-cell">
|
||||
<div className="d-flex align-items-center justify-content-start gap-1">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="fa-solid fa-file-lines"
|
||||
color="primary"
|
||||
/>
|
||||
<span>Description</span>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div className="d-flex align-items-center justify-content-center gap-1">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="fa-solid fa-gear"
|
||||
color="secondary"
|
||||
/>
|
||||
<span>Action</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<ConfirmModal
|
||||
type={"delete"}
|
||||
header={"Delete Bucket"}
|
||||
message={"Are you sure you want delete?"}
|
||||
onSubmit={handleDeleteContact}
|
||||
onClose={() => setDeleteBucket(null)}
|
||||
// loading={IsDeleting}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<tbody className="table-border-bottom-0 overflow-auto">
|
||||
{bucketList.map((bucket) => (
|
||||
<tr key={bucket.id}>
|
||||
<td colSpan={2} className="text-start">
|
||||
{bucket.name}
|
||||
</td>
|
||||
<td className="text-start d-none d-sm-table-cell">
|
||||
{bucket.description}
|
||||
</td>
|
||||
<td className="justify-content-center">
|
||||
<div className="d-flex justify-content-center align-items-center gap-2">
|
||||
<i
|
||||
className="bx bx-edit bx-sm text-primary cursor-pointer"
|
||||
onClick={() => {
|
||||
select_bucket(bucket);
|
||||
setAction_bucket(true);
|
||||
}}
|
||||
></i>
|
||||
<i className="bx bx-trash bx-sm text-danger cursor-pointer"></i>
|
||||
<i className="bx bx-user-plus cursor-pointer"></i>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="px-2 px-sm-0">
|
||||
<div className="">
|
||||
<label className="form-label">Bucket Name</label>
|
||||
<div className="container m-0 p-0" style={{ minHeight: "200px" }}>
|
||||
<div className="d-flex justify-content-center">
|
||||
<p className="fs-6 fw-semibold m-0">Manage Buckets</p>
|
||||
</div>
|
||||
<div className="d-flex justify-content-between px-2 px-sm-0 mt-5 mt-sm-1 align-items-center">
|
||||
{action_bucket ? (
|
||||
<i
|
||||
className={`fa-solid fa-arrow-left fs-5 cursor-pointer`}
|
||||
onClick={handleBack}
|
||||
></i>
|
||||
) : (
|
||||
<div className="d-flex align-items-center gap-2">
|
||||
<input
|
||||
type="search"
|
||||
className="form-control form-control-sm"
|
||||
{...register("name")}
|
||||
/>
|
||||
{errors.name && (
|
||||
<small className="danger-text">{errors.name.message}</small>
|
||||
)}
|
||||
placeholder="Search Bucket ..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<i
|
||||
className={`bx bx-refresh cursor-pointer fs-4 ${loading ? "spin" : ""
|
||||
}`}
|
||||
title="Refresh"
|
||||
onClick={() => rrefetch()}
|
||||
/>
|
||||
</div>
|
||||
<div className="">
|
||||
<label className="form-label">Bucket Discription</label>
|
||||
<textarea
|
||||
className="form-control form-control-sm"
|
||||
{...register("description")}
|
||||
/>
|
||||
{errors.description && (
|
||||
<small className="danger-text">
|
||||
{errors.description.message}
|
||||
</small>
|
||||
)}
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-sm btn-primary ms-auto ${
|
||||
action_bucket ? "d-none" : ""
|
||||
}`}
|
||||
onClick={() => setAction_bucket(true)}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
Add Bucket
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
{!action_bucket ? (
|
||||
<div className="table-responsive text-nowrap pt-1 px-2 px-sm-0">
|
||||
<table className="table px-2">
|
||||
<thead className="p-0 table-light">
|
||||
<tr className="p-0">
|
||||
<th
|
||||
colSpan={2}
|
||||
className="cursor-pointer"
|
||||
onClick={() => requestSort((e) => `${e.name} `)}
|
||||
>
|
||||
<div className="d-flex justify-content-start align-items-center gap-1 mx-2">
|
||||
<span>Name {getSortIcon()}</span>
|
||||
</div>
|
||||
</th>
|
||||
<th className="text-start d-none d-sm-table-cell">
|
||||
<div className="d-flex align-items-center justify-content-center gap-1">
|
||||
<span>Description</span>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div className="d-flex align-items-center justify-content-center gap-1">
|
||||
<span>Action</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody className="table-border-bottom-0 overflow-auto">
|
||||
{loading && (
|
||||
<tr className="mt-10">
|
||||
<td colSpan={4}>
|
||||
{" "}
|
||||
<div className="d-flex justify-content-center align-items-center py-5">
|
||||
Loading...
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{!loading && buckets.length == 0 && (
|
||||
<tr>
|
||||
<td colSpan={4}>
|
||||
<div className="d-flex justify-content-center align-items-center py-5">
|
||||
Bucket Not Available.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{!loading && sortedBucktesList.length == 0 && (
|
||||
<tr>
|
||||
<td className="text-center py-4 h-25" colSpan={4}>
|
||||
<div className="d-flex justify-content-center align-items-center py-5">
|
||||
No Matching Bucket Found.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{sortedBucktesList.map((bucket) => (
|
||||
<tr key={bucket.id}>
|
||||
<td colSpan={2} className="text-start text-wrap">
|
||||
<i className="bx bx-right-arrow-alt me-1"></i>{" "}
|
||||
{bucket.name}
|
||||
</td>
|
||||
<td
|
||||
className="text-start d-none d-sm-table-cell text-wrap"
|
||||
style={{ width: "60%" }}
|
||||
>
|
||||
{bucket.description}
|
||||
</td>
|
||||
<td className="justify-content-center">
|
||||
<div className="d-flex justify-content-center align-items-center gap-2">
|
||||
<i
|
||||
className="bx bx-edit bx-sm text-primary cursor-pointer"
|
||||
onClick={() => {
|
||||
select_bucket(bucket);
|
||||
setAction_bucket(true);
|
||||
}}
|
||||
></i>
|
||||
<i
|
||||
className="bx bx-trash bx-sm text-danger cursor-pointer"
|
||||
onClick={() => setDeleteBucket(bucket?.id)}
|
||||
></i>
|
||||
<i className="bx bx-user-plus cursor-pointer"></i>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="mt-2 d-flex justify-content-center gap-3">
|
||||
<button
|
||||
type="reset"
|
||||
className="btn btn-sm btn-secondary"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-sm btn-primary"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? "Please wait..." : "Submit"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
) : (
|
||||
<>
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="px-2 px-sm-0">
|
||||
<div className="">
|
||||
<label className="form-label">Bucket Name</label>
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
{...register("name")}
|
||||
/>
|
||||
{errors.name && (
|
||||
<small className="danger-text">{errors.name.message}</small>
|
||||
)}
|
||||
</div>
|
||||
<div className="">
|
||||
<label className="form-label">Bucket Discription</label>
|
||||
<textarea
|
||||
className="form-control form-control-sm"
|
||||
rows="3"
|
||||
{...register("description")}
|
||||
/>
|
||||
{errors.description && (
|
||||
<small className="danger-text">
|
||||
{errors.description.message}
|
||||
</small>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-2 d-flex justify-content-center gap-3">
|
||||
<button
|
||||
type="reset"
|
||||
className="btn btn-sm btn-secondary"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-sm btn-primary"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? "Please wait..." : "Submit"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<EmployeeList employees={employeesList} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -14,10 +14,11 @@ import useMaster, {
|
||||
} from "../../hooks/masterHook/useMaster";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { changeMaster } from "../../slices/localVariablesSlice";
|
||||
import { useBuckets } from "../../hooks/useDirectory";
|
||||
import { useBuckets, useOrganization } from "../../hooks/useDirectory";
|
||||
import { useProjects } from "../../hooks/useProjects";
|
||||
import SelectMultiple from "../common/SelectMultiple";
|
||||
import {ContactSchema} from "./DirectorySchema";
|
||||
import InputSuggestions from "../common/InputSuggestion";
|
||||
|
||||
|
||||
|
||||
@ -25,13 +26,15 @@ const ManageDirectory = ({ submitContact, onCLosed }) => {
|
||||
const selectedMaster = useSelector(
|
||||
(store) => store.localVariables.selectedMaster
|
||||
);
|
||||
const [categoryData, setCategoryData] = useState([]);
|
||||
const [ categoryData, setCategoryData ] = useState( [] );
|
||||
|
||||
const [TagsData, setTagsData] = useState([]);
|
||||
const { data, loading } = useMaster();
|
||||
const { buckets, loading: bucketsLoaging } = useBuckets();
|
||||
const { projects, loading: projectLoading } = useProjects();
|
||||
const { contactCategory, loading: contactCategoryLoading } =
|
||||
useContactCategory();
|
||||
const {organizationList,loading:orgLoading} = useOrganization()
|
||||
const { contactTags, loading: Tagloading } = useContactTags();
|
||||
const [IsSubmitting, setSubmitting] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
@ -136,6 +139,7 @@ useEffect(() => {
|
||||
setSubmitting(true);
|
||||
submitContact(cleaned, reset, setSubmitting);
|
||||
};
|
||||
const orgValue = watch("organization")
|
||||
|
||||
const handleClosed = () => {
|
||||
onCLosed();
|
||||
@ -160,10 +164,12 @@ useEffect(() => {
|
||||
|
||||
<div className="col-md-6 text-start">
|
||||
<label className="form-label">Organization</label>
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
{...register("organization")}
|
||||
/>
|
||||
<InputSuggestions
|
||||
organizationList={organizationList}
|
||||
value={getValues("organization") || ""}
|
||||
onChange={(val) => setValue("organization", val)}
|
||||
error={errors.organization?.message}
|
||||
/>
|
||||
{errors.organization && (
|
||||
<small className="danger-text">
|
||||
{errors.organization.message}
|
||||
@ -356,8 +362,7 @@ useEffect(() => {
|
||||
<label className="form-label ">Select Label</label>
|
||||
|
||||
<ul
|
||||
className="d-flex flex-wrap px-1 list-unstyled overflow-auto mb-0"
|
||||
style={{ maxHeight: "80px" }}
|
||||
className="d-flex flex-wrap px-1 list-unstyled mb-0"
|
||||
>
|
||||
{bucketsLoaging && <p>Loading...</p>}
|
||||
{buckets?.map((item) => (
|
||||
|
@ -6,7 +6,6 @@ import NotesDirectory from "./NotesDirectory";
|
||||
|
||||
const ProfileContactDirectory = ({ contact, setOpen_contact, closeModal }) => {
|
||||
const { conatProfile, loading } = useContactProfile(contact?.id);
|
||||
const [activeTab, setActiveTab] = useState("profile");
|
||||
const [profileContact, setProfileContact] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
@ -34,9 +33,11 @@ const ProfileContactDirectory = ({ contact, setOpen_contact, closeModal }) => {
|
||||
<i className="bx bx-building bx-xs"></i>{" "}
|
||||
{conatProfile?.organization}
|
||||
</span>
|
||||
<span className="small-text">Manager</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex flex-column text-start">
|
||||
<div className="row">
|
||||
<div className="col-12 col-md-6 d-flex flex-column text-start">
|
||||
{conatProfile?.contactEmails?.length > 0 && (
|
||||
<div className="d-flex mb-2">
|
||||
<div style={{ width: "100px", minWidth: "100px" }}>
|
||||
@ -61,7 +62,7 @@ const ProfileContactDirectory = ({ contact, setOpen_contact, closeModal }) => {
|
||||
</div>
|
||||
<div>
|
||||
<ul className="list-inline mb-0">
|
||||
{conatProfile.contactPhones.map((phone, idx) => (
|
||||
{conatProfile?.contactPhones.map((phone, idx) => (
|
||||
<li className="list-inline-item me-3" key={idx}>
|
||||
<i className="bx bx-phone bx-xs me-1"></i>
|
||||
{phone.phoneNumber}
|
||||
@ -87,7 +88,28 @@ const ProfileContactDirectory = ({ contact, setOpen_contact, closeModal }) => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{ conatProfile?.buckets?.length > 0 &&
|
||||
<div className="col-12 col-md-6 d-flex flex-column text-start">
|
||||
{conatProfile?.contactEmails?.length > 0 && (
|
||||
<div className="d-flex mb-2 align-items-center">
|
||||
<div style={{ width: "100px", minWidth: "100px" }}>
|
||||
<p className="m-0">Buckets</p>
|
||||
</div>
|
||||
<div>
|
||||
<ul className="list-inline mb-0">
|
||||
{conatProfile.buckets.map((bucket) => (
|
||||
<li className="list-inline-item me-2" key={bucket.id}>
|
||||
<span class="badge bg-label-primary my-1">{ bucket.name}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<hr className="my-1" />
|
||||
<NotesDirectory
|
||||
|
@ -386,8 +386,8 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
<label className="form-label ">Select Label</label>
|
||||
|
||||
<ul
|
||||
className="d-flex flex-wrap px-1 list-unstyled overflow-auto mb-0"
|
||||
style={{ maxHeight: "80px" }}
|
||||
className="d-flex flex-wrap px-1 list-unstyled mb-0"
|
||||
|
||||
>
|
||||
{bucketsLoaging && <p>Loading...</p>}
|
||||
{buckets?.map((item) => (
|
||||
|
35
src/components/common/FloatingMenu.css
Normal file
35
src/components/common/FloatingMenu.css
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
.fab-container {
|
||||
position: fixed;
|
||||
bottom: 35px;
|
||||
right: 30px;
|
||||
z-index: 1050;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.fab-main {
|
||||
/* width: 45px;
|
||||
height: 45px;
|
||||
border-radius: 100%;
|
||||
background-color: #0d6efd;
|
||||
color: white;
|
||||
border: none; */
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||
/* font-size: 24px; */
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.fab-option {
|
||||
pointer-events: auto;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.fab-container {
|
||||
right: 20px;
|
||||
left: 50%;
|
||||
bottom: 20px;
|
||||
}
|
||||
}
|
33
src/components/common/FloatingMenu.jsx
Normal file
33
src/components/common/FloatingMenu.jsx
Normal file
@ -0,0 +1,33 @@
|
||||
import React, { useState } from "react";
|
||||
import {useFab} from "../../Context/FabContext";
|
||||
import './FloatingMenu.css'
|
||||
|
||||
const FloatingMenu = () => {
|
||||
const { actions } = useFab();
|
||||
const [open, setOpen] = useState(false);
|
||||
if (actions.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="fab-container">
|
||||
{open &&
|
||||
actions.map((action, index) => (
|
||||
<button
|
||||
key={index}
|
||||
className={`badge bg-label-${action.color} rounded-pill mb-2 d-inline-flex align-items-center gap-2 px-3 py-1 cursor-pointer fab-option`}
|
||||
onClick={action.onClick}
|
||||
title={action.label}
|
||||
>
|
||||
<i className={action.icon}></i>
|
||||
<span>{action.label}</span>
|
||||
</button>
|
||||
))}
|
||||
|
||||
<button type="button" className="btn btn-lg btn-icon rounded-pill me-2 btn-primary fab-main " onClick={() => setOpen(!open)}>
|
||||
<span className={`bx ${open ? "bx-x" : "bx-plus"}`}></span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FloatingMenu;
|
||||
|
@ -54,7 +54,20 @@ useEffect(() => {
|
||||
// Dynamically generate data-bs attributes
|
||||
const dataAttributesProps = Object.keys(dataAttributes).map(key => ({
|
||||
[key]: dataAttributes[key],
|
||||
}));
|
||||
} ) );
|
||||
|
||||
|
||||
|
||||
// The gray background
|
||||
const backdropStyle = {
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: 'rgba(0,0,0,0.3)',
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -65,8 +78,9 @@ useEffect(() => {
|
||||
aria-hidden="true"
|
||||
ref={modalRef} // Assign the ref to the modal element
|
||||
{...dataAttributesProps}
|
||||
style={backdropStyle}
|
||||
>
|
||||
<div className={`modal-dialog ${dialogClass} ${modalSizeClass } mx-sm-auto mx-1`} role={role}>
|
||||
<div className={`modal-dialog ${dialogClass} ${modalSizeClass } mx-sm-auto mx-1`} role={role} >
|
||||
<div className="modal-content">
|
||||
<div className="modal-header p-0">
|
||||
{/* Close button inside the modal header */}
|
||||
|
79
src/components/common/InputSuggestion.jsx
Normal file
79
src/components/common/InputSuggestion.jsx
Normal file
@ -0,0 +1,79 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
const InputSuggestions = ({
|
||||
organizationList = [],
|
||||
value,
|
||||
onChange,
|
||||
error,
|
||||
}) => {
|
||||
const [filteredList, setFilteredList] = useState([]);
|
||||
const [showSuggestions, setShowSuggestions] = useState(false);
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const val = e.target.value;
|
||||
onChange(val);
|
||||
|
||||
const matches = organizationList.filter((org) =>
|
||||
org.toLowerCase().includes(val.toLowerCase())
|
||||
);
|
||||
setFilteredList(matches);
|
||||
setShowSuggestions(true);
|
||||
};
|
||||
|
||||
const handleSelectSuggestion = (val) => {
|
||||
onChange(val);
|
||||
setShowSuggestions(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="position-relative">
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
value={value}
|
||||
onChange={handleInputChange}
|
||||
onBlur={() => setTimeout(() => setShowSuggestions(false), 150)}
|
||||
onFocus={() => {
|
||||
if (value) setShowSuggestions(true);
|
||||
}}
|
||||
/>
|
||||
{showSuggestions && filteredList.length > 0 && (
|
||||
<ul
|
||||
className="list-group shadow-sm position-absolute w-100 bg-white border zindex-tooltip"
|
||||
style={{
|
||||
maxHeight: "180px",
|
||||
overflowY: "auto",
|
||||
marginTop: "2px",
|
||||
zIndex: 1000,
|
||||
borderRadius:"0px"
|
||||
}}
|
||||
>
|
||||
{filteredList.map((org) => (
|
||||
<li
|
||||
key={org}
|
||||
className="list-group-item list-group-item-action border-none "
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
padding: "5px 12px",
|
||||
fontSize: "14px",
|
||||
transition: "background-color 0.2s",
|
||||
}}
|
||||
onMouseDown={() => handleSelectSuggestion(org)}
|
||||
onMouseEnter={(e) =>
|
||||
(e.currentTarget.style.backgroundColor = "#f8f9fa")
|
||||
}
|
||||
onMouseLeave={(e) =>
|
||||
(e.currentTarget.style.backgroundColor = "transparent")
|
||||
}
|
||||
>
|
||||
{org}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{error && <small className="danger-text">{error}</small>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InputSuggestions;
|
@ -1,21 +1,21 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import './MultiSelectDropdown.css';
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { useFormContext } from "react-hook-form";
|
||||
import "./MultiSelectDropdown.css";
|
||||
|
||||
const SelectMultiple = ({
|
||||
name,
|
||||
options = [],
|
||||
label = 'Select options',
|
||||
labelKey = 'name',
|
||||
valueKey = 'id',
|
||||
placeholder = 'Please select...',
|
||||
IsLoading = false
|
||||
label = "Select options",
|
||||
labelKey = "name",
|
||||
valueKey = "id",
|
||||
placeholder = "Please select...",
|
||||
IsLoading = false,
|
||||
}) => {
|
||||
const { setValue, watch } = useFormContext();
|
||||
const selectedValues = watch(name) || [];
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const dropdownRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
@ -24,8 +24,8 @@ const SelectMultiple = ({
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => document.removeEventListener("mousedown", handleClickOutside);
|
||||
}, []);
|
||||
|
||||
const handleCheckboxChange = (value) => {
|
||||
@ -44,35 +44,37 @@ const SelectMultiple = ({
|
||||
<div ref={dropdownRef} className="multi-select-dropdown-container">
|
||||
<label className="form-label mb-1">{label}</label>
|
||||
|
||||
<div
|
||||
className="multi-select-dropdown-header"
|
||||
onClick={() => setIsOpen((prev) => !prev)}
|
||||
>
|
||||
<span
|
||||
className={
|
||||
selectedValues.length > 0
|
||||
? 'placeholder-style-selected'
|
||||
: 'placeholder-style'
|
||||
}
|
||||
>
|
||||
<div className="selected-badges-container">
|
||||
{selectedValues.length > 0 ? (
|
||||
selectedValues.map((val) => {
|
||||
const found = options.find((opt) => opt[valueKey] === val);
|
||||
return (
|
||||
<span key={val} className="badge badge-selected-item mx-1">
|
||||
{found ? found[labelKey] : ''}
|
||||
</span>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<span className="placeholder-text">{placeholder}</span>
|
||||
)}
|
||||
</div>
|
||||
</span>
|
||||
<i className="bx bx-chevron-down"></i>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="multi-select-dropdown-header"
|
||||
onClick={() => setIsOpen((prev) => !prev)}
|
||||
>
|
||||
<span
|
||||
className={
|
||||
selectedValues.length > 0
|
||||
? "placeholder-style-selected"
|
||||
: "placeholder-style"
|
||||
}
|
||||
>
|
||||
<div className="selected-badges-container">
|
||||
{selectedValues.length > 0 ? (
|
||||
selectedValues.map((val) => {
|
||||
const found = options.find((opt) => opt[valueKey] === val);
|
||||
return (
|
||||
<span
|
||||
key={val}
|
||||
className="badge badge-selected-item mx-1 mb-1"
|
||||
>
|
||||
{found ? found[labelKey] : ""}
|
||||
</span>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<span className="placeholder-text">{placeholder}</span>
|
||||
)}
|
||||
</div>
|
||||
</span>
|
||||
<i className="bx bx-chevron-down"></i>
|
||||
</div>
|
||||
|
||||
{isOpen && (
|
||||
<div className="multi-select-dropdown-options">
|
||||
@ -94,7 +96,9 @@ const SelectMultiple = ({
|
||||
return (
|
||||
<div
|
||||
key={valueVal}
|
||||
className={`multi-select-dropdown-option ${isChecked ? 'selected' : ''}`}
|
||||
className={`multi-select-dropdown-option ${
|
||||
isChecked ? "selected" : ""
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
@ -105,14 +109,16 @@ const SelectMultiple = ({
|
||||
<label className="text-secondary">{labelVal}</label>
|
||||
</div>
|
||||
);
|
||||
} )}
|
||||
})}
|
||||
{!IsLoading && filteredOptions.length === 0 && (
|
||||
<div className='multi-select-dropdown-Not-found'>
|
||||
<label className="text-muted">Not Found {`'${searchText}'`}</label>
|
||||
<div className="multi-select-dropdown-Not-found">
|
||||
<label className="text-muted">
|
||||
Not Found {`'${searchText}'`}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
{IsLoading && filteredOptions.length === 0 && (
|
||||
<div className='multi-select-dropdown-Not-found'>
|
||||
<div className="multi-select-dropdown-Not-found">
|
||||
<label className="text-muted">Loading...</label>
|
||||
</div>
|
||||
)}
|
||||
|
@ -37,47 +37,46 @@ export const useDirectory = (isActive) => {
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const useBuckets = () => {
|
||||
const [buckets, setBuckets] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const fetchBuckets = async () => {
|
||||
const cacheBuckets = getCachedData("buckets");
|
||||
if (!cacheBuckets) {
|
||||
setLoading( true );
|
||||
try {
|
||||
const resp = await DirectoryRepository.GetBucktes();
|
||||
setBuckets(resp.data);
|
||||
cacheData( "buckets", resp.data );
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
const msg =
|
||||
error?.response?.data?.message || error?.message || "Something went wrong";
|
||||
setError(msg);
|
||||
}
|
||||
} else {
|
||||
setBuckets(cacheBuckets);
|
||||
setLoading(true);
|
||||
try {
|
||||
const resp = await DirectoryRepository.GetBucktes();
|
||||
setBuckets(resp.data);
|
||||
cacheData("buckets", resp.data);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
const msg =
|
||||
error?.response?.data?.message ||
|
||||
error?.message ||
|
||||
"Something went wrong";
|
||||
setError( msg );
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchBuckets();
|
||||
const cacheBuckets = getCachedData("buckets");
|
||||
if (!cacheBuckets) {
|
||||
fetchBuckets();
|
||||
} else {
|
||||
setBuckets(cacheBuckets);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { buckets, loading, error };
|
||||
return { buckets, loading, error, refetch: fetchBuckets };
|
||||
};
|
||||
|
||||
export const useContactProfile = (id) =>
|
||||
{
|
||||
const [ conatProfile, setContactProfile ] = useState( null );
|
||||
const [ loading, setLoading ] = useState( false );
|
||||
const [ Error, setError ] = useState( "" );
|
||||
export const useContactProfile = (id) => {
|
||||
const [conatProfile, setContactProfile] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [Error, setError] = useState("");
|
||||
|
||||
|
||||
const fetchContactProfile = async () => {
|
||||
const fetchContactProfile = async () => {
|
||||
const cached = getCachedData("Contact Profile");
|
||||
|
||||
if (!cached || cached.contactId !== id) {
|
||||
@ -88,7 +87,9 @@ const fetchContactProfile = async () => {
|
||||
cacheData("Contact Profile", { data: resp.data, contactId: id });
|
||||
} catch (err) {
|
||||
const msg =
|
||||
err?.response?.data?.message || err?.message || "Something went wrong";
|
||||
err?.response?.data?.message ||
|
||||
err?.message ||
|
||||
"Something went wrong";
|
||||
setError(msg);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@ -99,35 +100,33 @@ const fetchContactProfile = async () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if ( id )
|
||||
{
|
||||
if (id) {
|
||||
fetchContactProfile(id);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
return { conatProfile, loading, Error };
|
||||
}
|
||||
};
|
||||
|
||||
export const useContactNotes = (id, IsActive) => {
|
||||
const [contactNotes, setContactNotes] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [Error, setError] = useState("");
|
||||
|
||||
export const useContactNotes = (id,IsActive) =>
|
||||
{
|
||||
const [ contactNotes, setContactNotes ] = useState( [] );
|
||||
const [ loading, setLoading ] = useState( false );
|
||||
const [ Error, setError ] = useState( "" );
|
||||
|
||||
|
||||
const fetchContactNotes = async () => {
|
||||
const fetchContactNotes = async () => {
|
||||
const cached = getCachedData("Contact Notes");
|
||||
|
||||
if (!cached || cached.contactId !== id) {
|
||||
setLoading(true);
|
||||
try {
|
||||
const resp = await DirectoryRepository.GetNote(id,IsActive);
|
||||
const resp = await DirectoryRepository.GetNote(id, IsActive);
|
||||
setContactNotes(resp.data);
|
||||
cacheData("Contact Notes", { data: resp.data, contactId: id });
|
||||
} catch (err) {
|
||||
const msg =
|
||||
err?.response?.data?.message || err?.message || "Something went wrong";
|
||||
err?.response?.data?.message ||
|
||||
err?.message ||
|
||||
"Something went wrong";
|
||||
setError(msg);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@ -138,11 +137,43 @@ const fetchContactNotes = async () => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if ( id )
|
||||
{
|
||||
if (id) {
|
||||
fetchContactNotes(id);
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
return { contactNotes, loading, Error };
|
||||
}
|
||||
};
|
||||
|
||||
export const useOrganization = () => {
|
||||
const [organizationList, setOrganizationList] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const fetchOrg = async () => {
|
||||
const cacheOrg = getCachedData("organizations");
|
||||
if (cacheOrg?.length != 0) {
|
||||
setLoading(true);
|
||||
try {
|
||||
const resp = await DirectoryRepository.GetOrganizations();
|
||||
cacheData("organizations", resp.data);
|
||||
setOrganizationList(resp.data);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
const msg =
|
||||
error?.response?.data?.message ||
|
||||
error?.message ||
|
||||
"Something went wrong";
|
||||
setError(msg);
|
||||
}
|
||||
} else {
|
||||
setOrganizationList(cacheOrg);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchOrg();
|
||||
}, []);
|
||||
|
||||
return { organizationList, loading, error };
|
||||
};
|
||||
|
35
src/hooks/useSortableData.js
Normal file
35
src/hooks/useSortableData.js
Normal file
@ -0,0 +1,35 @@
|
||||
|
||||
import { useState, useMemo } from 'react';
|
||||
|
||||
export const useSortableData = (items, config = null) => {
|
||||
const [sortConfig, setSortConfig] = useState(config);
|
||||
|
||||
const sortedItems = useMemo(() => {
|
||||
let sortableItems = [...items];
|
||||
if (sortConfig !== null) {
|
||||
sortableItems.sort((a, b) => {
|
||||
const aValue = sortConfig.key(a).toLowerCase();
|
||||
const bValue = sortConfig.key(b).toLowerCase();
|
||||
|
||||
if (aValue < bValue) return sortConfig.direction === 'asc' ? -1 : 1;
|
||||
if (aValue > bValue) return sortConfig.direction === 'asc' ? 1 : -1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
return sortableItems;
|
||||
}, [items, sortConfig]);
|
||||
|
||||
const requestSort = (keyFn) => {
|
||||
let direction = 'asc';
|
||||
if (
|
||||
sortConfig &&
|
||||
sortConfig.key.toString() === keyFn.toString() &&
|
||||
sortConfig.direction === 'asc'
|
||||
) {
|
||||
direction = 'desc';
|
||||
}
|
||||
setSortConfig({ key: keyFn, direction });
|
||||
};
|
||||
|
||||
return { items: sortedItems, requestSort, sortConfig };
|
||||
};
|
@ -4,25 +4,30 @@ import Header from "../components/Layout/Header";
|
||||
import Sidebar from "../components/Layout/Sidebar";
|
||||
|
||||
import Footer from "../components/Layout/Footer";
|
||||
import FloatingMenu from "../components/common/FloatingMenu";
|
||||
import { FabProvider } from "../Context/FabContext";
|
||||
|
||||
const HomeLayout = () => {
|
||||
useEffect(() => {
|
||||
Main();
|
||||
}, []);
|
||||
return (
|
||||
<div className="layout-wrapper layout-content-navbar" >
|
||||
<div className="layout-container" >
|
||||
<Sidebar />
|
||||
<div className="layout-page ">
|
||||
<Header />
|
||||
<div className="content-wrapper" >
|
||||
<Outlet />
|
||||
<Footer />
|
||||
<FabProvider>
|
||||
<div className="layout-wrapper layout-content-navbar">
|
||||
<div className="layout-container">
|
||||
<Sidebar />
|
||||
<div className="layout-page ">
|
||||
<Header />
|
||||
<div className="content-wrapper">
|
||||
<Outlet />
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
<FloatingMenu />
|
||||
<div className="layout-overlay layout-menu-toggle"></div>
|
||||
</div>
|
||||
<div className="layout-overlay layout-menu-toggle"></div>
|
||||
</div>
|
||||
</div>
|
||||
</FabProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -18,6 +18,7 @@ import ConfirmModal from "../../components/common/ConfirmModal";
|
||||
import DirectoryListTableHeader from "./DirectoryListTableHeader";
|
||||
import DirectoryPageHeader from "./DirectoryPageHeader";
|
||||
import ManageBucket from "../../components/Directory/ManageBucket";
|
||||
import {useFab} from "../../Context/FabContext";
|
||||
|
||||
const Directory = () =>
|
||||
{
|
||||
@ -36,11 +37,12 @@ const Directory = () =>
|
||||
const [openBucketModal,setOpenBucketModal] = useState(false)
|
||||
|
||||
const [tempSelectedBucketIds, setTempSelectedBucketIds] = useState([]);
|
||||
const [tempSelectedCategoryIds, setTempSelectedCategoryIds] = useState([]);
|
||||
const [ tempSelectedCategoryIds, setTempSelectedCategoryIds ] = useState( [] );
|
||||
const {setActions} = useFab()
|
||||
|
||||
const { contacts, loading } = useDirectory(IsActive);
|
||||
const { contacts, loading , refetch} = useDirectory(IsActive);
|
||||
const { contactCategory, loading: contactCategoryLoading } =
|
||||
useContactCategory();
|
||||
useContactCategory();
|
||||
const {buckets} = useBuckets();
|
||||
|
||||
const submitContact = async (data) => {
|
||||
@ -56,15 +58,17 @@ const Directory = () =>
|
||||
);
|
||||
showToast("Contact updated successfully", "success");
|
||||
setIsOpenModal(false);
|
||||
setSelectedContact(null);
|
||||
setSelectedContact( null );
|
||||
} else {
|
||||
response = await DirectoryRepository.CreateContact(data);
|
||||
updatedContacts = [...contacts_cache, response.data];
|
||||
showToast("Contact created successfully", "success");
|
||||
setIsOpenModal(false);
|
||||
}
|
||||
cacheData("Contacts", {data:updatedContacts,isActive:IsActive});
|
||||
setContactList(updatedContacts);
|
||||
|
||||
// cacheData("Contacts", {data:updatedContacts,isActive:IsActive});
|
||||
// setContactList(updatedContacts);
|
||||
refetch()
|
||||
} catch (error) {
|
||||
const msg =
|
||||
error.response?.data?.message ||
|
||||
@ -80,7 +84,7 @@ const Directory = () =>
|
||||
const contacts_cache = getCachedData("contacts")?.data || [];
|
||||
|
||||
const response = await DirectoryRepository.DeleteContact(deleteContact);
|
||||
const updatedContacts = ContactList.filter((c) => c.id !== deleteContact);
|
||||
const updatedContacts = ContactList.filter( ( c ) => c.id !== deleteContact );
|
||||
setContactList(updatedContacts);
|
||||
cacheData("Contacts", {data:updatedContacts,isActive:IsActive});
|
||||
showToast("Contact deleted successfully", "success");
|
||||
@ -155,7 +159,7 @@ const Directory = () =>
|
||||
|
||||
return matchesSearch && matchesCategory && matchesBucket;
|
||||
}).sort((a, b) => a.name.localeCompare(b.name));
|
||||
}, [ContactList, searchText, selectedCategoryIds, selectedBucketIds]);
|
||||
}, [ContactList, searchText, selectedCategoryIds, selectedBucketIds,selectedContact]);
|
||||
|
||||
const applyFilter = () => {
|
||||
setSelectedBucketIds(tempSelectedBucketIds);
|
||||
@ -192,8 +196,30 @@ const Directory = () =>
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setActions([
|
||||
{
|
||||
label: "New Contact",
|
||||
icon: "bx bx-plus-circle",
|
||||
color: "warning",
|
||||
onClick: () => setIsOpenModal(true),
|
||||
},
|
||||
{
|
||||
label: "Manage Bucket",
|
||||
icon: "fa-solid fa-bucket fs-5 ",
|
||||
color: "primary",
|
||||
onClick: () => setOpenBucketModal(true),
|
||||
},
|
||||
|
||||
]);
|
||||
|
||||
return () => setActions([]); // Clean up
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="container-xxl flex-grow-1 container-p-y">
|
||||
|
||||
<Breadcrumb
|
||||
data={[
|
||||
{ label: "Home", link: "/dashboard" },
|
||||
@ -257,13 +283,13 @@ const Directory = () =>
|
||||
<GlobalModel
|
||||
isOpen={openBucketModal}
|
||||
closeModal={() =>setOpenBucketModal(false)}
|
||||
size="md"
|
||||
size="lg"
|
||||
>
|
||||
<ManageBucket buckets={buckets} />
|
||||
</GlobalModel>
|
||||
)}
|
||||
|
||||
<div className="card p-2">
|
||||
<div className="card p-2 card-minHeight">
|
||||
<DirectoryPageHeader
|
||||
searchText={searchText}
|
||||
setSearchText={setSearchText}
|
||||
@ -333,6 +359,8 @@ const Directory = () =>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
</div>
|
||||
|
||||
{!loading && currentItems < ITEMS_PER_PAGE && (
|
||||
<nav aria-label="Page ">
|
||||
|
@ -11,43 +11,22 @@ const DirectoryListTableHeader = ( {children, IsActive} ) =>
|
||||
<tr>
|
||||
<th colSpan={2}>
|
||||
<div className="d-flex align-items-center gap-1">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="bx bx-user"
|
||||
color="secondary"
|
||||
onClick={() => alert("User icon clicked")}
|
||||
/>
|
||||
<span>Name</span>
|
||||
</div>
|
||||
</th>
|
||||
<th className="px-2 text-start">
|
||||
<div className="d-flex text-center align-items-center gap-1 justify-content-start">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="bx bx-envelope"
|
||||
color="primary"
|
||||
/>
|
||||
<span>Email</span>
|
||||
</div>
|
||||
</th>
|
||||
<th className="mx-2">
|
||||
<div className="d-flex align-items-center m-0 p-0 gap-1">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="bx bx-phone"
|
||||
color="warning"
|
||||
onClick={() => alert("User icon clicked")}
|
||||
/>
|
||||
<span>Phone</span>
|
||||
</div>
|
||||
</th>
|
||||
<th className="mx-2">
|
||||
<div className="d-flex align-items-center gap-1">
|
||||
<IconButton
|
||||
size={12}
|
||||
iconClass="bx bxs-grid-alt"
|
||||
color="info"
|
||||
/>
|
||||
|
||||
<span>Organization</span>
|
||||
</div>
|
||||
</th>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
const DirectoryPageHeader = ({
|
||||
searchText,
|
||||
@ -16,14 +16,19 @@ const DirectoryPageHeader = ({
|
||||
applyFilter,
|
||||
loading,
|
||||
IsActive,
|
||||
setIsOpenModal,
|
||||
setOpenBucketModal
|
||||
setIsOpenModal,
|
||||
setOpenBucketModal,
|
||||
}) => {
|
||||
const [filtered, setFiltered] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
setFiltered(
|
||||
tempSelectedBucketIds?.length + tempSelectedCategoryIds?.length
|
||||
);
|
||||
}, [tempSelectedBucketIds, tempSelectedCategoryIds]);
|
||||
return (
|
||||
<>
|
||||
<div className="row">
|
||||
|
||||
</div>
|
||||
<div className="row"></div>
|
||||
<div className="row mx-0 px-0 align-items-center">
|
||||
<div className="col-12 col-md-4 mb-2 px-1 d-flex align-items-center ">
|
||||
<input
|
||||
@ -66,22 +71,31 @@ const DirectoryPageHeader = ({
|
||||
<div className="dropdown" style={{ width: "fit-content" }}>
|
||||
<div className="dropdown" style={{ width: "fit-content" }}>
|
||||
<a
|
||||
className="dropdown-toggle hide-arrow cursor-pointer d-flex align-items-center"
|
||||
className="dropdown-toggle hide-arrow cursor-pointer d-flex align-items-center position-relative"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i className="fa-solid fa-filter ms-1 fs-5"></i>
|
||||
<i className={`fa-solid fa-filter ms-1 fs-5 ${filtered > 0 ? 'text-primary' : 'text-muted'}`}></i>
|
||||
|
||||
{filtered > 0 && (
|
||||
<span
|
||||
className="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-warning"
|
||||
style={{ fontSize: "0.4rem"}}
|
||||
>
|
||||
{filtered}
|
||||
</span>
|
||||
)}
|
||||
</a>
|
||||
|
||||
<ul className="dropdown-menu p-3" style={{ width: "320px" }}>
|
||||
<div>
|
||||
<p className="small-text fw-semibold text-muted m-0">
|
||||
<p className="text-small text-muted m-0">
|
||||
Filter by
|
||||
</p>
|
||||
|
||||
{/* Bucket Filter */}
|
||||
<div className="mt-1">
|
||||
<p className="small-text mb-1 fw-semibold">Buckets</p>
|
||||
<p className="text-small mb-1 ">Buckets</p>
|
||||
<div className="d-flex flex-wrap">
|
||||
{filteredBuckets.map(({ id, name }) => (
|
||||
<div
|
||||
@ -97,7 +111,7 @@ const DirectoryPageHeader = ({
|
||||
onChange={() => handleTempBucketChange(id)}
|
||||
/>
|
||||
<label
|
||||
className="form-check-label text-nowrap small-text "
|
||||
className="form-check-label text-nowrap text-small "
|
||||
htmlFor={`bucket-${id}`}
|
||||
>
|
||||
{name}
|
||||
@ -109,7 +123,7 @@ const DirectoryPageHeader = ({
|
||||
<hr className="m-0" />
|
||||
{/* Category Filter */}
|
||||
<div className="mt-1">
|
||||
<p className="small-text mb-1 fw-semibold">Categories</p>
|
||||
<p className="text-small mb-1 ">Categories</p>
|
||||
<div className="d-flex flex-wrap">
|
||||
{filteredCategories.map(({ id, name }) => (
|
||||
<div
|
||||
@ -125,7 +139,7 @@ const DirectoryPageHeader = ({
|
||||
onChange={() => handleTempCategoryChange(id)}
|
||||
/>
|
||||
<label
|
||||
className="form-check-label text-nowrap small-text"
|
||||
className="form-check-label text-nowrap text-small"
|
||||
htmlFor={`cat-${id}`}
|
||||
>
|
||||
{name}
|
||||
@ -154,49 +168,69 @@ const DirectoryPageHeader = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-md-8 mb-2 px-1 text-md-end text-end">
|
||||
<label className="switch switch-primary">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="switch-input"
|
||||
onChange={() => setIsActive(!IsActive)}
|
||||
value={IsActive}
|
||||
disabled={loading}
|
||||
/>
|
||||
<span className="switch-toggle-slider">
|
||||
<span className="switch-on">
|
||||
{/* <i class="icon-base bx bx-check"></i> */}
|
||||
</span>
|
||||
<span className="switch-off">
|
||||
{/* <i class="icon-base bx bx-x"></i> */}
|
||||
</span>
|
||||
</span>
|
||||
<span className="switch-label small-text">
|
||||
Show Inactive Contacts
|
||||
</span>
|
||||
</label>
|
||||
<div className="col-12 col-md-8 mb-2 px-1 d-flex justify-content-end gap-2 align-items-center text-end">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-xs btn-primary"
|
||||
className="btn btn-sm btn-primary"
|
||||
onClick={() => setIsOpenModal(true)}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
New Contact
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={`dropdown `}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-xs btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0 m-0"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i
|
||||
className="bx bx-dots-vertical-rounded bx-md text-muted "
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-offset="0,8"
|
||||
data-bs-placement="top"
|
||||
data-bs-custom-class="tooltip-dark"
|
||||
title="More Action"
|
||||
></i>
|
||||
</button>
|
||||
<ul className="dropdown-menu dropdown-menu-end w-auto">
|
||||
<li>
|
||||
<a className="dropdown-item px-2 ">
|
||||
<label className="switch switch-primary align-self-start mb-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="switch-input"
|
||||
onChange={() => setIsActive(!IsActive)}
|
||||
value={IsActive}
|
||||
disabled={loading}
|
||||
/>
|
||||
<span className="switch-toggle-slider">
|
||||
<span className="switch-on"></span>
|
||||
<span className="switch-off"></span>
|
||||
</span>
|
||||
<span className=" list-inline-item ">
|
||||
Show Inactive Contacts
|
||||
</span>
|
||||
</label>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="dropdown-item cursor-pointer px-2 "
|
||||
onClick={() => setOpenBucketModal(true)}
|
||||
>
|
||||
<i className="fa-solid fa-bucket fs-5 me-4"></i>
|
||||
<span className="align-left">Manage Buckets</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="col-12 d-flex justify-content-end">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-xs btn-primary"
|
||||
onClick={()=>setOpenBucketModal(true)}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
Manage Buckets
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DirectoryPageHeader;
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
import {api} from "../utils/axiosClient";
|
||||
|
||||
export const DirectoryRepository = {
|
||||
GetOrganizations:()=>api.get('/api/directory/organization'),
|
||||
|
||||
GetContacts: (isActive) => api.get( `/api/directory?active=${isActive}` ),
|
||||
CreateContact: ( data ) => api.post( '/api/directory', data ),
|
||||
UpdateContact: ( id, data ) => api.put( `/api/directory/${ id }`, data ),
|
||||
@ -8,7 +10,8 @@ export const DirectoryRepository = {
|
||||
|
||||
GetBucktes: () => api.get( `/api/directory/buckets` ),
|
||||
CreateBuckets: ( data ) => api.post( `/api/Directory/bucket`, data ),
|
||||
UpdateBuckets: (id,data) => api.put( `/api/Directory/bucket/${id}`,data ),
|
||||
UpdateBuckets: ( id, data ) => api.put( `/api/Directory/bucket/${ id }`, data ),
|
||||
DeleteBucket:(id)=>api.delete(`/api/directory/bucket/${id}`),
|
||||
|
||||
GetContactProfile: ( id ) => api.get( `/api/directory/profile/${ id }` ),
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user