pramod_Task-#390 : Tasks for Bucket Admin #158

Merged
pramod.mahajan merged 9 commits from pramod_Task-#390 into Feature_Directory 2025-05-28 13:46:16 +00:00
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,16 +96,14 @@ 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>
@ -91,7 +113,7 @@ const EmployeeList = ({ employees }) => {
<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>
</div>
</div>
</td>
<td className="text-start">
<small className="text-muted">{employee.email}</small> <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> </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"> <div className="d-flex align-items-center gap-2">
<Avatar <Avatar
size="xs" size="xs"
classAvatar="m-0"
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>
<span >{contact?.name || ""}</span> </div>
</div>
</td> </td>
{/* Emails */} <td className="px-2" style={{ width: "20%" }}>
<td > <div className="d-flex flex-column align-items-start text-truncate">
<div className="d-flex flex-column align-items-start px-1">
{contact.contactEmails?.map((email, index) => ( {contact.contactEmails?.map((email, index) => (
<span key={email.id}> <span key={email.id} className="text-truncate">
<i className={getEmailIcon(email.label)} style={{fontSize:"12px"}}></i> <i className={getEmailIcon(email.label)} style={{ fontSize: "12px" }}></i>
<a href={`mailto:${email.email}`} className="text-decoration-none">{email.emailAddress}</a> <a href={`mailto:${email.emailAddress}`} className="text-decoration-none ms-1">{email.emailAddress}</a>
</span> </span>
))} ))}
</div> </div>
</td> </td>
<td> <td className="px-2" style={{ width: "20%" }}>
<div className="d-flex flex-column align-items-start"> <div className="d-flex flex-column align-items-start text-truncate">
{contact.contactPhones?.map((phone, index) => ( {contact.contactPhones?.map((phone, index) => (
<span key={phone.id}> <span key={phone.id}>
<i className={getPhoneIcon(phone.label)} style={{fontSize:"12px"}}></i> <i className={getPhoneIcon(phone.label)} style={{ fontSize: "12px" }}></i>
{phone.phoneNumber} <span className="ms-1">{phone.phoneNumber}</span>
</span> </span>
))} ))}
</div> </div>
</td> </td>
<td colSpan={2} className="text-start text-truncate px-2" style={{ width: "20%", maxWidth: "200px" }}>
<td className="text-start text-wrap">{contact.organization}</td> {contact.organization}
<td>
<div className="d-flex flex-column align-items-start">
<span className="badge badge-outline-primary">{contact?.contactCategory?.name }</span>
</div>
</td> </td>
{/* Actions */} <td className="px-2" style={{ width: "10%" }}>
{IsActive && <span className="badge badge-outline-secondary">
<td className="align-middle text-center "> {contact?.contactCategory?.name}
<i className='bx bx-edit bx-sm text-primary cursor-pointer' onClick={() => </span>
{ </td>
setSelectedContact( contact )
setIsOpenModal( true ) {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>
<i className='bx bx-trash bx-sm text-danger cursor-pointer' onClick={() => IsDeleted( contact.id )}></i> <i className="bx bx-trash bx-sm text-danger cursor-pointer"
onClick={() => IsDeleted(contact.id)}></i>
</td> </td>
} )}
</tr> </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,15 +147,17 @@ 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);
@ -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 && (
@ -175,17 +237,18 @@ const ManageBucket = () => {
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" title="Refresh"
onClick={() => rrefetch()} 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,18 +315,26 @@ const ManageBucket = () => {
</td> </td>
</tr> </tr>
)} )}
{sortedBucktesList.map((bucket) => ( {!loading &&
sortedBucktesList.map((bucket) => (
<tr key={bucket.id}> <tr key={bucket.id}>
<td colSpan={2} className="text-start text-wrap"> <td colSpan={2} className="text-start text-wrap">
<i className="bx bx-right-arrow-alt me-1"></i>{" "} <i className="bx bx-right-arrow-alt me-1"></i>{" "}
{bucket.name} {bucket.name}
</td> </td>
<td <td
className="text-start d-none d-sm-table-cell text-wrap" className="text-start d-none d-sm-table-cell text-truncate"
style={{ width: "60%" }} style={{
maxWidth: "300px",
whiteSpace: "wrap",
overflow: "hidden",
textOverflow: "ellipsis",
}}
title={bucket.description}
> >
{bucket.description} {bucket.description}
</td> </td>
<td>{bucket.numberOfContacts}</td>
<td className="justify-content-center"> <td className="justify-content-center">
<div className="d-flex justify-content-center align-items-center gap-2"> <div className="d-flex justify-content-center align-items-center gap-2">
<i <i
@ -276,7 +348,6 @@ const ManageBucket = () => {
className="bx bx-trash bx-sm text-danger cursor-pointer" className="bx bx-trash bx-sm text-danger cursor-pointer"
onClick={() => setDeleteBucket(bucket?.id)} onClick={() => setDeleteBucket(bucket?.id)}
></i> ></i>
<i className="bx bx-user-plus cursor-pointer"></i>
</div> </div>
</td> </td>
</tr> </tr>
@ -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,17 +10,15 @@ 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);
@ -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,9 +91,13 @@ 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
type="checkbox"
className="switch-input"
onChange={() => setIsActive(!IsActive)}
value={IsActive}
/>
<span className="switch-toggle-slider"> <span className="switch-toggle-slider">
<span className="switch-on"> <span className="switch-on">
{/* <i class="icon-base bx bx-check"></i> */} {/* <i class="icon-base bx bx-check"></i> */}
@ -108,7 +110,7 @@ const NotesDirectory = ( {isLoading, contactProfile, setProfileContact} ) =>
</label> </label>
<span <span
className={`btn btn-xs ${addNote ? "btn-danger" : "btn-primary"}`} className={`btn btn-xs ${addNote ? "btn-danger" : "btn-primary"}`}
onClick={() => setAddNote( !addNote )} onClick={() => setAddNote(!addNote)}
> >
{/* <i {/* <i
className={`icon-base bx ${ className={`icon-base bx ${
@ -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,11 +195,14 @@ 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">

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">
Organization
<span>Organization</span>
</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 }`)
} }