Merge pull request 'pramod_Task-#390 : Tasks for Bucket Admin' (#158) from pramod_Task-#390 into Feature_Directory

Reviewed-on: #158
This commit is contained in:
pramod.mahajan 2025-05-28 13:46:15 +00:00
commit 8c43d07488
9 changed files with 311 additions and 217 deletions

View File

@ -1,25 +1,57 @@
import React, { useState } from "react"; import React,{useState,useEffect} from 'react'
import { useSortableData } from "../../hooks/useSortableData"; import {useSortableData} from '../../hooks/useSortableData';
const EmployeeList = ( {employees, onChange, assignedEmployee = []} ) =>
const EmployeeList = ({ employees }) => { {
const [selectedIds, setSelectedIds] = useState([]); const [employeefiltered, setEmployeeFilter] = useState([]);
const [employeeStatusList, setEmployeeStatusList] = useState([]);
const [searchTerm, setSearchTerm] = 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) => { const handleCheckboxChange = (id) => {
setSelectedIds((prev) => setEmployeeStatusList((prev) => {
prev.includes(id) ? prev?.filter((empId) => empId !== id) : [...prev, id] 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 = () => { const isChecked = (id) => {
console.log("Selected Employee IDs:", selectedIds); const found = employeeStatusList.find((emp) => emp.employeeId === id);
return found?.isActive || false;
}; };
// Sorting
const { const {
items: sortedEmployees, items: sortedEmployees,
requestSort, requestSort,
sortConfig, sortConfig,
} = useSortableData(employees, { } = useSortableData(employeefiltered, {
key: (e) => `${e?.firstName} ${e?.lastName}`, key: (e) => `${e?.firstName} ${e?.lastName}`,
direction: "asc", direction: "asc",
}); });
@ -34,21 +66,13 @@ const EmployeeList = ({ employees }) => {
}; };
const filteredEmployees = sortedEmployees?.filter((employee) => { const filteredEmployees = sortedEmployees?.filter((employee) => {
const fullName = const fullName = `${employee?.firstName} ${employee?.lastName}`?.toLowerCase();
`${employee?.firstName} ${employee?.lastName}`?.toLowerCase(); return fullName.includes(searchTerm.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 ( return (
<> <>
<div className="d-flex justify-content-between mt-2"> <div className="d-flex justify-content-between align-items-center mt-2">
<p className="m-0 fs-6 fw-normal">Add Employee</p> <p className="m-0 fw-normal">Add Employee</p>
<div className="px-1"> <div className="px-1">
<input <input
type="search" type="search"
@ -72,27 +96,25 @@ const EmployeeList = ({ employees }) => {
> >
<span className="ps-2">Name {getSortIcon()}</span> <span className="ps-2">Name {getSortIcon()}</span>
</th> </th>
<th className="text-start">Role</th> <th className="text-start">Email</th>
<th scope="col">Status</th>
<th className="text-start">Bucket</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{employees.length === 0 ? ( {employees.length === 0 ? (
<tr> <tr>
<td colSpan={4} > <td colSpan={4}>
<div className="d-flex justify-content-center align-items-center py-5"> <div className="d-flex justify-content-center align-items-center py-5">
No Employee Available No Employee Available
</div> </div>
</td> </td>
</tr> </tr>
) : filteredEmployees.length === 0 ? ( ) : filteredEmployees.length === 0 ? (
<tr className="my-4"> <tr className="my-4">
<td colSpan={4}> <td colSpan={4}>
<div className="d-flex justify-content-center align-items-center py-5"> <div className="d-flex justify-content-center align-items-center py-5">
No Matchinng Employee Found. No Matching Employee Found.
</div> </div>
</td> </td>
</tr> </tr>
) : ( ) : (
@ -103,36 +125,18 @@ const EmployeeList = ({ employees }) => {
<input <input
className="form-check-input me-3 mt-1" className="form-check-input me-3 mt-1"
type="checkbox" type="checkbox"
checked={selectedIds.includes(employee.id)} checked={isChecked(employee.id)}
onChange={() => handleCheckboxChange(employee.id)} onChange={() => handleCheckboxChange(employee.id)}
/> />
<div> <div>
<p className="fw-semibold mb-0"> <p className="fw-normal mb-0">
{`${employee.firstName} ${employee.lastName}`} {`${employee.firstName} ${employee.lastName}`}
</p> </p>
<small className="text-muted">{employee.email}</small>
</div> </div>
</div> </div>
</td> </td>
<td className="text-start"> <td className="text-start">
<small className="text-muted">{employee.jobRole}</small> <small className="text-muted">{employee.email}</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> </td>
</tr> </tr>
)) ))

View File

@ -5,73 +5,69 @@ import { getEmailIcon,getPhoneIcon } from './DirectoryUtils';
const ListViewDirectory = ({IsActive, contact,setSelectedContact,setIsOpenModal,setOpen_contact,setIsOpenModalNote,IsDeleted}) => { const ListViewDirectory = ({IsActive, contact,setSelectedContact,setIsOpenModal,setOpen_contact,setIsOpenModalNote,IsDeleted}) => {
return ( return (
<tr style={{background:`${!IsActive ? "#f8f6f6":""}`}} > <tr className={!IsActive ? "bg-light" : ""}>
<td className="text-start cursor-pointer" style={{width: '18%'}} colSpan={2} onClick={() => <td className="text-start cursor-pointer" style={{ width: "18%" }} colSpan={2} onClick={() => {
{ if (IsActive) {
if ( IsActive ) setIsOpenModalNote(true);
{ setOpen_contact(contact);
setIsOpenModalNote(true) }
setOpen_contact(contact) }}>
} <div className="d-flex align-items-center gap-2">
}}> <Avatar
<div className="d-flex align-items-center"> size="xs"
<Avatar classAvatar="m-0"
size="xs" firstName={(contact?.name || "").trim().split(" ")[0]?.charAt(0) || ""}
firstName={(contact?.name || "").trim().split(" ")[0]?.charAt(0) || ""} lastName={(contact?.name || "").trim().split(" ")[1]?.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> <td className="px-2" style={{ width: "20%" }}>
</div> <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 className="px-2" style={{ width: "10%" }}>
<td > <span className="badge badge-outline-secondary">
<div className="d-flex flex-column align-items-start px-1"> {contact?.contactCategory?.name}
{contact.contactEmails?.map((email, index) => ( </span>
<span key={email.id}> </td>
<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> {IsActive && (
<div className="d-flex flex-column align-items-start"> <td className="align-middle text-center" style={{ width: "12%" }}>
{contact.contactPhones?.map((phone, index) => ( <i className="bx bx-edit bx-sm text-primary cursor-pointer me-2"
<span key={phone.id}> onClick={() => {
<i className={getPhoneIcon(phone.label)} style={{fontSize:"12px"}}></i> setSelectedContact(contact);
{phone.phoneNumber} setIsOpenModal(true);
</span> }}></i>
))} <i className="bx bx-trash bx-sm text-danger cursor-pointer"
</div> onClick={() => IsDeleted(contact.id)}></i>
</td> </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>
); );
}; };

View File

@ -16,7 +16,8 @@ import ConfirmModal from "../common/ConfirmModal";
const ManageBucket = () => { const ManageBucket = () => {
const [bucketList, setBucketList] = useState([]); const [bucketList, setBucketList] = useState([]);
const { employeesList } = useAllEmployees(false); 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 [action_bucket, setAction_bucket] = useState(false);
const [isSubmitting, setSubmitting] = useState(false); const [isSubmitting, setSubmitting] = useState(false);
const [selected_bucket, select_bucket] = useState(null); const [selected_bucket, select_bucket] = useState(null);
@ -54,28 +55,88 @@ const ManageBucket = () => {
const onSubmit = async (data) => { const onSubmit = async (data) => {
setSubmitting(true); setSubmitting(true);
try { try {
const cache_buckets = getCachedData("buckets") || [];
let response; 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) { 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( response = await DirectoryRepository.UpdateBuckets(
selected_bucket.id, selected_bucket.id,
payload payload
); );
const cache_buckets = getCachedData("buckets") || [];
const updatedBuckets = cache_buckets.map((bucket) => const updatedBuckets = cache_buckets.map((bucket) =>
bucket.id === selected_bucket.id ? response?.data : bucket bucket.id === selected_bucket.id ? response?.data : bucket
); );
cacheData("buckets", updatedBuckets); cacheData("buckets", updatedBuckets);
setBucketList(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"); showToast("Bucket Updated Successfully", "success");
} else { }
// CREATE new bucket
else {
response = await DirectoryRepository.CreateBuckets(data); response = await DirectoryRepository.CreateBuckets(data);
const cache_buckets = getCachedData("buckets") || [];
const updatedBuckets = [...cache_buckets, response?.data]; const updatedBuckets = [...cache_buckets, response?.data];
cacheData("buckets", updatedBuckets); cacheData("buckets", updatedBuckets);
setBucketList(updatedBuckets); setBucketList(updatedBuckets);
showToast("Bucket Created Successfully", "success"); showToast("Bucket Created Successfully", "success");
} }
@ -86,18 +147,20 @@ const ManageBucket = () => {
error?.message || error?.message ||
"Error occurred during API call"; "Error occurred during API call";
showToast(message, "error"); showToast(message, "error");
} finally {
setSubmitting(false);
} }
}; };
const handleDeleteContact = async () => { const handleDeleteContact = async () => {
try { try {
const resp = await DirectoryRepository.DeleteBucket( deleteBucket ); const resp = await DirectoryRepository.DeleteBucket(deleteBucket);
const cache_buckets = getCachedData("buckets") || []; const cache_buckets = getCachedData("buckets") || [];
const updatedBuckets = cache_buckets.filter((bucket) => const updatedBuckets = cache_buckets.filter(
bucket.id != deleteBucket (bucket) => bucket.id != deleteBucket
); );
cacheData("buckets", updatedBuckets); cacheData("buckets", updatedBuckets);
setBucketList(updatedBuckets); setBucketList(updatedBuckets);
showToast("Bucket deleted successfully", "success"); showToast("Bucket deleted successfully", "success");
setDeleteBucket(null); setDeleteBucket(null);
} catch (error) { } catch (error) {
@ -131,7 +194,6 @@ const ManageBucket = () => {
const name = bucket.name?.toLowerCase(); const name = bucket.name?.toLowerCase();
return name?.includes(term); return name?.includes(term);
}); });
return ( return (
<> <>
{deleteBucket && ( {deleteBucket && (
@ -173,19 +235,20 @@ const ManageBucket = () => {
placeholder="Search Bucket ..." placeholder="Search Bucket ..."
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
/> />
<i <i
className={`bx bx-refresh cursor-pointer fs-4 ${loading ? "spin" : "" className={`bx bx-refresh cursor-pointer fs-4 ${
}`} loading ? "spin" : ""
title="Refresh" }`}
onClick={() => rrefetch()} title="Refresh"
/> onClick={() => refetch()}
/>
</div> </div>
)} )}
<button <button
type="button" type="button"
className={`btn btn-sm btn-primary ms-auto ${ className={`btn btn-xs btn-primary ms-auto ${
action_bucket ? "d-none" : "" action_bucket ? "d-none" : ""
}`} }`}
onClick={() => setAction_bucket(true)} onClick={() => setAction_bucket(true)}
@ -214,6 +277,7 @@ const ManageBucket = () => {
<span>Description</span> <span>Description</span>
</div> </div>
</th> </th>
<th>Contacts</th>
<th> <th>
<div className="d-flex align-items-center justify-content-center gap-1"> <div className="d-flex align-items-center justify-content-center gap-1">
<span>Action</span> <span>Action</span>
@ -251,36 +315,43 @@ const ManageBucket = () => {
</td> </td>
</tr> </tr>
)} )}
{sortedBucktesList.map((bucket) => ( {!loading &&
<tr key={bucket.id}> sortedBucktesList.map((bucket) => (
<td colSpan={2} className="text-start text-wrap"> <tr key={bucket.id}>
<i className="bx bx-right-arrow-alt me-1"></i>{" "} <td colSpan={2} className="text-start text-wrap">
{bucket.name} <i className="bx bx-right-arrow-alt me-1"></i>{" "}
</td> {bucket.name}
<td </td>
className="text-start d-none d-sm-table-cell text-wrap" <td
style={{ width: "60%" }} className="text-start d-none d-sm-table-cell text-truncate"
> style={{
{bucket.description} maxWidth: "300px",
</td> whiteSpace: "wrap",
<td className="justify-content-center"> overflow: "hidden",
<div className="d-flex justify-content-center align-items-center gap-2"> textOverflow: "ellipsis",
<i }}
className="bx bx-edit bx-sm text-primary cursor-pointer" title={bucket.description}
onClick={() => { >
select_bucket(bucket); {bucket.description}
setAction_bucket(true); </td>
}} <td>{bucket.numberOfContacts}</td>
></i> <td className="justify-content-center">
<i <div className="d-flex justify-content-center align-items-center gap-2">
className="bx bx-trash bx-sm text-danger cursor-pointer" <i
onClick={() => setDeleteBucket(bucket?.id)} className="bx bx-edit bx-sm text-primary cursor-pointer"
></i> onClick={() => {
<i className="bx bx-user-plus cursor-pointer"></i> select_bucket(bucket);
</div> setAction_bucket(true);
</td> }}
</tr> ></i>
))} <i
className="bx bx-trash bx-sm text-danger cursor-pointer"
onClick={() => setDeleteBucket(bucket?.id)}
></i>
</div>
</td>
</tr>
))}
</tbody> </tbody>
</table> </table>
</div> </div>
@ -310,9 +381,18 @@ const ManageBucket = () => {
</small> </small>
)} )}
</div> </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"> <div className="mt-2 d-flex justify-content-center gap-3">
<button <button
type="reset" onClick={() => handleBack()}
className="btn btn-sm btn-secondary" className="btn btn-sm btn-secondary"
disabled={isSubmitting} disabled={isSubmitting}
> >
@ -327,8 +407,6 @@ const ManageBucket = () => {
</button> </button>
</div> </div>
</form> </form>
<EmployeeList employees={employeesList} />
</> </>
)} )}
</div> </div>

View File

@ -10,18 +10,16 @@ import moment from "moment";
import { cacheData, getCachedData } from "../../slices/apiDataManager"; import { cacheData, getCachedData } from "../../slices/apiDataManager";
import NoteCardDirectory from "./NoteCardDirectory"; import NoteCardDirectory from "./NoteCardDirectory";
import showToast from "../../services/toastService"; import showToast from "../../services/toastService";
import {useContactNotes} from "../../hooks/useDirectory"; import { useContactNotes } from "../../hooks/useDirectory";
const schema = z.object({ const schema = z.object({
note: z.string().min(1, { message: "Note is required" }), note: z.string().min(1, { message: "Note is required" }),
}); });
const NotesDirectory = ( {isLoading, contactProfile, setProfileContact} ) => const NotesDirectory = ({ isLoading, contactProfile, setProfileContact }) => {
{ const [IsActive, setIsActive] = useState(true);
const [ IsActive, setIsActive ] = useState( true ) const { contactNotes } = useContactNotes(contactProfile?.id, !IsActive);
const {contactNotes} = useContactNotes(contactProfile?.id,!IsActive)
const [NotesData, setNotesData] = useState(); const [NotesData, setNotesData] = useState();
const [IsSubmitting, setIsSubmitting] = useState(false); const [IsSubmitting, setIsSubmitting] = useState(false);
const [addNote, setAddNote] = useState(false); const [addNote, setAddNote] = useState(false);
@ -72,8 +70,8 @@ const NotesDirectory = ( {isLoading, contactProfile, setProfileContact} ) =>
setValue("note", ""); setValue("note", "");
setIsSubmitting(false); setIsSubmitting(false);
showToast("Note added successfully!", "success"); showToast("Note added successfully!", "success");
setAddNote( false ); setAddNote(false);
setIsActive(true) setIsActive(true);
} catch (error) { } catch (error) {
setIsSubmitting(false); setIsSubmitting(false);
const msg = const msg =
@ -93,31 +91,35 @@ const NotesDirectory = ( {isLoading, contactProfile, setProfileContact} ) =>
<div className="d-flex align-items-center justify-content-between"> <div className="d-flex align-items-center justify-content-between">
<p className="fw-semibold m-0">Notes :</p> <p className="fw-semibold m-0">Notes :</p>
<div className="m-0 d-flex aligin-items-center"> <div className="m-0 d-flex aligin-items-center">
<label className="switch switch-primary"> <label className="switch switch-primary">
<input type="checkbox" className="switch-input" onChange={() => setIsActive( !IsActive )} value={IsActive} /> <input
<span className="switch-toggle-slider"> type="checkbox"
<span className="switch-on"> className="switch-input"
{/* <i class="icon-base bx bx-check"></i> */} onChange={() => setIsActive(!IsActive)}
</span> value={IsActive}
<span className="switch-off"> />
{/* <i class="icon-base bx bx-x"></i> */} <span className="switch-toggle-slider">
</span> <span className="switch-on">
{/* <i class="icon-base bx bx-check"></i> */}
</span> </span>
<span className="switch-label small-text">Show Inactive Notes</span> <span className="switch-off">
</label> {/* <i class="icon-base bx bx-x"></i> */}
<span </span>
className={`btn btn-xs ${addNote ? "btn-danger" : "btn-primary"}`} </span>
onClick={() => setAddNote( !addNote )} <span className="switch-label small-text">Show Inactive Notes</span>
> </label>
{/* <i <span
className={`btn btn-xs ${addNote ? "btn-danger" : "btn-primary"}`}
onClick={() => setAddNote(!addNote)}
>
{/* <i
className={`icon-base bx ${ className={`icon-base bx ${
addNote ? "bx-x bx-sm" : "bx-pencil" addNote ? "bx-x bx-sm" : "bx-pencil"
} bx-xs `} } bx-xs `}
></i> */} ></i> */}
{addNote ? "close" : "Add Note"} {addNote ? "close" : "Add Note"}
</span> </span>
</div> </div>
</div> </div>
{addNote && ( {addNote && (
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
@ -141,7 +143,7 @@ const NotesDirectory = ( {isLoading, contactProfile, setProfileContact} ) =>
</div> </div>
)} )}
{!isLoading && {!isLoading &&
[...(IsActive ? contactProfile?.notes || [] : contactNotes || [])] [...(IsActive ? contactProfile?.notes || [] : contactNotes || [])]
.reverse() .reverse()
.map((noteItem) => ( .map((noteItem) => (
<NoteCardDirectory <NoteCardDirectory
@ -151,11 +153,20 @@ const NotesDirectory = ( {isLoading, contactProfile, setProfileContact} ) =>
setProfileContact={setProfileContact} setProfileContact={setProfileContact}
key={noteItem.id} 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 && ( {!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>
</div> </div>

View File

@ -14,10 +14,11 @@ import useMaster, {
} from "../../hooks/masterHook/useMaster"; } from "../../hooks/masterHook/useMaster";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { changeMaster } from "../../slices/localVariablesSlice"; import { changeMaster } from "../../slices/localVariablesSlice";
import { useBuckets } from "../../hooks/useDirectory"; import { useBuckets, useOrganization } from "../../hooks/useDirectory";
import { useProjects } from "../../hooks/useProjects"; import { useProjects } from "../../hooks/useProjects";
import SelectMultiple from "../common/SelectMultiple"; import SelectMultiple from "../common/SelectMultiple";
import { ContactSchema } from "./DirectorySchema"; import { ContactSchema } from "./DirectorySchema";
import InputSuggestions from "../common/InputSuggestion";
const UpdateContact = ({ submitContact, existingContact, onCLosed }) => { const UpdateContact = ({ submitContact, existingContact, onCLosed }) => {
const selectedMaster = useSelector( const selectedMaster = useSelector(
@ -34,6 +35,7 @@ const UpdateContact = ({ submitContact, existingContact, onCLosed }) => {
const [ IsSubmitting, setSubmitting ] = useState( false ); const [ IsSubmitting, setSubmitting ] = useState( false );
const [isInitialized, setIsInitialized] = useState(false); const [isInitialized, setIsInitialized] = useState(false);
const dispatch = useDispatch(); const dispatch = useDispatch();
const {organizationList} = useOrganization()
const methods = useForm({ const methods = useForm({
resolver: zodResolver(ContactSchema), resolver: zodResolver(ContactSchema),
@ -137,7 +139,7 @@ await submitContact({ ...cleaned, id: existingContact.id });
setSubmitting(false); setSubmitting(false);
}; };
const orgValue = watch("organization")
const handleClosed = () => { const handleClosed = () => {
onCLosed(); onCLosed();
}; };
@ -193,12 +195,15 @@ await submitContact({ ...cleaned, id: existingContact.id });
)} )}
</div> </div>
<div className="col-md-6 text-start"> <div className="col-md-6 text-start">
<label className="form-label">Organization</label> <label className="form-label">Organization</label>
<input <InputSuggestions
className="form-control form-control-sm" organizationList={organizationList}
{...register("organization")} value={getValues("organization") || ""}
/> onChange={(val) => setValue("organization", val)}
error={errors.organization?.message}
/>
{errors.organization && ( {errors.organization && (
<small className="danger-text"> <small className="danger-text">
{errors.organization.message} {errors.organization.message}

View File

@ -43,7 +43,6 @@ const Teams = ({ project }) => {
.then((response) => { .then((response) => {
setEmployees(response.data); setEmployees(response.data);
setFilteredEmployees( response.data.filter( ( emp ) => emp.isActive ) ); setFilteredEmployees( response.data.filter( ( emp ) => emp.isActive ) );
console.log(response)
setEmployeeLoading(false); setEmployeeLoading(false);
}) })
.catch((error) => { .catch((error) => {

View File

@ -10,7 +10,7 @@ function hashString(str) {
return hash; 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 // Combine firstName and lastName to create a unique string for hashing
const fullName = `${firstName} ${lastName}`; const fullName = `${firstName} ${lastName}`;
@ -51,7 +51,7 @@ const Avatar = ({ firstName, lastName, size = "sm" }) => {
return ( return (
<div className="avatar-wrapper p-1"> <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}`}> <span className={`avatar-initial rounded-circle ${bgClass}`}>
{generateAvatarText(firstName, lastName)} {generateAvatarText(firstName, lastName)}
</span> </span>

View File

@ -24,11 +24,11 @@ const DirectoryListTableHeader = ( {children, IsActive} ) =>
<span>Phone</span> <span>Phone</span>
</div> </div>
</th> </th>
<th className="mx-2"> <th colSpan={2} className="mx-2 ps-20">
<div className="d-flex align-items-center gap-1">
<span>Organization</span> Organization
</div>
</th> </th>
<th className="mx-2">Category</th> <th className="mx-2">Category</th>
{IsActive && <th>Action</th>} {IsActive && <th>Action</th>}

View File

@ -6,7 +6,8 @@ export const DirectoryRepository = {
GetContacts: (isActive) => api.get( `/api/directory?active=${isActive}` ), GetContacts: (isActive) => api.get( `/api/directory?active=${isActive}` ),
CreateContact: ( data ) => api.post( '/api/directory', data ), CreateContact: ( data ) => api.post( '/api/directory', data ),
UpdateContact: ( id, data ) => api.put( `/api/directory/${ id }`, 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` ), GetBucktes: () => api.get( `/api/directory/buckets` ),
CreateBuckets: ( data ) => api.post( `/api/Directory/bucket`, data ), CreateBuckets: ( data ) => api.post( `/api/Directory/bucket`, data ),
@ -16,7 +17,7 @@ export const DirectoryRepository = {
GetContactProfile: ( id ) => api.get( `/api/directory/profile/${ id }` ), GetContactProfile: ( id ) => api.get( `/api/directory/profile/${ id }` ),
CreateNote: ( data ) => api.post( '/api/directory/note', data ), 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 ), UpdateNote: ( id, data ) => api.put( `/api/directory/note/${ id }`, data ),
DeleteNote:(id)=> api.delete(`/api/directory/note/${ id }`) DeleteNote:(id)=> api.delete(`/api/directory/note/${ id }`)
} }