pramod_Task-#390 : Tasks for Bucket Admin #158
@ -1,25 +1,57 @@
|
||||
import React, { useState } from "react";
|
||||
import { useSortableData } from "../../hooks/useSortableData";
|
||||
|
||||
const EmployeeList = ({ employees }) => {
|
||||
const [selectedIds, setSelectedIds] = useState([]);
|
||||
import React,{useState,useEffect} from 'react'
|
||||
import {useSortableData} from '../../hooks/useSortableData';
|
||||
const EmployeeList = ( {employees, onChange, assignedEmployee = []} ) =>
|
||||
{
|
||||
const [employeefiltered, setEmployeeFilter] = useState([]);
|
||||
const [employeeStatusList, setEmployeeStatusList] = useState([]);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
// Populate filtered list on load
|
||||
useEffect(() => {
|
||||
setEmployeeFilter(employees?.filter((emp) => emp.email != null) || []);
|
||||
}, [employees]);
|
||||
|
||||
// Initialize checked employees based on assignedEmployee prop
|
||||
useEffect(() => {
|
||||
if (Array.isArray(assignedEmployee)) {
|
||||
const initialStatus = assignedEmployee.map((id) => ({
|
||||
employeeId: id,
|
||||
isActive: true,
|
||||
}));
|
||||
setEmployeeStatusList(initialStatus);
|
||||
}
|
||||
}, [assignedEmployee]);
|
||||
|
||||
// Send updated list to parent
|
||||
useEffect(() => {
|
||||
if (onChange) {
|
||||
onChange(employeeStatusList);
|
||||
}
|
||||
}, [employeeStatusList]);
|
||||
|
||||
const handleCheckboxChange = (id) => {
|
||||
setSelectedIds((prev) =>
|
||||
prev.includes(id) ? prev?.filter((empId) => empId !== id) : [...prev, id]
|
||||
);
|
||||
setEmployeeStatusList((prev) => {
|
||||
const exists = prev.find((emp) => emp.employeeId === id);
|
||||
if (exists) {
|
||||
return prev.map((emp) =>
|
||||
emp.employeeId === id ? { ...emp, isActive: !emp.isActive } : emp
|
||||
);
|
||||
} else {
|
||||
return [...prev, { employeeId: id, isActive: true }];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getSelectedEmployees = () => {
|
||||
console.log("Selected Employee IDs:", selectedIds);
|
||||
const isChecked = (id) => {
|
||||
const found = employeeStatusList.find((emp) => emp.employeeId === id);
|
||||
return found?.isActive || false;
|
||||
};
|
||||
|
||||
// Sorting
|
||||
const {
|
||||
items: sortedEmployees,
|
||||
requestSort,
|
||||
sortConfig,
|
||||
} = useSortableData(employees, {
|
||||
} = useSortableData(employeefiltered, {
|
||||
key: (e) => `${e?.firstName} ${e?.lastName}`,
|
||||
direction: "asc",
|
||||
});
|
||||
@ -34,21 +66,13 @@ const EmployeeList = ({ employees }) => {
|
||||
};
|
||||
|
||||
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)
|
||||
const fullName = `${employee?.firstName} ${employee?.lastName}`?.toLowerCase();
|
||||
return fullName.includes(searchTerm.toLowerCase());
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="d-flex justify-content-between mt-2">
|
||||
<p className="m-0 fs-6 fw-normal">Add Employee</p>
|
||||
<div className="d-flex justify-content-between align-items-center mt-2">
|
||||
<p className="m-0 fw-normal">Add Employee</p>
|
||||
<div className="px-1">
|
||||
<input
|
||||
type="search"
|
||||
@ -72,27 +96,25 @@ const EmployeeList = ({ employees }) => {
|
||||
>
|
||||
<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>
|
||||
<th className="text-start">Email</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 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 colSpan={4}>
|
||||
<div className="d-flex justify-content-center align-items-center py-5">
|
||||
No Matching Employee Found.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
@ -103,36 +125,18 @@ const EmployeeList = ({ employees }) => {
|
||||
<input
|
||||
className="form-check-input me-3 mt-1"
|
||||
type="checkbox"
|
||||
checked={selectedIds.includes(employee.id)}
|
||||
checked={isChecked(employee.id)}
|
||||
onChange={() => handleCheckboxChange(employee.id)}
|
||||
/>
|
||||
<div>
|
||||
<p className="fw-semibold mb-0">
|
||||
<p className="fw-normal 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>
|
||||
<small className="text-muted">{employee.email}</small>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
|
@ -5,73 +5,69 @@ import { getEmailIcon,getPhoneIcon } from './DirectoryUtils';
|
||||
|
||||
const ListViewDirectory = ({IsActive, contact,setSelectedContact,setIsOpenModal,setOpen_contact,setIsOpenModalNote,IsDeleted}) => {
|
||||
return (
|
||||
<tr style={{background:`${!IsActive ? "#f8f6f6":""}`}} >
|
||||
<td className="text-start cursor-pointer" style={{width: '18%'}} colSpan={2} onClick={() =>
|
||||
{
|
||||
if ( IsActive )
|
||||
{
|
||||
setIsOpenModalNote(true)
|
||||
setOpen_contact(contact)
|
||||
}
|
||||
}}>
|
||||
<div className="d-flex align-items-center">
|
||||
<Avatar
|
||||
size="xs"
|
||||
firstName={(contact?.name || "").trim().split(" ")[0]?.charAt(0) || ""}
|
||||
lastName={(contact?.name || "").trim().split(" ")[1]?.charAt(0) || ""}
|
||||
/>
|
||||
<tr className={!IsActive ? "bg-light" : ""}>
|
||||
<td className="text-start cursor-pointer" style={{ width: "18%" }} colSpan={2} onClick={() => {
|
||||
if (IsActive) {
|
||||
setIsOpenModalNote(true);
|
||||
setOpen_contact(contact);
|
||||
}
|
||||
}}>
|
||||
<div className="d-flex align-items-center gap-2">
|
||||
<Avatar
|
||||
size="xs"
|
||||
classAvatar="m-0"
|
||||
firstName={(contact?.name || "").trim().split(" ")[0]?.charAt(0) || ""}
|
||||
lastName={(contact?.name || "").trim().split(" ")[1]?.charAt(0) || ""}
|
||||
/>
|
||||
<span className="text-truncate mx-0" style={{ maxWidth: "150px" }}>{contact?.name || ""}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<span >{contact?.name || ""}</span>
|
||||
</div>
|
||||
<td className="px-2" style={{ width: "20%" }}>
|
||||
<div className="d-flex flex-column align-items-start text-truncate">
|
||||
{contact.contactEmails?.map((email, index) => (
|
||||
<span key={email.id} className="text-truncate">
|
||||
<i className={getEmailIcon(email.label)} style={{ fontSize: "12px" }}></i>
|
||||
<a href={`mailto:${email.emailAddress}`} className="text-decoration-none ms-1">{email.emailAddress}</a>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-2" style={{ width: "20%" }}>
|
||||
<div className="d-flex flex-column align-items-start text-truncate">
|
||||
{contact.contactPhones?.map((phone, index) => (
|
||||
<span key={phone.id}>
|
||||
<i className={getPhoneIcon(phone.label)} style={{ fontSize: "12px" }}></i>
|
||||
<span className="ms-1">{phone.phoneNumber}</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
</td>
|
||||
<td colSpan={2} className="text-start text-truncate px-2" style={{ width: "20%", maxWidth: "200px" }}>
|
||||
{contact.organization}
|
||||
</td>
|
||||
|
||||
{/* Emails */}
|
||||
<td >
|
||||
<div className="d-flex flex-column align-items-start px-1">
|
||||
{contact.contactEmails?.map((email, index) => (
|
||||
<span key={email.id}>
|
||||
<i className={getEmailIcon(email.label)} style={{fontSize:"12px"}}></i>
|
||||
<a href={`mailto:${email.email}`} className="text-decoration-none">{email.emailAddress}</a>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-2" style={{ width: "10%" }}>
|
||||
<span className="badge badge-outline-secondary">
|
||||
{contact?.contactCategory?.name}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div className="d-flex flex-column align-items-start">
|
||||
{contact.contactPhones?.map((phone, index) => (
|
||||
<span key={phone.id}>
|
||||
<i className={getPhoneIcon(phone.label)} style={{fontSize:"12px"}}></i>
|
||||
{phone.phoneNumber}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
{IsActive && (
|
||||
<td className="align-middle text-center" style={{ width: "12%" }}>
|
||||
<i className="bx bx-edit bx-sm text-primary cursor-pointer me-2"
|
||||
onClick={() => {
|
||||
setSelectedContact(contact);
|
||||
setIsOpenModal(true);
|
||||
}}></i>
|
||||
<i className="bx bx-trash bx-sm text-danger cursor-pointer"
|
||||
onClick={() => IsDeleted(contact.id)}></i>
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
|
||||
|
||||
<td className="text-start text-wrap">{contact.organization}</td>
|
||||
|
||||
|
||||
<td>
|
||||
<div className="d-flex flex-column align-items-start">
|
||||
<span className="badge badge-outline-primary">{contact?.contactCategory?.name }</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{/* Actions */}
|
||||
{IsActive &&
|
||||
<td className="align-middle text-center ">
|
||||
<i className='bx bx-edit bx-sm text-primary cursor-pointer' onClick={() =>
|
||||
{
|
||||
setSelectedContact( contact )
|
||||
setIsOpenModal( true )
|
||||
}}></i>
|
||||
<i className='bx bx-trash bx-sm text-danger cursor-pointer' onClick={() => IsDeleted( contact.id )}></i>
|
||||
</td>
|
||||
}
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -16,7 +16,8 @@ import ConfirmModal from "../common/ConfirmModal";
|
||||
const ManageBucket = () => {
|
||||
const [bucketList, setBucketList] = useState([]);
|
||||
const { employeesList } = useAllEmployees(false);
|
||||
const { buckets, loading,refetch } = useBuckets();
|
||||
const [selectedEmployee, setSelectEmployee] = useState([]);
|
||||
const { buckets, loading, refetch } = useBuckets();
|
||||
const [action_bucket, setAction_bucket] = useState(false);
|
||||
const [isSubmitting, setSubmitting] = useState(false);
|
||||
const [selected_bucket, select_bucket] = useState(null);
|
||||
@ -54,28 +55,88 @@ const ManageBucket = () => {
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
setSubmitting(true);
|
||||
|
||||
try {
|
||||
const cache_buckets = getCachedData("buckets") || [];
|
||||
let response;
|
||||
|
||||
// Utility: Compare arrays regardless of order
|
||||
const arraysAreEqual = (a, b) => {
|
||||
if (a.length !== b.length) return false;
|
||||
const setA = new Set(a);
|
||||
const setB = new Set(b);
|
||||
return [...setA].every((id) => setB.has(id));
|
||||
};
|
||||
|
||||
// UPDATE existing bucket
|
||||
if (selected_bucket) {
|
||||
let payload = { ...data, id: selected_bucket.id };
|
||||
const payload = { ...data, id: selected_bucket.id };
|
||||
|
||||
// 1. Update bucket details
|
||||
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);
|
||||
|
||||
// 2. Update employee assignments if they changed
|
||||
const existingEmployeeIds = selected_bucket?.employeeIds || [];
|
||||
const employeesToUpdate = selectedEmployee.filter((emp) => {
|
||||
const isExisting = existingEmployeeIds.includes(emp.employeeId);
|
||||
return (!isExisting && emp.isActive) || (isExisting && !emp.isActive);
|
||||
});
|
||||
|
||||
// Create a filtered list of active employee IDs to compare
|
||||
const newActiveEmployeeIds = selectedEmployee
|
||||
.filter((emp) => {
|
||||
const isExisting = existingEmployeeIds.includes(emp.employeeId);
|
||||
return (
|
||||
(!isExisting && emp.isActive) || (isExisting && !emp.isActive)
|
||||
);
|
||||
})
|
||||
.map((emp) => emp.employeeId);
|
||||
|
||||
if (
|
||||
!arraysAreEqual(newActiveEmployeeIds, existingEmployeeIds) &&
|
||||
employeesToUpdate.length != 0
|
||||
) {
|
||||
try {
|
||||
response = await DirectoryRepository.AssignedBuckets(
|
||||
selected_bucket.id,
|
||||
employeesToUpdate
|
||||
);
|
||||
} catch (assignError) {
|
||||
const assignMessage =
|
||||
assignError?.response?.data?.message ||
|
||||
assignError?.message ||
|
||||
"Error assigning employees.";
|
||||
showToast(assignMessage, "error");
|
||||
}
|
||||
}
|
||||
const updatedData = cache_buckets?.map((bucket) =>
|
||||
bucket.id === response?.data?.id ? response.data : bucket
|
||||
);
|
||||
|
||||
cacheData("buckets", updatedData);
|
||||
|
||||
setBucketList(updatedData);
|
||||
showToast("Bucket Updated Successfully", "success");
|
||||
} else {
|
||||
}
|
||||
|
||||
// CREATE new bucket
|
||||
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");
|
||||
}
|
||||
|
||||
@ -86,18 +147,20 @@ const ManageBucket = () => {
|
||||
error?.message ||
|
||||
"Error occurred during API call";
|
||||
showToast(message, "error");
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
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) {
|
||||
@ -131,7 +194,6 @@ const ManageBucket = () => {
|
||||
const name = bucket.name?.toLowerCase();
|
||||
return name?.includes(term);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{deleteBucket && (
|
||||
@ -173,19 +235,20 @@ const ManageBucket = () => {
|
||||
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()}
|
||||
/>
|
||||
/>
|
||||
<i
|
||||
className={`bx bx-refresh cursor-pointer fs-4 ${
|
||||
loading ? "spin" : ""
|
||||
}`}
|
||||
title="Refresh"
|
||||
onClick={() => refetch()}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-sm btn-primary ms-auto ${
|
||||
className={`btn btn-xs btn-primary ms-auto ${
|
||||
action_bucket ? "d-none" : ""
|
||||
}`}
|
||||
onClick={() => setAction_bucket(true)}
|
||||
@ -214,6 +277,7 @@ const ManageBucket = () => {
|
||||
<span>Description</span>
|
||||
</div>
|
||||
</th>
|
||||
<th>Contacts</th>
|
||||
<th>
|
||||
<div className="d-flex align-items-center justify-content-center gap-1">
|
||||
<span>Action</span>
|
||||
@ -251,36 +315,43 @@ const ManageBucket = () => {
|
||||
</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>
|
||||
))}
|
||||
{!loading &&
|
||||
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-truncate"
|
||||
style={{
|
||||
maxWidth: "300px",
|
||||
whiteSpace: "wrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
title={bucket.description}
|
||||
>
|
||||
{bucket.description}
|
||||
</td>
|
||||
<td>{bucket.numberOfContacts}</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>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@ -310,9 +381,18 @@ const ManageBucket = () => {
|
||||
</small>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{selected_bucket && (
|
||||
<EmployeeList
|
||||
employees={employeesList}
|
||||
onChange={(data) => setSelectEmployee(data)}
|
||||
assignedEmployee={selected_bucket?.employeeIds}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="mt-2 d-flex justify-content-center gap-3">
|
||||
<button
|
||||
type="reset"
|
||||
onClick={() => handleBack()}
|
||||
className="btn btn-sm btn-secondary"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
@ -327,8 +407,6 @@ const ManageBucket = () => {
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<EmployeeList employees={employeesList} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
@ -10,18 +10,16 @@ import moment from "moment";
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
import NoteCardDirectory from "./NoteCardDirectory";
|
||||
import showToast from "../../services/toastService";
|
||||
import {useContactNotes} from "../../hooks/useDirectory";
|
||||
import { useContactNotes } from "../../hooks/useDirectory";
|
||||
|
||||
const schema = z.object({
|
||||
note: z.string().min(1, { message: "Note is required" }),
|
||||
});
|
||||
|
||||
const NotesDirectory = ( {isLoading, contactProfile, setProfileContact} ) =>
|
||||
{
|
||||
const [ IsActive, setIsActive ] = useState( true )
|
||||
const {contactNotes} = useContactNotes(contactProfile?.id,!IsActive)
|
||||
const NotesDirectory = ({ isLoading, contactProfile, setProfileContact }) => {
|
||||
const [IsActive, setIsActive] = useState(true);
|
||||
const { contactNotes } = useContactNotes(contactProfile?.id, !IsActive);
|
||||
|
||||
|
||||
const [NotesData, setNotesData] = useState();
|
||||
const [IsSubmitting, setIsSubmitting] = useState(false);
|
||||
const [addNote, setAddNote] = useState(false);
|
||||
@ -72,8 +70,8 @@ const NotesDirectory = ( {isLoading, contactProfile, setProfileContact} ) =>
|
||||
setValue("note", "");
|
||||
setIsSubmitting(false);
|
||||
showToast("Note added successfully!", "success");
|
||||
setAddNote( false );
|
||||
setIsActive(true)
|
||||
setAddNote(false);
|
||||
setIsActive(true);
|
||||
} catch (error) {
|
||||
setIsSubmitting(false);
|
||||
const msg =
|
||||
@ -93,31 +91,35 @@ const NotesDirectory = ( {isLoading, contactProfile, setProfileContact} ) =>
|
||||
<div className="d-flex align-items-center justify-content-between">
|
||||
<p className="fw-semibold m-0">Notes :</p>
|
||||
<div className="m-0 d-flex aligin-items-center">
|
||||
|
||||
<label className="switch switch-primary">
|
||||
<input type="checkbox" className="switch-input" onChange={() => setIsActive( !IsActive )} value={IsActive} />
|
||||
<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>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="switch-input"
|
||||
onChange={() => setIsActive(!IsActive)}
|
||||
value={IsActive}
|
||||
/>
|
||||
<span className="switch-toggle-slider">
|
||||
<span className="switch-on">
|
||||
{/* <i class="icon-base bx bx-check"></i> */}
|
||||
</span>
|
||||
<span className="switch-label small-text">Show Inactive Notes</span>
|
||||
</label>
|
||||
<span
|
||||
className={`btn btn-xs ${addNote ? "btn-danger" : "btn-primary"}`}
|
||||
onClick={() => setAddNote( !addNote )}
|
||||
>
|
||||
{/* <i
|
||||
<span className="switch-off">
|
||||
{/* <i class="icon-base bx bx-x"></i> */}
|
||||
</span>
|
||||
</span>
|
||||
<span className="switch-label small-text">Show Inactive Notes</span>
|
||||
</label>
|
||||
<span
|
||||
className={`btn btn-xs ${addNote ? "btn-danger" : "btn-primary"}`}
|
||||
onClick={() => setAddNote(!addNote)}
|
||||
>
|
||||
{/* <i
|
||||
className={`icon-base bx ${
|
||||
addNote ? "bx-x bx-sm" : "bx-pencil"
|
||||
} bx-xs `}
|
||||
></i> */}
|
||||
{addNote ? "close" : "Add Note"}
|
||||
</span>
|
||||
</div>
|
||||
{addNote ? "close" : "Add Note"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{addNote && (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
@ -141,7 +143,7 @@ const NotesDirectory = ( {isLoading, contactProfile, setProfileContact} ) =>
|
||||
</div>
|
||||
)}
|
||||
{!isLoading &&
|
||||
[...(IsActive ? contactProfile?.notes || [] : contactNotes || [])]
|
||||
[...(IsActive ? contactProfile?.notes || [] : contactNotes || [])]
|
||||
.reverse()
|
||||
.map((noteItem) => (
|
||||
<NoteCardDirectory
|
||||
@ -151,11 +153,20 @@ const NotesDirectory = ( {isLoading, contactProfile, setProfileContact} ) =>
|
||||
setProfileContact={setProfileContact}
|
||||
key={noteItem.id}
|
||||
/>
|
||||
) )}
|
||||
{IsActive && ( <p>{!isLoading && contactProfile?.notes.length == 0 && !addNote && ( <p className="text-center">No Notes Found</p> )}</p> )}
|
||||
))}
|
||||
{IsActive && (
|
||||
<p>
|
||||
{!isLoading && contactProfile?.notes.length == 0 && !addNote && (
|
||||
<p className="text-center">No Notes Found</p>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
{!IsActive && (
|
||||
<p>{!isLoading && contactNotes.length == 0 && !addNote && (<p className="text-center">No Notes Found</p>) }</p>
|
||||
|
||||
<p>
|
||||
{!isLoading && contactNotes.length == 0 && !addNote && (
|
||||
<p className="text-center">No Notes Found</p>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
</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";
|
||||
|
||||
const UpdateContact = ({ submitContact, existingContact, onCLosed }) => {
|
||||
const selectedMaster = useSelector(
|
||||
@ -34,6 +35,7 @@ const UpdateContact = ({ submitContact, existingContact, onCLosed }) => {
|
||||
const [ IsSubmitting, setSubmitting ] = useState( false );
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const {organizationList} = useOrganization()
|
||||
|
||||
const methods = useForm({
|
||||
resolver: zodResolver(ContactSchema),
|
||||
@ -137,7 +139,7 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
setSubmitting(false);
|
||||
|
||||
};
|
||||
|
||||
const orgValue = watch("organization")
|
||||
const handleClosed = () => {
|
||||
onCLosed();
|
||||
};
|
||||
@ -193,12 +195,15 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
<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}
|
||||
|
@ -43,7 +43,6 @@ const Teams = ({ project }) => {
|
||||
.then((response) => {
|
||||
setEmployees(response.data);
|
||||
setFilteredEmployees( response.data.filter( ( emp ) => emp.isActive ) );
|
||||
console.log(response)
|
||||
setEmployeeLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
|
@ -10,7 +10,7 @@ function hashString(str) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
const Avatar = ({ firstName, lastName, size = "sm" }) => {
|
||||
const Avatar = ({ firstName, lastName, size = "sm", classAvatar }) => {
|
||||
// Combine firstName and lastName to create a unique string for hashing
|
||||
const fullName = `${firstName} ${lastName}`;
|
||||
|
||||
@ -51,7 +51,7 @@ const Avatar = ({ firstName, lastName, size = "sm" }) => {
|
||||
|
||||
return (
|
||||
<div className="avatar-wrapper p-1">
|
||||
<div className={`avatar avatar-${size} me-2`}>
|
||||
<div className={`avatar avatar-${size} me-2 ${classAvatar}`}>
|
||||
<span className={`avatar-initial rounded-circle ${bgClass}`}>
|
||||
{generateAvatarText(firstName, lastName)}
|
||||
</span>
|
||||
|
@ -24,11 +24,11 @@ const DirectoryListTableHeader = ( {children, IsActive} ) =>
|
||||
<span>Phone</span>
|
||||
</div>
|
||||
</th>
|
||||
<th className="mx-2">
|
||||
<div className="d-flex align-items-center gap-1">
|
||||
<th colSpan={2} className="mx-2 ps-20">
|
||||
|
||||
|
||||
<span>Organization</span>
|
||||
</div>
|
||||
Organization
|
||||
|
||||
</th>
|
||||
<th className="mx-2">Category</th>
|
||||
{IsActive && <th>Action</th>}
|
||||
|
@ -6,7 +6,8 @@ export const DirectoryRepository = {
|
||||
GetContacts: (isActive) => api.get( `/api/directory?active=${isActive}` ),
|
||||
CreateContact: ( data ) => api.post( '/api/directory', data ),
|
||||
UpdateContact: ( id, data ) => api.put( `/api/directory/${ id }`, data ),
|
||||
DeleteContact:(id)=>api.delete(`/api/directory/${id}`),
|
||||
DeleteContact: ( id ) => api.delete( `/api/directory/${ id }` ),
|
||||
AssignedBuckets:(id,data)=>api.post(`/api/directory/assign-bucket/${id}`,data),
|
||||
|
||||
GetBucktes: () => api.get( `/api/directory/buckets` ),
|
||||
CreateBuckets: ( data ) => api.post( `/api/Directory/bucket`, data ),
|
||||
@ -16,7 +17,7 @@ export const DirectoryRepository = {
|
||||
GetContactProfile: ( id ) => api.get( `/api/directory/profile/${ id }` ),
|
||||
|
||||
CreateNote: ( data ) => api.post( '/api/directory/note', data ),
|
||||
GetNote: ( id,isActive ) => api.get( `/api/directory/note/${ id }?active=${isActive}` ),
|
||||
GetNote: ( id,isActive ) => api.get( `/api/directory/notes/${ id }?active=${isActive}` ),
|
||||
UpdateNote: ( id, data ) => api.put( `/api/directory/note/${ id }`, data ),
|
||||
DeleteNote:(id)=> api.delete(`/api/directory/note/${ id }`)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user