From 3670409977a85600f5da82e6978ac438c11d2136 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 12:18:05 +0530 Subject: [PATCH 001/433] created new repo for get contact list --- src/repositories/DirectoryRepository.jsx | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/repositories/DirectoryRepository.jsx diff --git a/src/repositories/DirectoryRepository.jsx b/src/repositories/DirectoryRepository.jsx new file mode 100644 index 00000000..f1925efc --- /dev/null +++ b/src/repositories/DirectoryRepository.jsx @@ -0,0 +1,5 @@ +import {api} from "../utils/axiosClient"; + +export const DirectoryRepository = { + GetContacts: () => api.get('/api/directory'), +} \ No newline at end of file From 5415210d70548e9aaac4706eef8cebdb2675e663 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 12:19:30 +0530 Subject: [PATCH 002/433] created new hook for get a contacts --- src/hooks/useDirectory.js | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/hooks/useDirectory.js diff --git a/src/hooks/useDirectory.js b/src/hooks/useDirectory.js new file mode 100644 index 00000000..f1e31c14 --- /dev/null +++ b/src/hooks/useDirectory.js @@ -0,0 +1,38 @@ +import {useState} from "react" +import {DirectoryRepository} from "../repositories/DirectoryRepository"; +import {cacheData, getCachedData} from "../slices/apiDataManager"; + +export const useDirectory = () => +{ + const [ contacts, setContacts ] = useState( [] ) + const [ loading, setLoading ] = useState( false ) + const [ error, setError ] = useState(); + + + + const fetch = async() => + { + const cache_contacts = getCachedData( "contacts" ); + if ( !cache_contacts ) + { + setLoading(true) + try + { + const response = await DirectoryRepository.GetContacts(); + setContacts( response.data ) + cacheData("contacts",response.data) + } catch ( error ) + { + setError( error ); + setLoading(false) + } + } + + } + + useState( () => + { + fetch() + }, [] ) + return {contacts,loading,error} +} \ No newline at end of file From cc98f34b4427998249504b4e2f2c9fa2ffba79b2 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 12:19:57 +0530 Subject: [PATCH 003/433] displaye contacts in List View --- .../Directory/ListViewDirectory.jsx | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/components/Directory/ListViewDirectory.jsx diff --git a/src/components/Directory/ListViewDirectory.jsx b/src/components/Directory/ListViewDirectory.jsx new file mode 100644 index 00000000..5011d229 --- /dev/null +++ b/src/components/Directory/ListViewDirectory.jsx @@ -0,0 +1,76 @@ +import React from 'react' + +const getEmailIcon = (type) => { + switch (type) { + case 'work': return "bx bx-briefcase me-1 " ; + case 'personal': return "bx bx-user me-1"; + case 'support': return "bxr headphone-mic me-1"; + case 'billing': return "bx bx-receipt me-1"; + default: return "bx bx-envelope me-1"; + } +}; + +const getPhoneIcon = (type) => { + switch (type) { + case 'business': return "bx bx-briefcase me-1 "; + case 'home': return "bx bx-mobile me-1 "; + case 'personal': return "bx bx-user me-1 "; + case 'landline': return "bx bx-phone me-1 "; + case 'fax': return "bx bx-printer me-1"; + case 'whatsapp': return "bxl-whatsapp me-1"; + case 'emergency': return "bx bx-error-circle me-1"; + default: return "bx bx-phone me-1"; + } +}; + +const ListViewDirectory = ({ contact }) => { + return ( + + {`${contact.name}`} + + {/* Emails */} + +
+ {contact.contactEmails?.map((email, index) => ( + + + {email.emailAddress} + + ))} +
+ + + {/* Phones */} + +
+ {contact.contactPhones?.map((phone, index) => ( + + + {phone.phoneNumber} + + ))} +
+ + + {/* Organization */} + {contact.organization} + + {/* Tags */} + +
+ {contact.tags?.map((tag, index) => ( + {tag} + ))} +
+ + + {/* Actions */} + + + + + + ); +}; + +export default ListViewDirectory; \ No newline at end of file From 50ef04547939cc590ceac64124c70c12dfa6bf93 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 12:20:55 +0530 Subject: [PATCH 004/433] display contact in list view --- src/pages/Directory/Directory.jsx | 47 +++++++++++++++++++------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/pages/Directory/Directory.jsx b/src/pages/Directory/Directory.jsx index c845e96b..d1736a6c 100644 --- a/src/pages/Directory/Directory.jsx +++ b/src/pages/Directory/Directory.jsx @@ -3,11 +3,16 @@ import Breadcrumb from "../../components/common/Breadcrumb"; import IconButton from "../../components/common/IconButton"; import GlobalModel from "../../components/common/GlobalModel"; import ManageDirectory from "../../components/Directory/ManageDirectory"; +import ListViewDirectory from "../../components/Directory/ListViewDirectory"; +import {useDirectory} from "../../hooks/useDirectory"; + const Directory = () => { const [isOpenModal, setIsOpenModal] = useState(false); - const closedModel = () => setIsOpenModal(false); - + const closedModel = () => setIsOpenModal( false ); + + const {contacts} = useDirectory(); + console.log(contacts) return (
{ ]} > - + -
+
@@ -43,22 +48,30 @@ const Directory = () => {
- - - - + + + + - - - + {contacts.map((contact) => ( + + ))}
- Name - +
+ alert("User icon clicked")} + /> + Name +
+
+
alert("User icon clicked")} /> Email
{ data-bs-toggle="dropdown" aria-expanded="false" > - Type + Category {/*
    {[ @@ -124,11 +137,9 @@ const Directory = () => {
- comming soon.... -
From 784eebafea3831ffaced7473f252807528a3edff Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 15:04:58 +0530 Subject: [PATCH 005/433] added one more master in masterList - Contact Category --- src/data/masters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/masters.js b/src/data/masters.js index 0f302184..e64972b4 100644 --- a/src/data/masters.js +++ b/src/data/masters.js @@ -1,5 +1,5 @@ // it important ------ -export const mastersList = [ {id: 1, name: "Application Role"}, {id: 2, name: "Job Role"}, {id: 3, name: "Activity"},{id: 4, name:"Work Category"} ] +export const mastersList = [ {id: 1, name: "Application Role"}, {id: 2, name: "Job Role"}, {id: 3, name: "Activity"},{id: 4, name:"Work Category"},{id:5,name:"Contact Category"}] // ------------------- export const dailyTask = [ From 74ed8d3c4fc250a761b3c5b8ad05e55427e3c57f Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 15:07:37 +0530 Subject: [PATCH 006/433] created new constact category master --- .../master/CreateContactCategory.jsx | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 src/components/master/CreateContactCategory.jsx diff --git a/src/components/master/CreateContactCategory.jsx b/src/components/master/CreateContactCategory.jsx new file mode 100644 index 00000000..5bce4171 --- /dev/null +++ b/src/components/master/CreateContactCategory.jsx @@ -0,0 +1,113 @@ +import React, { useEffect,useState } from 'react' +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { MasterRespository } from '../../repositories/MastersRepository'; +import { clearApiCacheKey } from '../../slices/apiCacheSlice'; +import { getCachedData,cacheData } from '../../slices/apiDataManager'; +import showToast from '../../services/toastService'; + + +const schema = z.object({ + name: z.string().min(1, { message: "Category name is required" }), + description: z.string().min(1, { message: "Description is required" }) + .max(255, { message: "Description cannot exceed 255 characters" }), +}); + +const CreateContactCategory = ({onClose}) => { + + const[isLoading,setIsLoading] = useState(false) + const { + register, + handleSubmit, + formState: { errors },reset + + } = useForm({ + resolver: zodResolver(schema), + defaultValues: { + name: "", + description: "", + + }, + }); + + const onSubmit = (data) => { + setIsLoading(true) + MasterRespository.createContactCategory(data).then((resp)=>{ + setIsLoading(false) + resetForm() + const cachedData = getCachedData("Contact Category"); + const updatedData = [...cachedData, resp?.data]; + cacheData("Contact Category", updatedData); + showToast("Contact Category Added successfully.", "success"); + + onClose() + }).catch((error)=>{ + showToast(error?.response?.data?.message, "error"); + setIsLoading(false) + }) + }; + const resetForm = () => { + reset({ + name: "", + description: "" + }); + setDescriptionLength(0); + } + + useEffect(()=>{ + return ()=>resetForm() + },[]) + + const [descriptionLength, setDescriptionLength] = useState(0); + const maxDescriptionLength = 255; + return (<> +
+
+ + + {errors.name &&

{errors.name.message}

} +
+
+ + +
+ {maxDescriptionLength - descriptionLength} characters left +
+ {errors.description && ( +

{errors.description.message}

+ )} +
+ +
+ + +
+ +
+ + + ) +} + +export default CreateContactCategory; \ No newline at end of file From 40df12de8631410d67b12294f6e58bb17ba8ea38 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 15:08:55 +0530 Subject: [PATCH 007/433] added CreateContactCategory in master modal --- src/components/master/MasterModal.jsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/master/MasterModal.jsx b/src/components/master/MasterModal.jsx index 056451a4..58ae4b16 100644 --- a/src/components/master/MasterModal.jsx +++ b/src/components/master/MasterModal.jsx @@ -13,6 +13,7 @@ import {cacheData, getCachedData} from "../../slices/apiDataManager"; import showToast from "../../services/toastService"; import CreateWorkCategory from "./CreateWorkCategory"; import EditWorkCategory from "./EditWorkCategory"; +import CreateCategory from "./CreateContactCategory"; const MasterModal = ({ modaldata, closeModal }) => { @@ -74,7 +75,6 @@ const MasterModal = ({ modaldata, closeModal }) => {
); } - return (
{ >
- +
{modaldata.modalType === "Application Role" && ( @@ -125,6 +128,9 @@ const MasterModal = ({ modaldata, closeModal }) => { {modaldata.modalType === "Edit-Work Category" && ( )} + {modaldata.modalType === "Contact Category" && ( + + )}
From cd21bc14ff9abf0134619e2927302fe7e3484b96 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 15:09:57 +0530 Subject: [PATCH 008/433] created new repo for tag and contact category mastes --- src/repositories/MastersRepository.jsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/repositories/MastersRepository.jsx b/src/repositories/MastersRepository.jsx index ee91022d..33761336 100644 --- a/src/repositories/MastersRepository.jsx +++ b/src/repositories/MastersRepository.jsx @@ -40,10 +40,20 @@ export const MasterRespository = { "Job Role": ( id ) => api.delete( `/api/roles/jobrole/${ id }` ), "Activity": ( id ) => api.delete( `/api/master/activity/delete/${ id }` ), "Application Role":(id)=>api.delete(`/api/roles/${id}`), - "Work Category": (id) => api.delete(`api/master/work-category/${id}`), + "Work Category": ( id ) => api.delete( `api/master/work-category/${ id }` ), + "Contact Category": ( id ) => api.delete( `/api/master/contact-category` ), + "Conatct Tag" :(id)=>api.delete("/api/master/contact-tag"), getWorkCategory:() => api.get(`/api/master/work-categories`), createWorkCategory: (data) => api.post(`/api/master/work-category`,data), - updateWorkCategory: (id,data) => api.post(`/api/master/work-category/edit/${id}`,data), + updateWorkCategory: ( id, data ) => api.post( `/api/master/work-category/edit/${ id }`, data ), + + getContactCategory: () => api.get( `/api/master/contact-categories` ), + createContactCategory: (data ) => api.post( `/api/master/contact-category`, data ), + updateContactCategory: ( id, data ) => api.post( `/api/master/contact-category/${ id }`, data ), + + getContactTag: () => api.get( `/api/master/contact-tag` ), + createContactTag: (data ) => api.post( `/api/master/contact-tag`, data ), + updateContactTag: ( id, data ) => api.post( `/api/master/contact-tag/${ id }`, data ) } \ No newline at end of file From 413dba4f5c902a17f25b31cd9bbf6687059d5f2e Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 21:47:20 +0530 Subject: [PATCH 009/433] added Tag master inside masterList --- src/data/masters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/masters.js b/src/data/masters.js index e64972b4..e94de7a3 100644 --- a/src/data/masters.js +++ b/src/data/masters.js @@ -1,5 +1,5 @@ // it important ------ -export const mastersList = [ {id: 1, name: "Application Role"}, {id: 2, name: "Job Role"}, {id: 3, name: "Activity"},{id: 4, name:"Work Category"},{id:5,name:"Contact Category"}] +export const mastersList = [ {id: 1, name: "Application Role"}, {id: 2, name: "Job Role"}, {id: 3, name: "Activity"},{id: 4, name:"Work Category"},{id:5,name:"Contact Category"},{id:6,name:"Contact Tag"}] // ------------------- export const dailyTask = [ From dcc611b768bd1320fd8d2ce95210c4a3ecbfa3cf Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 21:47:51 +0530 Subject: [PATCH 010/433] created new createContactMaster component --- src/components/master/CreateContactTag.jsx | 114 +++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/components/master/CreateContactTag.jsx diff --git a/src/components/master/CreateContactTag.jsx b/src/components/master/CreateContactTag.jsx new file mode 100644 index 00000000..dc92b789 --- /dev/null +++ b/src/components/master/CreateContactTag.jsx @@ -0,0 +1,114 @@ +import React, { useEffect,useState } from 'react' +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { MasterRespository } from '../../repositories/MastersRepository'; +import { clearApiCacheKey } from '../../slices/apiCacheSlice'; +import { getCachedData,cacheData } from '../../slices/apiDataManager'; +import showToast from '../../services/toastService'; + + +const schema = z.object({ + name: z.string().min(1, { message: "Tag name is required" }), + description: z.string().min(1, { message: "Description is required" }) + .max(255, { message: "Description cannot exceed 255 characters" }), +}); + +const CreateContactTag = ({onClose}) => { + + const[isLoading,setIsLoading] = useState(false) + const { + register, + handleSubmit, + formState: { errors },reset + + } = useForm({ + resolver: zodResolver(schema), + defaultValues: { + name: "", + description: "", + + }, + }); + + const onSubmit = (data) => { + setIsLoading(true) + MasterRespository.createContactTag(data).then((resp)=>{ + setIsLoading(false) + resetForm() + debugger + const cachedData = getCachedData("Contact Tag"); + const updatedData = [...cachedData, resp?.data]; + cacheData("Contact Tag", updatedData); + showToast("Contact Tag Added successfully.", "success"); + console.log(getCachedData("Contact Tag")) + onClose() + }).catch((error)=>{ + showToast(error?.response?.data?.message, "error"); + setIsLoading(false) + }) + }; + const resetForm = () => { + reset({ + name: "", + description: "" + }); + setDescriptionLength(0); + } + + useEffect(()=>{ + return ()=>resetForm() + },[]) + + const [descriptionLength, setDescriptionLength] = useState(0); + const maxDescriptionLength = 255; + return (<> +
+
+ + + {errors.name &&

{errors.name.message}

} +
+
+ + +
+ {maxDescriptionLength - descriptionLength} characters left +
+ {errors.description && ( +

{errors.description.message}

+ )} +
+ +
+ + +
+ +
+ + + ) +} + +export default CreateContactTag; \ No newline at end of file From d8df89137038164edf6f3ca04f876ca998bbe84c Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 21:48:44 +0530 Subject: [PATCH 011/433] added createContactTg component inside MasterModal --- src/components/master/MasterModal.jsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/master/MasterModal.jsx b/src/components/master/MasterModal.jsx index 58ae4b16..d7e32142 100644 --- a/src/components/master/MasterModal.jsx +++ b/src/components/master/MasterModal.jsx @@ -14,6 +14,7 @@ import showToast from "../../services/toastService"; import CreateWorkCategory from "./CreateWorkCategory"; import EditWorkCategory from "./EditWorkCategory"; import CreateCategory from "./CreateContactCategory"; +import CreateContactTag from "./CreateContactTag"; const MasterModal = ({ modaldata, closeModal }) => { @@ -131,6 +132,9 @@ const MasterModal = ({ modaldata, closeModal }) => { {modaldata.modalType === "Contact Category" && ( )} + {modaldata.modalType === "Contact Tag" && ( + + )}
From 04297346fa17d77cf7140d3d400b3d8ce457f707 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 22:21:20 +0530 Subject: [PATCH 012/433] feat: integrate Get Contact List API in useMaster hook --- src/hooks/masterHook/useMaster.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hooks/masterHook/useMaster.js b/src/hooks/masterHook/useMaster.js index 8ab98397..87ebb98d 100644 --- a/src/hooks/masterHook/useMaster.js +++ b/src/hooks/masterHook/useMaster.js @@ -47,10 +47,14 @@ const useMaster = (isMa) => { response = await MasterRespository.getActivites(); response = response.data break; - case "Work Category": + case "Work Category": response = await MasterRespository.getWorkCategory(); response = response.data break; + case "Contact Category": + response = await MasterRespository.getContactCategory() + response = response.data + break; case "Status": response = [{description: null,featurePermission: null,id: "02dd4761-363c-49ed-8851-3d2489a3e98d",status:"status 1"},{description: null,featurePermission: null,id: "03dy9761-363c-49ed-8851-3d2489a3e98d",status:"status 2"},{description: null,featurePermission: null,id: "03dy7761-263c-49ed-8851-3d2489a3e98d",status:"Status 3"}]; break; From a3d21ba098e57c52e6579b76e8b67788f5744331 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 22:58:02 +0530 Subject: [PATCH 013/433] updated incorrect API endpoint --- src/repositories/MastersRepository.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repositories/MastersRepository.jsx b/src/repositories/MastersRepository.jsx index 33761336..7a4ab1f0 100644 --- a/src/repositories/MastersRepository.jsx +++ b/src/repositories/MastersRepository.jsx @@ -52,7 +52,7 @@ export const MasterRespository = { createContactCategory: (data ) => api.post( `/api/master/contact-category`, data ), updateContactCategory: ( id, data ) => api.post( `/api/master/contact-category/${ id }`, data ), - getContactTag: () => api.get( `/api/master/contact-tag` ), + getContactTag: () => api.get( `/api/master/contact-tags` ), createContactTag: (data ) => api.post( `/api/master/contact-tag`, data ), updateContactTag: ( id, data ) => api.post( `/api/master/contact-tag/${ id }`, data ) From 2922cca22d0a32d05da7f29a95f96fb9693c8570 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 22:58:50 +0530 Subject: [PATCH 014/433] integrated Contact Tag list API and display in master table --- src/hooks/masterHook/useMaster.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hooks/masterHook/useMaster.js b/src/hooks/masterHook/useMaster.js index 87ebb98d..174c43cb 100644 --- a/src/hooks/masterHook/useMaster.js +++ b/src/hooks/masterHook/useMaster.js @@ -52,7 +52,11 @@ const useMaster = (isMa) => { response = response.data break; case "Contact Category": - response = await MasterRespository.getContactCategory() + response = await MasterRespository.getContactCategory(); + response = response.data + break; + case "Contact Tag": + response = await MasterRespository.getContactTag(); response = response.data break; case "Status": From 1c1a1da8a07d605dfc686f0a4ece8c54f525bca2 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Fri, 16 May 2025 19:07:15 +0530 Subject: [PATCH 015/433] removed backdrop --- src/components/common/GlobalModel.jsx | 37 ++++++++++++++------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/components/common/GlobalModel.jsx b/src/components/common/GlobalModel.jsx index 04d24d5a..bebdd5ca 100644 --- a/src/components/common/GlobalModel.jsx +++ b/src/components/common/GlobalModel.jsx @@ -12,28 +12,29 @@ const GlobalModel = ({ }) => { const modalRef = useRef(null); // Reference to the modal element - useEffect(() => { - const modalElement = modalRef.current; - const modalInstance = new window.bootstrap.Modal(modalElement); +useEffect(() => { + const modalElement = modalRef.current; + const modalInstance = new window.bootstrap.Modal(modalElement, { + backdrop: false // Disable backdrop + }); - // Show modal if isOpen is true - if (isOpen) { - modalInstance.show(); - } else { - modalInstance.hide(); - } + if (isOpen) { + modalInstance.show(); + } else { + modalInstance.hide(); + } - // Handle modal hide event to invoke the closeModal function - const handleHideModal = () => { - closeModal(); // Close the modal via React state - }; + const handleHideModal = () => { + closeModal(); + }; - modalElement.addEventListener('hidden.bs.modal', handleHideModal); + modalElement.addEventListener('hidden.bs.modal', handleHideModal); + + return () => { + modalElement.removeEventListener('hidden.bs.modal', handleHideModal); + }; +}, [isOpen, closeModal]); - return () => { - modalElement.removeEventListener('hidden.bs.modal', handleHideModal); - }; - }, [isOpen, closeModal]); // Dynamically set the modal size classes (modal-sm, modal-lg, modal-xl) const modalSizeClass = size ? `modal-${size}` : ''; // Default is empty if no size is specified From 7cfe04de2e23d92f2d0389271da68255109ee0c8 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Fri, 16 May 2025 19:08:41 +0530 Subject: [PATCH 016/433] modified TagInput, for array an object --- src/components/common/TagInput.jsx | 171 ++++++++++++++++++++++------- 1 file changed, 134 insertions(+), 37 deletions(-) diff --git a/src/components/common/TagInput.jsx b/src/components/common/TagInput.jsx index 58f3d16f..cdc88afb 100644 --- a/src/components/common/TagInput.jsx +++ b/src/components/common/TagInput.jsx @@ -1,32 +1,89 @@ -import React, { useState, useEffect } from "react"; import { useFormContext } from "react-hook-form"; +import React, { useEffect, useState } from "react"; +import { useDispatch } from "react-redux"; +import { changeMaster } from "../../slices/localVariablesSlice"; const TagInput = ({ label = "Tags", name = "tags", - placeholder = "Enter ... ", + placeholder = "Start typing to add...", color = "#e9ecef", + options = [], }) => { const [tags, setTags] = useState([]); const [input, setInput] = useState(""); - const { setValue } = useFormContext(); + const [suggestions, setSuggestions] = useState([]); + const { setValue, trigger } = useFormContext(); + const dispatch = useDispatch(); useEffect(() => { - setValue(name, tags); // sync to form when tags change - }, [tags, name, setValue]); + setValue( + name, + tags.map((tag) => ({ + id: tag.id ?? null, + name: tag.name, + })) + ); + }, [ tags, name, setValue ] ); - const addTag = (e) => { - e.preventDefault(); - const trimmed = input.trim(); - if (trimmed !== "" && !tags.includes(trimmed)) { - setTags([...tags, trimmed]); - setInput(""); + useEffect(() => { + if (input.trim() === "") { + setSuggestions([]); + } else { + const filtered = options?.filter( + (opt) => + opt?.name?.toLowerCase()?.includes(input?.toLowerCase()) && + !tags?.some((tag) => tag?.name === opt?.name) + ); + setSuggestions(filtered); + } + }, [input, options, tags]); + + const addTag = async ( tagObj ) => + { + if (!tags.some((tag) => tag.id === tagObj.id)) { + const cleanedTag = { + id: tagObj.id ?? null, + name: tagObj.name, + }; + const newTags = [...tags, cleanedTag]; + setTags(newTags); + setValue(name, newTags, { shouldValidate: true }); // ✅ only id + name + await trigger(name); + setInput(""); + setSuggestions([]); + } +}; + + + const handleInputKeyDown = (e) => { + if (e.key === "Enter" && input.trim() !== "") { + e.preventDefault(); + const existing = options.find( + (opt) => opt.name.toLowerCase() === input.trim().toLowerCase() + ); + const newTag = existing + ? existing + : { + id: null, + name: input.trim(), + description: input.trim(), + }; + addTag(newTag); // Call async function (not awaiting because it's UI input) + } else if (e.key === "Backspace" && input === "") { + setTags((prev) => prev.slice(0, -1)); } }; - const removeTag = (removeIndex) => { - const updated = tags.filter((_, i) => i !== removeIndex); - setTags(updated); + const handleSuggestionClick = (suggestion) => { + addTag(suggestion); + }; + + const removeTag = (indexToRemove) => { + const newTags = tags.filter((_, i) => i !== indexToRemove); + setTags(newTags); + setValue(name, newTags, { shouldValidate: true }); + trigger(name); }; const backgroundColor = color || "#f8f9fa"; @@ -34,32 +91,72 @@ const TagInput = ({ return ( <> - -
- {tags.map((tag, i) => ( - + {label} + + +
+
+ {tags.map((tag, index) => ( + + {tag.name} + removeTag(index)} + style={{ cursor: "pointer" }} + /> + + ))} + setInput(e.target.value)} + onKeyDown={handleInputKeyDown} + placeholder={placeholder} style={{ - color: iconColor, - backgroundColor, - padding: "2px 3px", - borderRadius: "2px" + border: "none", + outline: "none", + flex: 1, + minWidth: "120px", + }} + onFocus={() => dispatch(changeMaster("Contact Tag"))} + /> +
+ + {suggestions.length > 0 && ( +
    - {tag} - removeTag(i)}> - - ))} - setInput(e.target.value)} - onKeyDown={(e) => (e.key === "Enter" ? addTag(e) : null)} - placeholder={placeholder} - style={{ outline: "none", minWidth: "120px" }} - /> + {suggestions.map((sugg, i) => ( +
  • handleSuggestionClick(sugg)} + style={{ cursor: "pointer", fontSize: "0.875rem" }} + > + {sugg.name} +
  • + ))} +
+ )}
); From 085f45210edf55e2793b06cff99be453dfce5deb Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Fri, 16 May 2025 19:09:24 +0530 Subject: [PATCH 017/433] changed coulmn --- src/components/Directory/ListViewDirectory.jsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/Directory/ListViewDirectory.jsx b/src/components/Directory/ListViewDirectory.jsx index 5011d229..d9188fd2 100644 --- a/src/components/Directory/ListViewDirectory.jsx +++ b/src/components/Directory/ListViewDirectory.jsx @@ -26,10 +26,10 @@ const getPhoneIcon = (type) => { const ListViewDirectory = ({ contact }) => { return ( - {`${contact.name}`} + {`${contact.name}`} {/* Emails */} - +
{contact.contactEmails?.map((email, index) => ( @@ -52,20 +52,18 @@ const ListViewDirectory = ({ contact }) => {
- {/* Organization */} + {contact.organization} - {/* Tags */} +
- {contact.tags?.map((tag, index) => ( - {tag} - ))} + {contact?.contactCategory?.name }
{/* Actions */} - + From a1ee9dac38157b1b31f408c735116872b15ed8b5 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Fri, 16 May 2025 19:13:56 +0530 Subject: [PATCH 018/433] added useBuckets hook and set up cached data if have --- src/hooks/useDirectory.js | 43 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/hooks/useDirectory.js b/src/hooks/useDirectory.js index f1e31c14..707200df 100644 --- a/src/hooks/useDirectory.js +++ b/src/hooks/useDirectory.js @@ -1,4 +1,4 @@ -import {useState} from "react" +import {useEffect, useState} from "react" import {DirectoryRepository} from "../repositories/DirectoryRepository"; import {cacheData, getCachedData} from "../slices/apiDataManager"; @@ -20,12 +20,16 @@ export const useDirectory = () => { const response = await DirectoryRepository.GetContacts(); setContacts( response.data ) - cacheData("contacts",response.data) + cacheData( "contacts", response.data ) + setLoading(false) } catch ( error ) { setError( error ); setLoading(false) } + } else + { + setContacts(cache_contacts) } } @@ -35,4 +39,39 @@ export const useDirectory = () => fetch() }, [] ) return {contacts,loading,error} +} + +export const useBuckets = () => +{ + const [ buckets, setbuckets ] = useState(); + const [ loading, setLoading ] = useState(); + const [ Error, setError ] = useState( '' ) + + const fetch = async() => + { const cache_buckets = getCachedData("buckets") + if ( !cache_buckets ) + { + setLoading(true) + try + { + const resp = await DirectoryRepository.GetBucktes(); + setbuckets( resp.data ); + cacheData( "bucktes", resp.data ) + setLoading(false) + } catch ( error ) + { + const msg = error.response.data.message || error.message || "Something wrong"; + setError(msg) + } + } else + { + setbuckets(cache_buckets) + } + } + + useEffect( () => + { + fetch() + }, [] ) + return {buckets,loading,Error} } \ No newline at end of file From c7b5a0ba7d2d5369351806f7400b365146531ed9 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Fri, 16 May 2025 19:14:34 +0530 Subject: [PATCH 019/433] created new contact form --- src/components/Directory/ManageDirectory.jsx | 482 ++++++++++++++----- 1 file changed, 362 insertions(+), 120 deletions(-) diff --git a/src/components/Directory/ManageDirectory.jsx b/src/components/Directory/ManageDirectory.jsx index 5eae91af..2198c023 100644 --- a/src/components/Directory/ManageDirectory.jsx +++ b/src/components/Directory/ManageDirectory.jsx @@ -1,40 +1,82 @@ -import React, { useEffect } from "react"; -import { useForm, useFieldArray, FormProvider } from "react-hook-form"; +import React, { useEffect, useState } from "react"; +import { + useForm, + useFieldArray, + FormProvider, + useFormContext, +} from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import TagInput from "../common/TagInput"; import { z } from "zod"; +import IconButton from "../common/IconButton"; +import useMaster from "../../hooks/masterHook/useMaster"; +import { useDispatch, useSelector } from "react-redux"; +import { changeMaster } from "../../slices/localVariablesSlice"; +import { useBuckets } from "../../hooks/useDirectory"; +import {useProjects} from "../../hooks/useProjects"; -export const directorySchema = z.object({ - firstName: z.string().min(1, "First Name is required"), - lastName: z.string().min(1, "Last Name is required"), +export const ContactSchema = z.object({ + Name: z.string().min(1, "Name is required"), organization: z.string().min(1, "Organization name is required"), - type: z.string().min(1, "Type is required"), + ContactCategoryId: z.string().optional(), address: z.string().optional(), - description: z.string().min(1, { message: "Description is required" }), - email: z - .array(z.string().email("Invalid email")) - .nonempty("At least one email required"), - phone: z - .array(z.string().regex(/^\d{10}$/, "Phone must be 10 digits")) - .nonempty("At least one phone number is required"), - tags: z.array(z.string()).optional(), + description: z.string().min( 1, {message: "Description is required"} ), + ProjectId :z.string().optional(), + ContactEmails: z + .array( + z.object({ + label: z.string(), + emailAddress: z.string().email("Invalid email"), + }) + ) + .optional() + .default([]), + ContactPhones: z + .array( + z.object({ + label: z.string(), + phoneNumber: z.string().regex(/^\d{10}$/, "Phone must be 10 digits"), + }) + ) + .optional() + .default([]), + + tags: z + .array( + z.object({ + id: z.string().nullable(), + name: z.string(), + }) + ) + .optional(), + BucketIds: z.array(z.string()).optional(), }); +const ManageDirectory = ({submitContact,onCLosed}) => { + const selectedMaster = useSelector( + (store) => store.localVariables.selectedMaster + ); + const [categoryData, setCategoryData] = useState([]); + const [TagsData, setTagsData] = useState([]); + const { data, loading } = useMaster(); + const {buckets, loading: bucketsLoaging} = useBuckets(); + const {projects, loading: projectLoading} = useProjects(); + const [IsSubmitting,setSubmitting] = useState(false) + const dispatch = useDispatch(); - -const ManageDirectory = () => { const methods = useForm({ - resolver: zodResolver(directorySchema), + resolver: zodResolver(ContactSchema), defaultValues: { - firstName: "", - lastName: "", + Name: "", organization: "", - type: "", + ContactCategoryId: null, address: "", description: "", - email: [""], - phone: [""], + ProjectId:null, + ContactEmails: [], + ContactPhones: [], tags: [], + BucketIds: [], }, }); @@ -44,6 +86,9 @@ const ManageDirectory = () => { control, getValues, trigger, + setValue, + watch, + reset, formState: { errors }, } = methods; @@ -51,160 +96,357 @@ const ManageDirectory = () => { fields: emailFields, append: appendEmail, remove: removeEmail, - } = useFieldArray({ control, name: "email" }); + } = useFieldArray({ control, name: "ContactEmails" }); const { fields: phoneFields, append: appendPhone, remove: removePhone, - } = useFieldArray({ control, name: "phone" }); + } = useFieldArray({ control, name: "ContactPhones" }); useEffect(() => { - if (emailFields.length === 0) appendEmail(""); - if (phoneFields.length === 0) appendPhone(""); + if (emailFields.length === 0) appendEmail(""); + if (phoneFields.length === 0) appendPhone(""); }, [emailFields.length, phoneFields.length]); - const onSubmit = (data) => { - // console.log("Submitted:\n" + JSON.stringify(data, null, 2)); - }; + const handleAddEmail = async () => { - const emails = getValues("email"); + const emails = getValues("ContactEmails"); const lastIndex = emails.length - 1; - const valid = await trigger(`email.${lastIndex}`); - if (valid) appendEmail(""); + const valid = await trigger(`ContactEmails.${lastIndex}.emailAddress`); + if (valid) { + appendEmail({ label: "Work", emailAddress: "" }); + } }; const handleAddPhone = async () => { - const phones = getValues("phone"); + const phones = getValues("ContactPhones"); const lastIndex = phones.length - 1; - const valid = await trigger(`phone.${lastIndex}`); - if (valid) appendPhone(""); + const valid = await trigger(`ContactPhones.${lastIndex}.phoneNumber`); + if (valid) { + appendPhone({ label: "Office", phoneNumber: "" }); + } }; + useEffect(() => { + if (selectedMaster === "Contact Category") { + setCategoryData(data); + } else { + setTagsData(data); + } + }, [selectedMaster, data]); + + const watchBucketIds = watch("BucketIds"); + + const toggleBucketId = (id) => { + const updated = watchBucketIds?.includes(id) + ? watchBucketIds.filter((val) => val !== id) + : [...watchBucketIds, id]; + + setValue("BucketIds", updated, { shouldValidate: true }); + }; + const handleCheckboxChange = (id) => { + const updated = watchBucketIds.includes(id) + ? watchBucketIds.filter((i) => i !== id) + : [...watchBucketIds, id]; + + setValue("BucketIds", updated, { shouldValidate: true }); + }; + + + + + const onSubmit = ( data ) => + { + setSubmitting(true) + submitContact( data, reset, setSubmitting ) + + }; return (
+
+ {" "} +
Create New Contact
+
- - - {errors.firstName && {errors.firstName.message}} + + + {errors.Name && ( + {errors.Name.message} + )}
- - - {errors.lastName && {errors.lastName.message}} + + + {errors.organization && ( + + {errors.organization.message} + + )}
- -
- - - {errors.organization && {errors.organization.message}} -
- -
+
- - {emailFields.map((field, index) => (<> -
- - {index === emailFields.length - 1 ? ( - + ) : ( + + )} +
+ {errors.ContactEmails?.[index]?.emailAddress && ( + + {errors.ContactEmails[index].emailAddress.message} + + )} +
- {errors.email?.[index] && ( - - {errors.email[index]?.message} - - )} - ))} - -
- - {phoneFields.map((field, index) => (<> -
- - {index === phoneFields.length - 1 ? ( - + ) : ( + + )} +
+ {errors.ContactPhones?.[index]?.phoneNumber && ( + + {errors.ContactPhones[index].phoneNumber.message} + + )} +
- {errors.phone?.[ index ] && {errors.phone[ index ]?.message}} - ))} -
- - - {errors.type && {errors.type.message}} + + + {errors.ContactCategoryId && ( + {errors.ContactCategoryId.message} + )}
- + +
+
+
+
+ + +
+ {bucketsLoaging &&

Loading...

} + {buckets?.map((item) => ( +
+
+ handleCheckboxChange(item.id)} + /> + +
+
+ ))} +
+ + {errors.BucketIds && ( + {errors.BucketIds.message} + )} +
+ +
+ + + {errors.category && ( + {errors.category.message} + )}
- +
+ {maxDescriptionLength - descriptionLength} characters left +
+ {errors.description && ( +

{errors.description.message}

+ )} +
+ +
+ + +
+ + + + + ) +} + +export default EditContactTag; \ No newline at end of file From 3593e7d46caa561498aa3fcef97d98b782e7b8c5 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sat, 24 May 2025 16:02:16 +0530 Subject: [PATCH 105/433] added new modal for edit contact tag and delete contact tag --- src/components/master/MasterModal.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/master/MasterModal.jsx b/src/components/master/MasterModal.jsx index d7e32142..fc8209fe 100644 --- a/src/components/master/MasterModal.jsx +++ b/src/components/master/MasterModal.jsx @@ -15,6 +15,7 @@ import CreateWorkCategory from "./CreateWorkCategory"; import EditWorkCategory from "./EditWorkCategory"; import CreateCategory from "./CreateContactCategory"; import CreateContactTag from "./CreateContactTag"; +import EditContactTag from "./EditContactTag"; const MasterModal = ({ modaldata, closeModal }) => { @@ -23,7 +24,6 @@ const MasterModal = ({ modaldata, closeModal }) => { const handleSelectedMasterDeleted = async () => { const deleteFn = MasterRespository[modaldata.masterType]; - if (!deleteFn) { showToast(`No delete strategy defined for master type`,"error"); return false; @@ -135,6 +135,9 @@ const MasterModal = ({ modaldata, closeModal }) => { {modaldata.modalType === "Contact Tag" && ( )} + {modaldata.modalType === "Edit-Contact Tag" && ( + + )} From 79e38f6cd502857cafbcb25f5c20accae1b18ef2 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sat, 24 May 2025 16:02:46 +0530 Subject: [PATCH 106/433] modified url for delete and update contact tag --- src/repositories/MastersRepository.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/repositories/MastersRepository.jsx b/src/repositories/MastersRepository.jsx index 7a4ab1f0..30af194b 100644 --- a/src/repositories/MastersRepository.jsx +++ b/src/repositories/MastersRepository.jsx @@ -42,7 +42,7 @@ export const MasterRespository = { "Application Role":(id)=>api.delete(`/api/roles/${id}`), "Work Category": ( id ) => api.delete( `api/master/work-category/${ id }` ), "Contact Category": ( id ) => api.delete( `/api/master/contact-category` ), - "Conatct Tag" :(id)=>api.delete("/api/master/contact-tag"), + "Contact Tag" :(id)=>api.delete(`/api/master/contact-tag/${id}`), getWorkCategory:() => api.get(`/api/master/work-categories`), createWorkCategory: (data) => api.post(`/api/master/work-category`,data), @@ -54,6 +54,6 @@ export const MasterRespository = { getContactTag: () => api.get( `/api/master/contact-tags` ), createContactTag: (data ) => api.post( `/api/master/contact-tag`, data ), - updateContactTag: ( id, data ) => api.post( `/api/master/contact-tag/${ id }`, data ) + updateContactTag: ( id, data ) => api.post( `/api/master/contact-tag/edit/${ id }`, data ) } \ No newline at end of file From dcaa9ae7ec431838753a5a4245b9ac81dfe7bfc9 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sat, 24 May 2025 16:27:11 +0530 Subject: [PATCH 107/433] Add support for deleting contact categories --- src/repositories/MastersRepository.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repositories/MastersRepository.jsx b/src/repositories/MastersRepository.jsx index 7a4ab1f0..332ff779 100644 --- a/src/repositories/MastersRepository.jsx +++ b/src/repositories/MastersRepository.jsx @@ -41,7 +41,7 @@ export const MasterRespository = { "Activity": ( id ) => api.delete( `/api/master/activity/delete/${ id }` ), "Application Role":(id)=>api.delete(`/api/roles/${id}`), "Work Category": ( id ) => api.delete( `api/master/work-category/${ id }` ), - "Contact Category": ( id ) => api.delete( `/api/master/contact-category` ), + "Contact Category": ( id ) => api.delete( `/api/master/contact-category/${id}` ), "Conatct Tag" :(id)=>api.delete("/api/master/contact-tag"), getWorkCategory:() => api.get(`/api/master/work-categories`), From 695369cbf872fbb54bbebb5be4b5028f6c166520 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sat, 24 May 2025 17:26:20 +0530 Subject: [PATCH 108/433] modified url endpoint --- src/repositories/MastersRepository.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repositories/MastersRepository.jsx b/src/repositories/MastersRepository.jsx index 7a4ab1f0..549736f8 100644 --- a/src/repositories/MastersRepository.jsx +++ b/src/repositories/MastersRepository.jsx @@ -50,7 +50,7 @@ export const MasterRespository = { getContactCategory: () => api.get( `/api/master/contact-categories` ), createContactCategory: (data ) => api.post( `/api/master/contact-category`, data ), - updateContactCategory: ( id, data ) => api.post( `/api/master/contact-category/${ id }`, data ), + updateContactCategory: ( id, data ) => api.post( `/api/master/contact-category/edit/${ id }`, data ), getContactTag: () => api.get( `/api/master/contact-tags` ), createContactTag: (data ) => api.post( `/api/master/contact-tag`, data ), From 36b09747b01a01c78185ca38ecd37e685edf2aca Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sat, 24 May 2025 17:27:03 +0530 Subject: [PATCH 109/433] created new component for update contact category --- src/components/master/EditContactCategory.jsx | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/components/master/EditContactCategory.jsx diff --git a/src/components/master/EditContactCategory.jsx b/src/components/master/EditContactCategory.jsx new file mode 100644 index 00000000..331802d6 --- /dev/null +++ b/src/components/master/EditContactCategory.jsx @@ -0,0 +1,126 @@ +import React, { useEffect,useState } from 'react' +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { MasterRespository } from '../../repositories/MastersRepository'; +import { clearApiCacheKey } from '../../slices/apiCacheSlice'; +import { getCachedData,cacheData } from '../../slices/apiDataManager'; +import showToast from '../../services/toastService'; + + +const schema = z.object({ + name: z.string().min(1, { message: "Category name is required" }), + description: z.string().min(1, { message: "Description is required" }) + .max(255, { message: "Description cannot exceed 255 characters" }), +}); + +const EditContactCategory= ({data,onClose}) => { + + const[isLoading,setIsLoading] = useState(false) + const { + register, + handleSubmit, + formState: { errors },reset + + } = useForm({ + resolver: zodResolver(schema), + defaultValues: { + name: data?.name || "", + description:data?.description || "", + + }, + }); + + const onSubmit = (formdata) => { + setIsLoading(true) + const result = { + id:data?.id, + name: formdata?.name, + description: formdata.description, + }; + + + + MasterRespository.updateContactCategory(data?.id,result).then((resp)=>{ + setIsLoading(false) + showToast("Contact Category Updated successfully.", "success"); + const cachedData = getCachedData("Contact Category"); + if (cachedData) { + + const updatedData = cachedData.map((category) => + category.id === data?.id ? { ...category, ...resp.data } : category + ); + cacheData("Contact Category", updatedData); + } + + onClose() + }).catch((error)=>{ + showToast(error?.response?.data?.message, "error") + setIsLoading(false) + }) + + }; + const resetForm = () => { + reset({ + name: "", + description: "" + }); + setDescriptionLength(0); + } + + useEffect(()=>{ + return ()=>resetForm() + },[]) + + const [descriptionLength, setDescriptionLength] = useState(0); + const maxDescriptionLength = 255; + return (<> +
+
+ + + {errors.name &&

{errors.name.message}

} +
+
+ + +
+ {maxDescriptionLength - descriptionLength} characters left +
+ {errors.description && ( +

{errors.description.message}

+ )} +
+ +
+ + +
+ +
+ + + ) +} + +export default EditContactCategory; \ No newline at end of file From 1cdaefbec4b74e67331a06efff80f2ffd0a6aa7c Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sat, 24 May 2025 17:27:47 +0530 Subject: [PATCH 110/433] added EditcontactCatgorry component in modal for update --- src/components/master/MasterModal.jsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/master/MasterModal.jsx b/src/components/master/MasterModal.jsx index d7e32142..c65372da 100644 --- a/src/components/master/MasterModal.jsx +++ b/src/components/master/MasterModal.jsx @@ -15,6 +15,7 @@ import CreateWorkCategory from "./CreateWorkCategory"; import EditWorkCategory from "./EditWorkCategory"; import CreateCategory from "./CreateContactCategory"; import CreateContactTag from "./CreateContactTag"; +import EditContactCategory from "./EditContactCategory"; const MasterModal = ({ modaldata, closeModal }) => { @@ -131,6 +132,9 @@ const MasterModal = ({ modaldata, closeModal }) => { )} {modaldata.modalType === "Contact Category" && ( + )} + {modaldata.modalType === "Edit-Contact Category" && ( + )} {modaldata.modalType === "Contact Tag" && ( From 06c0c5ebb3d1adf13237136fc99b8f33e81c420d Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sun, 25 May 2025 00:21:33 +0530 Subject: [PATCH 111/433] added schema for bucket form validation --- src/components/Directory/DirectorySchema.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/Directory/DirectorySchema.js b/src/components/Directory/DirectorySchema.js index a9743829..3d30292f 100644 --- a/src/components/Directory/DirectorySchema.js +++ b/src/components/Directory/DirectorySchema.js @@ -53,5 +53,13 @@ export const ContactSchema = z // return hasValidEmail || hasValidPhone; // }, { // message: "At least one contact (email or phone) is required", -// path: ["contactPhone"], +// path: ["contactPhone"], // }); + + +// Buckets + +export const bucketScheam = z.object( { + name: z.string().min( 1, {message: "Name is required"} ), + description:z.string().min(1,{message:"Description is required"}) +}) \ No newline at end of file From 199cf4867f4d90f16e58b9edd0b8104a44014426 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sun, 25 May 2025 00:22:12 +0530 Subject: [PATCH 112/433] created new component for bucket managment --- src/components/Directory/ManageBucket.jsx | 226 ++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 src/components/Directory/ManageBucket.jsx diff --git a/src/components/Directory/ManageBucket.jsx b/src/components/Directory/ManageBucket.jsx new file mode 100644 index 00000000..d3a5d5c3 --- /dev/null +++ b/src/components/Directory/ManageBucket.jsx @@ -0,0 +1,226 @@ +import React, { useEffect, useState } from "react"; +import IconButton from "../common/IconButton"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { bucketScheam } from "./DirectorySchema"; +import showToast from "../../services/toastService"; +import Directory from "../../pages/Directory/Directory"; +import { DirectoryRepository } from "../../repositories/DirectoryRepository"; +import { cacheData, getCachedData } from "../../slices/apiDataManager"; +import { useBuckets } from "../../hooks/useDirectory"; + +const ManageBucket = () => +{ + const [bucketList, setBucketList] = useState([]); + + const { buckets } = useBuckets(); + const [action_bucket, setAction_bucket] = useState(false); + const [isSubmitting, setSubmitting] = useState(false); + const [selected_bucket, select_bucket] = useState(null); + + const { + register, + handleSubmit, + reset, + formState: { errors }, + } = useForm({ + resolver: zodResolver(bucketScheam), + defaultValues: { + 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"); + } + + handleBack(); + } 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 ] ); + + useEffect( () => + { + setBucketList( buckets ) + }, [ buckets ] ) + + const handleBack = () => { + select_bucket(null); + setAction_bucket(false); + setSubmitting(false); + }; + + + return ( +
+
+

Manage Buckets

+
+
+ + +
+
+ {!action_bucket ? ( +
+ + + + + + + + + + + {bucketList.map((bucket) => ( + + + + + + ))} + +
+
+ + Name +
+
+
+ + Description +
+
+
+ + Action +
+
+ {bucket.name} + + {bucket.description} + +
+ { + select_bucket(bucket); + setAction_bucket(true); + }} + > + + + +
+
+
+ ) : ( +
+
+ + + {errors.name && ( + {errors.name.message} + )} +
+
+ + +
+ {maxDescriptionLength - descriptionLength} characters left +
+ {errors.description && ( +

{errors.description.message}

+ )} +
+ +
+ + +
+ +
+ + + ) +} + +export default CreateContactCategory; \ No newline at end of file From a3428fed851407db9db329aaf989f6a716cd3ac0 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 15:08:55 +0530 Subject: [PATCH 225/433] added CreateContactCategory in master modal --- src/components/master/MasterModal.jsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/master/MasterModal.jsx b/src/components/master/MasterModal.jsx index 056451a4..58ae4b16 100644 --- a/src/components/master/MasterModal.jsx +++ b/src/components/master/MasterModal.jsx @@ -13,6 +13,7 @@ import {cacheData, getCachedData} from "../../slices/apiDataManager"; import showToast from "../../services/toastService"; import CreateWorkCategory from "./CreateWorkCategory"; import EditWorkCategory from "./EditWorkCategory"; +import CreateCategory from "./CreateContactCategory"; const MasterModal = ({ modaldata, closeModal }) => { @@ -74,7 +75,6 @@ const MasterModal = ({ modaldata, closeModal }) => {
); } - return (
{ >
- +
{modaldata.modalType === "Application Role" && ( @@ -125,6 +128,9 @@ const MasterModal = ({ modaldata, closeModal }) => { {modaldata.modalType === "Edit-Work Category" && ( )} + {modaldata.modalType === "Contact Category" && ( + + )}
From b4081e73bfd3c9ebc3a89c91d29ede83dab2bba9 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 15:09:57 +0530 Subject: [PATCH 226/433] created new repo for tag and contact category mastes --- src/repositories/MastersRepository.jsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/repositories/MastersRepository.jsx b/src/repositories/MastersRepository.jsx index ee91022d..33761336 100644 --- a/src/repositories/MastersRepository.jsx +++ b/src/repositories/MastersRepository.jsx @@ -40,10 +40,20 @@ export const MasterRespository = { "Job Role": ( id ) => api.delete( `/api/roles/jobrole/${ id }` ), "Activity": ( id ) => api.delete( `/api/master/activity/delete/${ id }` ), "Application Role":(id)=>api.delete(`/api/roles/${id}`), - "Work Category": (id) => api.delete(`api/master/work-category/${id}`), + "Work Category": ( id ) => api.delete( `api/master/work-category/${ id }` ), + "Contact Category": ( id ) => api.delete( `/api/master/contact-category` ), + "Conatct Tag" :(id)=>api.delete("/api/master/contact-tag"), getWorkCategory:() => api.get(`/api/master/work-categories`), createWorkCategory: (data) => api.post(`/api/master/work-category`,data), - updateWorkCategory: (id,data) => api.post(`/api/master/work-category/edit/${id}`,data), + updateWorkCategory: ( id, data ) => api.post( `/api/master/work-category/edit/${ id }`, data ), + + getContactCategory: () => api.get( `/api/master/contact-categories` ), + createContactCategory: (data ) => api.post( `/api/master/contact-category`, data ), + updateContactCategory: ( id, data ) => api.post( `/api/master/contact-category/${ id }`, data ), + + getContactTag: () => api.get( `/api/master/contact-tag` ), + createContactTag: (data ) => api.post( `/api/master/contact-tag`, data ), + updateContactTag: ( id, data ) => api.post( `/api/master/contact-tag/${ id }`, data ) } \ No newline at end of file From 8c0ab1102b51dcfd1db47c0ecc6a023b98bd80cb Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 21:47:20 +0530 Subject: [PATCH 227/433] added Tag master inside masterList --- src/data/masters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/masters.js b/src/data/masters.js index e64972b4..e94de7a3 100644 --- a/src/data/masters.js +++ b/src/data/masters.js @@ -1,5 +1,5 @@ // it important ------ -export const mastersList = [ {id: 1, name: "Application Role"}, {id: 2, name: "Job Role"}, {id: 3, name: "Activity"},{id: 4, name:"Work Category"},{id:5,name:"Contact Category"}] +export const mastersList = [ {id: 1, name: "Application Role"}, {id: 2, name: "Job Role"}, {id: 3, name: "Activity"},{id: 4, name:"Work Category"},{id:5,name:"Contact Category"},{id:6,name:"Contact Tag"}] // ------------------- export const dailyTask = [ From 5836a24aed0f0d6e2d31ee2584465234a9b3598e Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 21:47:51 +0530 Subject: [PATCH 228/433] created new createContactMaster component --- src/components/master/CreateContactTag.jsx | 114 +++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/components/master/CreateContactTag.jsx diff --git a/src/components/master/CreateContactTag.jsx b/src/components/master/CreateContactTag.jsx new file mode 100644 index 00000000..dc92b789 --- /dev/null +++ b/src/components/master/CreateContactTag.jsx @@ -0,0 +1,114 @@ +import React, { useEffect,useState } from 'react' +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { MasterRespository } from '../../repositories/MastersRepository'; +import { clearApiCacheKey } from '../../slices/apiCacheSlice'; +import { getCachedData,cacheData } from '../../slices/apiDataManager'; +import showToast from '../../services/toastService'; + + +const schema = z.object({ + name: z.string().min(1, { message: "Tag name is required" }), + description: z.string().min(1, { message: "Description is required" }) + .max(255, { message: "Description cannot exceed 255 characters" }), +}); + +const CreateContactTag = ({onClose}) => { + + const[isLoading,setIsLoading] = useState(false) + const { + register, + handleSubmit, + formState: { errors },reset + + } = useForm({ + resolver: zodResolver(schema), + defaultValues: { + name: "", + description: "", + + }, + }); + + const onSubmit = (data) => { + setIsLoading(true) + MasterRespository.createContactTag(data).then((resp)=>{ + setIsLoading(false) + resetForm() + debugger + const cachedData = getCachedData("Contact Tag"); + const updatedData = [...cachedData, resp?.data]; + cacheData("Contact Tag", updatedData); + showToast("Contact Tag Added successfully.", "success"); + console.log(getCachedData("Contact Tag")) + onClose() + }).catch((error)=>{ + showToast(error?.response?.data?.message, "error"); + setIsLoading(false) + }) + }; + const resetForm = () => { + reset({ + name: "", + description: "" + }); + setDescriptionLength(0); + } + + useEffect(()=>{ + return ()=>resetForm() + },[]) + + const [descriptionLength, setDescriptionLength] = useState(0); + const maxDescriptionLength = 255; + return (<> +
+
+ + + {errors.name &&

{errors.name.message}

} +
+
+ + +
+ {maxDescriptionLength - descriptionLength} characters left +
+ {errors.description && ( +

{errors.description.message}

+ )} +
+ +
+ + +
+ +
+ + + ) +} + +export default CreateContactTag; \ No newline at end of file From 8d7fcc62a7265f7a777fd37c2b8bb3d7d8f5e135 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 21:48:44 +0530 Subject: [PATCH 229/433] added createContactTg component inside MasterModal --- src/components/master/MasterModal.jsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/master/MasterModal.jsx b/src/components/master/MasterModal.jsx index 58ae4b16..d7e32142 100644 --- a/src/components/master/MasterModal.jsx +++ b/src/components/master/MasterModal.jsx @@ -14,6 +14,7 @@ import showToast from "../../services/toastService"; import CreateWorkCategory from "./CreateWorkCategory"; import EditWorkCategory from "./EditWorkCategory"; import CreateCategory from "./CreateContactCategory"; +import CreateContactTag from "./CreateContactTag"; const MasterModal = ({ modaldata, closeModal }) => { @@ -131,6 +132,9 @@ const MasterModal = ({ modaldata, closeModal }) => { {modaldata.modalType === "Contact Category" && ( )} + {modaldata.modalType === "Contact Tag" && ( + + )} From 1e3a1f03658f1bb8b73a5a0fd9145f709b3bb8db Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 22:21:20 +0530 Subject: [PATCH 230/433] feat: integrate Get Contact List API in useMaster hook --- src/hooks/masterHook/useMaster.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hooks/masterHook/useMaster.js b/src/hooks/masterHook/useMaster.js index 8ab98397..87ebb98d 100644 --- a/src/hooks/masterHook/useMaster.js +++ b/src/hooks/masterHook/useMaster.js @@ -47,10 +47,14 @@ const useMaster = (isMa) => { response = await MasterRespository.getActivites(); response = response.data break; - case "Work Category": + case "Work Category": response = await MasterRespository.getWorkCategory(); response = response.data break; + case "Contact Category": + response = await MasterRespository.getContactCategory() + response = response.data + break; case "Status": response = [{description: null,featurePermission: null,id: "02dd4761-363c-49ed-8851-3d2489a3e98d",status:"status 1"},{description: null,featurePermission: null,id: "03dy9761-363c-49ed-8851-3d2489a3e98d",status:"status 2"},{description: null,featurePermission: null,id: "03dy7761-263c-49ed-8851-3d2489a3e98d",status:"Status 3"}]; break; From 5b3b6904bfc41dc9933eb815594c9427d277814e Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 22:58:02 +0530 Subject: [PATCH 231/433] updated incorrect API endpoint --- src/repositories/MastersRepository.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repositories/MastersRepository.jsx b/src/repositories/MastersRepository.jsx index 33761336..7a4ab1f0 100644 --- a/src/repositories/MastersRepository.jsx +++ b/src/repositories/MastersRepository.jsx @@ -52,7 +52,7 @@ export const MasterRespository = { createContactCategory: (data ) => api.post( `/api/master/contact-category`, data ), updateContactCategory: ( id, data ) => api.post( `/api/master/contact-category/${ id }`, data ), - getContactTag: () => api.get( `/api/master/contact-tag` ), + getContactTag: () => api.get( `/api/master/contact-tags` ), createContactTag: (data ) => api.post( `/api/master/contact-tag`, data ), updateContactTag: ( id, data ) => api.post( `/api/master/contact-tag/${ id }`, data ) From e8504c662731a0192ad88903bf5f7a47a9cc73b7 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Thu, 15 May 2025 22:58:50 +0530 Subject: [PATCH 232/433] integrated Contact Tag list API and display in master table --- src/hooks/masterHook/useMaster.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hooks/masterHook/useMaster.js b/src/hooks/masterHook/useMaster.js index 87ebb98d..174c43cb 100644 --- a/src/hooks/masterHook/useMaster.js +++ b/src/hooks/masterHook/useMaster.js @@ -52,7 +52,11 @@ const useMaster = (isMa) => { response = response.data break; case "Contact Category": - response = await MasterRespository.getContactCategory() + response = await MasterRespository.getContactCategory(); + response = response.data + break; + case "Contact Tag": + response = await MasterRespository.getContactTag(); response = response.data break; case "Status": From 33f62206222a318cc7aac81cc64c3d23518eb9c3 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Fri, 16 May 2025 19:07:15 +0530 Subject: [PATCH 233/433] removed backdrop --- src/components/common/GlobalModel.jsx | 37 ++++++++++++++------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/components/common/GlobalModel.jsx b/src/components/common/GlobalModel.jsx index 04d24d5a..bebdd5ca 100644 --- a/src/components/common/GlobalModel.jsx +++ b/src/components/common/GlobalModel.jsx @@ -12,28 +12,29 @@ const GlobalModel = ({ }) => { const modalRef = useRef(null); // Reference to the modal element - useEffect(() => { - const modalElement = modalRef.current; - const modalInstance = new window.bootstrap.Modal(modalElement); +useEffect(() => { + const modalElement = modalRef.current; + const modalInstance = new window.bootstrap.Modal(modalElement, { + backdrop: false // Disable backdrop + }); - // Show modal if isOpen is true - if (isOpen) { - modalInstance.show(); - } else { - modalInstance.hide(); - } + if (isOpen) { + modalInstance.show(); + } else { + modalInstance.hide(); + } - // Handle modal hide event to invoke the closeModal function - const handleHideModal = () => { - closeModal(); // Close the modal via React state - }; + const handleHideModal = () => { + closeModal(); + }; - modalElement.addEventListener('hidden.bs.modal', handleHideModal); + modalElement.addEventListener('hidden.bs.modal', handleHideModal); + + return () => { + modalElement.removeEventListener('hidden.bs.modal', handleHideModal); + }; +}, [isOpen, closeModal]); - return () => { - modalElement.removeEventListener('hidden.bs.modal', handleHideModal); - }; - }, [isOpen, closeModal]); // Dynamically set the modal size classes (modal-sm, modal-lg, modal-xl) const modalSizeClass = size ? `modal-${size}` : ''; // Default is empty if no size is specified From 354c65324091cbfa253797505f954233957c4121 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Fri, 16 May 2025 19:08:41 +0530 Subject: [PATCH 234/433] modified TagInput, for array an object --- src/components/common/TagInput.jsx | 171 ++++++++++++++++++++++------- 1 file changed, 134 insertions(+), 37 deletions(-) diff --git a/src/components/common/TagInput.jsx b/src/components/common/TagInput.jsx index 58f3d16f..cdc88afb 100644 --- a/src/components/common/TagInput.jsx +++ b/src/components/common/TagInput.jsx @@ -1,32 +1,89 @@ -import React, { useState, useEffect } from "react"; import { useFormContext } from "react-hook-form"; +import React, { useEffect, useState } from "react"; +import { useDispatch } from "react-redux"; +import { changeMaster } from "../../slices/localVariablesSlice"; const TagInput = ({ label = "Tags", name = "tags", - placeholder = "Enter ... ", + placeholder = "Start typing to add...", color = "#e9ecef", + options = [], }) => { const [tags, setTags] = useState([]); const [input, setInput] = useState(""); - const { setValue } = useFormContext(); + const [suggestions, setSuggestions] = useState([]); + const { setValue, trigger } = useFormContext(); + const dispatch = useDispatch(); useEffect(() => { - setValue(name, tags); // sync to form when tags change - }, [tags, name, setValue]); + setValue( + name, + tags.map((tag) => ({ + id: tag.id ?? null, + name: tag.name, + })) + ); + }, [ tags, name, setValue ] ); - const addTag = (e) => { - e.preventDefault(); - const trimmed = input.trim(); - if (trimmed !== "" && !tags.includes(trimmed)) { - setTags([...tags, trimmed]); - setInput(""); + useEffect(() => { + if (input.trim() === "") { + setSuggestions([]); + } else { + const filtered = options?.filter( + (opt) => + opt?.name?.toLowerCase()?.includes(input?.toLowerCase()) && + !tags?.some((tag) => tag?.name === opt?.name) + ); + setSuggestions(filtered); + } + }, [input, options, tags]); + + const addTag = async ( tagObj ) => + { + if (!tags.some((tag) => tag.id === tagObj.id)) { + const cleanedTag = { + id: tagObj.id ?? null, + name: tagObj.name, + }; + const newTags = [...tags, cleanedTag]; + setTags(newTags); + setValue(name, newTags, { shouldValidate: true }); // ✅ only id + name + await trigger(name); + setInput(""); + setSuggestions([]); + } +}; + + + const handleInputKeyDown = (e) => { + if (e.key === "Enter" && input.trim() !== "") { + e.preventDefault(); + const existing = options.find( + (opt) => opt.name.toLowerCase() === input.trim().toLowerCase() + ); + const newTag = existing + ? existing + : { + id: null, + name: input.trim(), + description: input.trim(), + }; + addTag(newTag); // Call async function (not awaiting because it's UI input) + } else if (e.key === "Backspace" && input === "") { + setTags((prev) => prev.slice(0, -1)); } }; - const removeTag = (removeIndex) => { - const updated = tags.filter((_, i) => i !== removeIndex); - setTags(updated); + const handleSuggestionClick = (suggestion) => { + addTag(suggestion); + }; + + const removeTag = (indexToRemove) => { + const newTags = tags.filter((_, i) => i !== indexToRemove); + setTags(newTags); + setValue(name, newTags, { shouldValidate: true }); + trigger(name); }; const backgroundColor = color || "#f8f9fa"; @@ -34,32 +91,72 @@ const TagInput = ({ return ( <> - -
- {tags.map((tag, i) => ( - + {label} + + +
+
+ {tags.map((tag, index) => ( + + {tag.name} + removeTag(index)} + style={{ cursor: "pointer" }} + /> + + ))} + setInput(e.target.value)} + onKeyDown={handleInputKeyDown} + placeholder={placeholder} style={{ - color: iconColor, - backgroundColor, - padding: "2px 3px", - borderRadius: "2px" + border: "none", + outline: "none", + flex: 1, + minWidth: "120px", + }} + onFocus={() => dispatch(changeMaster("Contact Tag"))} + /> +
+ + {suggestions.length > 0 && ( +
    - {tag} - removeTag(i)}> - - ))} - setInput(e.target.value)} - onKeyDown={(e) => (e.key === "Enter" ? addTag(e) : null)} - placeholder={placeholder} - style={{ outline: "none", minWidth: "120px" }} - /> + {suggestions.map((sugg, i) => ( +
  • handleSuggestionClick(sugg)} + style={{ cursor: "pointer", fontSize: "0.875rem" }} + > + {sugg.name} +
  • + ))} +
+ )}
); From 07a402d28dc81030d60577c98d9745d6a238f3bd Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Fri, 16 May 2025 19:09:24 +0530 Subject: [PATCH 235/433] changed coulmn --- src/components/Directory/ListViewDirectory.jsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/Directory/ListViewDirectory.jsx b/src/components/Directory/ListViewDirectory.jsx index 5011d229..d9188fd2 100644 --- a/src/components/Directory/ListViewDirectory.jsx +++ b/src/components/Directory/ListViewDirectory.jsx @@ -26,10 +26,10 @@ const getPhoneIcon = (type) => { const ListViewDirectory = ({ contact }) => { return ( - {`${contact.name}`} + {`${contact.name}`} {/* Emails */} - +
{contact.contactEmails?.map((email, index) => ( @@ -52,20 +52,18 @@ const ListViewDirectory = ({ contact }) => {
- {/* Organization */} + {contact.organization} - {/* Tags */} +
- {contact.tags?.map((tag, index) => ( - {tag} - ))} + {contact?.contactCategory?.name }
{/* Actions */} - + From a3191716749b15d09cf3474ccbe53443a50e29f1 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Fri, 16 May 2025 19:13:56 +0530 Subject: [PATCH 236/433] added useBuckets hook and set up cached data if have --- src/hooks/useDirectory.js | 43 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/hooks/useDirectory.js b/src/hooks/useDirectory.js index f1e31c14..707200df 100644 --- a/src/hooks/useDirectory.js +++ b/src/hooks/useDirectory.js @@ -1,4 +1,4 @@ -import {useState} from "react" +import {useEffect, useState} from "react" import {DirectoryRepository} from "../repositories/DirectoryRepository"; import {cacheData, getCachedData} from "../slices/apiDataManager"; @@ -20,12 +20,16 @@ export const useDirectory = () => { const response = await DirectoryRepository.GetContacts(); setContacts( response.data ) - cacheData("contacts",response.data) + cacheData( "contacts", response.data ) + setLoading(false) } catch ( error ) { setError( error ); setLoading(false) } + } else + { + setContacts(cache_contacts) } } @@ -35,4 +39,39 @@ export const useDirectory = () => fetch() }, [] ) return {contacts,loading,error} +} + +export const useBuckets = () => +{ + const [ buckets, setbuckets ] = useState(); + const [ loading, setLoading ] = useState(); + const [ Error, setError ] = useState( '' ) + + const fetch = async() => + { const cache_buckets = getCachedData("buckets") + if ( !cache_buckets ) + { + setLoading(true) + try + { + const resp = await DirectoryRepository.GetBucktes(); + setbuckets( resp.data ); + cacheData( "bucktes", resp.data ) + setLoading(false) + } catch ( error ) + { + const msg = error.response.data.message || error.message || "Something wrong"; + setError(msg) + } + } else + { + setbuckets(cache_buckets) + } + } + + useEffect( () => + { + fetch() + }, [] ) + return {buckets,loading,Error} } \ No newline at end of file From 283e13985d4abdf4b7c0cc5be9fe11f743fa8c54 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Fri, 16 May 2025 19:14:34 +0530 Subject: [PATCH 237/433] created new contact form --- src/components/Directory/ManageDirectory.jsx | 482 ++++++++++++++----- 1 file changed, 362 insertions(+), 120 deletions(-) diff --git a/src/components/Directory/ManageDirectory.jsx b/src/components/Directory/ManageDirectory.jsx index 5eae91af..2198c023 100644 --- a/src/components/Directory/ManageDirectory.jsx +++ b/src/components/Directory/ManageDirectory.jsx @@ -1,40 +1,82 @@ -import React, { useEffect } from "react"; -import { useForm, useFieldArray, FormProvider } from "react-hook-form"; +import React, { useEffect, useState } from "react"; +import { + useForm, + useFieldArray, + FormProvider, + useFormContext, +} from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import TagInput from "../common/TagInput"; import { z } from "zod"; +import IconButton from "../common/IconButton"; +import useMaster from "../../hooks/masterHook/useMaster"; +import { useDispatch, useSelector } from "react-redux"; +import { changeMaster } from "../../slices/localVariablesSlice"; +import { useBuckets } from "../../hooks/useDirectory"; +import {useProjects} from "../../hooks/useProjects"; -export const directorySchema = z.object({ - firstName: z.string().min(1, "First Name is required"), - lastName: z.string().min(1, "Last Name is required"), +export const ContactSchema = z.object({ + Name: z.string().min(1, "Name is required"), organization: z.string().min(1, "Organization name is required"), - type: z.string().min(1, "Type is required"), + ContactCategoryId: z.string().optional(), address: z.string().optional(), - description: z.string().min(1, { message: "Description is required" }), - email: z - .array(z.string().email("Invalid email")) - .nonempty("At least one email required"), - phone: z - .array(z.string().regex(/^\d{10}$/, "Phone must be 10 digits")) - .nonempty("At least one phone number is required"), - tags: z.array(z.string()).optional(), + description: z.string().min( 1, {message: "Description is required"} ), + ProjectId :z.string().optional(), + ContactEmails: z + .array( + z.object({ + label: z.string(), + emailAddress: z.string().email("Invalid email"), + }) + ) + .optional() + .default([]), + ContactPhones: z + .array( + z.object({ + label: z.string(), + phoneNumber: z.string().regex(/^\d{10}$/, "Phone must be 10 digits"), + }) + ) + .optional() + .default([]), + + tags: z + .array( + z.object({ + id: z.string().nullable(), + name: z.string(), + }) + ) + .optional(), + BucketIds: z.array(z.string()).optional(), }); +const ManageDirectory = ({submitContact,onCLosed}) => { + const selectedMaster = useSelector( + (store) => store.localVariables.selectedMaster + ); + const [categoryData, setCategoryData] = useState([]); + const [TagsData, setTagsData] = useState([]); + const { data, loading } = useMaster(); + const {buckets, loading: bucketsLoaging} = useBuckets(); + const {projects, loading: projectLoading} = useProjects(); + const [IsSubmitting,setSubmitting] = useState(false) + const dispatch = useDispatch(); - -const ManageDirectory = () => { const methods = useForm({ - resolver: zodResolver(directorySchema), + resolver: zodResolver(ContactSchema), defaultValues: { - firstName: "", - lastName: "", + Name: "", organization: "", - type: "", + ContactCategoryId: null, address: "", description: "", - email: [""], - phone: [""], + ProjectId:null, + ContactEmails: [], + ContactPhones: [], tags: [], + BucketIds: [], }, }); @@ -44,6 +86,9 @@ const ManageDirectory = () => { control, getValues, trigger, + setValue, + watch, + reset, formState: { errors }, } = methods; @@ -51,160 +96,357 @@ const ManageDirectory = () => { fields: emailFields, append: appendEmail, remove: removeEmail, - } = useFieldArray({ control, name: "email" }); + } = useFieldArray({ control, name: "ContactEmails" }); const { fields: phoneFields, append: appendPhone, remove: removePhone, - } = useFieldArray({ control, name: "phone" }); + } = useFieldArray({ control, name: "ContactPhones" }); useEffect(() => { - if (emailFields.length === 0) appendEmail(""); - if (phoneFields.length === 0) appendPhone(""); + if (emailFields.length === 0) appendEmail(""); + if (phoneFields.length === 0) appendPhone(""); }, [emailFields.length, phoneFields.length]); - const onSubmit = (data) => { - // console.log("Submitted:\n" + JSON.stringify(data, null, 2)); - }; + const handleAddEmail = async () => { - const emails = getValues("email"); + const emails = getValues("ContactEmails"); const lastIndex = emails.length - 1; - const valid = await trigger(`email.${lastIndex}`); - if (valid) appendEmail(""); + const valid = await trigger(`ContactEmails.${lastIndex}.emailAddress`); + if (valid) { + appendEmail({ label: "Work", emailAddress: "" }); + } }; const handleAddPhone = async () => { - const phones = getValues("phone"); + const phones = getValues("ContactPhones"); const lastIndex = phones.length - 1; - const valid = await trigger(`phone.${lastIndex}`); - if (valid) appendPhone(""); + const valid = await trigger(`ContactPhones.${lastIndex}.phoneNumber`); + if (valid) { + appendPhone({ label: "Office", phoneNumber: "" }); + } }; + useEffect(() => { + if (selectedMaster === "Contact Category") { + setCategoryData(data); + } else { + setTagsData(data); + } + }, [selectedMaster, data]); + + const watchBucketIds = watch("BucketIds"); + + const toggleBucketId = (id) => { + const updated = watchBucketIds?.includes(id) + ? watchBucketIds.filter((val) => val !== id) + : [...watchBucketIds, id]; + + setValue("BucketIds", updated, { shouldValidate: true }); + }; + const handleCheckboxChange = (id) => { + const updated = watchBucketIds.includes(id) + ? watchBucketIds.filter((i) => i !== id) + : [...watchBucketIds, id]; + + setValue("BucketIds", updated, { shouldValidate: true }); + }; + + + + + const onSubmit = ( data ) => + { + setSubmitting(true) + submitContact( data, reset, setSubmitting ) + + }; return (
+
+ {" "} +
Create New Contact
+
- - - {errors.firstName && {errors.firstName.message}} + + + {errors.Name && ( + {errors.Name.message} + )}
- - - {errors.lastName && {errors.lastName.message}} + + + {errors.organization && ( + + {errors.organization.message} + + )}
- -
- - - {errors.organization && {errors.organization.message}} -
- -
+
- - {emailFields.map((field, index) => (<> -
- - {index === emailFields.length - 1 ? ( - + ) : ( + + )} +
+ {errors.ContactEmails?.[index]?.emailAddress && ( + + {errors.ContactEmails[index].emailAddress.message} + + )} +
- {errors.email?.[index] && ( - - {errors.email[index]?.message} - - )} - ))} - -
- - {phoneFields.map((field, index) => (<> -
- - {index === phoneFields.length - 1 ? ( - + ) : ( + + )} +
+ {errors.ContactPhones?.[index]?.phoneNumber && ( + + {errors.ContactPhones[index].phoneNumber.message} + + )} +
- {errors.phone?.[ index ] && {errors.phone[ index ]?.message}} - ))} -
- - - {errors.type && {errors.type.message}} + + + {errors.ContactCategoryId && ( + {errors.ContactCategoryId.message} + )}
- + +
+
+
+
+ + +
+ {bucketsLoaging &&

Loading...

} + {buckets?.map((item) => ( +
+
+ handleCheckboxChange(item.id)} + /> + +
+
+ ))} +
+ + {errors.BucketIds && ( + {errors.BucketIds.message} + )} +
+ +
+ + + {errors.category && ( + {errors.category.message} + )}
- +
+ {maxDescriptionLength - descriptionLength} characters left +
+ {errors.description && ( +

{errors.description.message}

+ )} +
+ +
+ + +
+ + + + + ) +} + +export default EditContactTag; \ No newline at end of file From 8999d7bb479f0466d218603ad1075f6c50b09a66 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sat, 24 May 2025 16:02:16 +0530 Subject: [PATCH 323/433] added new modal for edit contact tag and delete contact tag --- src/components/master/MasterModal.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/master/MasterModal.jsx b/src/components/master/MasterModal.jsx index d7e32142..fc8209fe 100644 --- a/src/components/master/MasterModal.jsx +++ b/src/components/master/MasterModal.jsx @@ -15,6 +15,7 @@ import CreateWorkCategory from "./CreateWorkCategory"; import EditWorkCategory from "./EditWorkCategory"; import CreateCategory from "./CreateContactCategory"; import CreateContactTag from "./CreateContactTag"; +import EditContactTag from "./EditContactTag"; const MasterModal = ({ modaldata, closeModal }) => { @@ -23,7 +24,6 @@ const MasterModal = ({ modaldata, closeModal }) => { const handleSelectedMasterDeleted = async () => { const deleteFn = MasterRespository[modaldata.masterType]; - if (!deleteFn) { showToast(`No delete strategy defined for master type`,"error"); return false; @@ -135,6 +135,9 @@ const MasterModal = ({ modaldata, closeModal }) => { {modaldata.modalType === "Contact Tag" && ( )} + {modaldata.modalType === "Edit-Contact Tag" && ( + + )} From 51e48e632a1798bb928259cbc17186c7e3068a2d Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sat, 24 May 2025 16:02:46 +0530 Subject: [PATCH 324/433] modified url for delete and update contact tag --- src/repositories/MastersRepository.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/repositories/MastersRepository.jsx b/src/repositories/MastersRepository.jsx index 7a4ab1f0..30af194b 100644 --- a/src/repositories/MastersRepository.jsx +++ b/src/repositories/MastersRepository.jsx @@ -42,7 +42,7 @@ export const MasterRespository = { "Application Role":(id)=>api.delete(`/api/roles/${id}`), "Work Category": ( id ) => api.delete( `api/master/work-category/${ id }` ), "Contact Category": ( id ) => api.delete( `/api/master/contact-category` ), - "Conatct Tag" :(id)=>api.delete("/api/master/contact-tag"), + "Contact Tag" :(id)=>api.delete(`/api/master/contact-tag/${id}`), getWorkCategory:() => api.get(`/api/master/work-categories`), createWorkCategory: (data) => api.post(`/api/master/work-category`,data), @@ -54,6 +54,6 @@ export const MasterRespository = { getContactTag: () => api.get( `/api/master/contact-tags` ), createContactTag: (data ) => api.post( `/api/master/contact-tag`, data ), - updateContactTag: ( id, data ) => api.post( `/api/master/contact-tag/${ id }`, data ) + updateContactTag: ( id, data ) => api.post( `/api/master/contact-tag/edit/${ id }`, data ) } \ No newline at end of file From 6a8332b6067be155c80e832af08446eed8817685 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sat, 24 May 2025 17:26:20 +0530 Subject: [PATCH 325/433] modified url endpoint --- src/repositories/MastersRepository.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/repositories/MastersRepository.jsx b/src/repositories/MastersRepository.jsx index 30af194b..d0148366 100644 --- a/src/repositories/MastersRepository.jsx +++ b/src/repositories/MastersRepository.jsx @@ -50,7 +50,7 @@ export const MasterRespository = { getContactCategory: () => api.get( `/api/master/contact-categories` ), createContactCategory: (data ) => api.post( `/api/master/contact-category`, data ), - updateContactCategory: ( id, data ) => api.post( `/api/master/contact-category/${ id }`, data ), + updateContactCategory: ( id, data ) => api.post( `/api/master/contact-category/edit/${ id }`, data ), getContactTag: () => api.get( `/api/master/contact-tags` ), createContactTag: (data ) => api.post( `/api/master/contact-tag`, data ), From ded8ab7752f76c0aa5a01e13fb731e546f7c7a3d Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sat, 24 May 2025 17:27:03 +0530 Subject: [PATCH 326/433] created new component for update contact category --- src/components/master/EditContactCategory.jsx | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/components/master/EditContactCategory.jsx diff --git a/src/components/master/EditContactCategory.jsx b/src/components/master/EditContactCategory.jsx new file mode 100644 index 00000000..331802d6 --- /dev/null +++ b/src/components/master/EditContactCategory.jsx @@ -0,0 +1,126 @@ +import React, { useEffect,useState } from 'react' +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { MasterRespository } from '../../repositories/MastersRepository'; +import { clearApiCacheKey } from '../../slices/apiCacheSlice'; +import { getCachedData,cacheData } from '../../slices/apiDataManager'; +import showToast from '../../services/toastService'; + + +const schema = z.object({ + name: z.string().min(1, { message: "Category name is required" }), + description: z.string().min(1, { message: "Description is required" }) + .max(255, { message: "Description cannot exceed 255 characters" }), +}); + +const EditContactCategory= ({data,onClose}) => { + + const[isLoading,setIsLoading] = useState(false) + const { + register, + handleSubmit, + formState: { errors },reset + + } = useForm({ + resolver: zodResolver(schema), + defaultValues: { + name: data?.name || "", + description:data?.description || "", + + }, + }); + + const onSubmit = (formdata) => { + setIsLoading(true) + const result = { + id:data?.id, + name: formdata?.name, + description: formdata.description, + }; + + + + MasterRespository.updateContactCategory(data?.id,result).then((resp)=>{ + setIsLoading(false) + showToast("Contact Category Updated successfully.", "success"); + const cachedData = getCachedData("Contact Category"); + if (cachedData) { + + const updatedData = cachedData.map((category) => + category.id === data?.id ? { ...category, ...resp.data } : category + ); + cacheData("Contact Category", updatedData); + } + + onClose() + }).catch((error)=>{ + showToast(error?.response?.data?.message, "error") + setIsLoading(false) + }) + + }; + const resetForm = () => { + reset({ + name: "", + description: "" + }); + setDescriptionLength(0); + } + + useEffect(()=>{ + return ()=>resetForm() + },[]) + + const [descriptionLength, setDescriptionLength] = useState(0); + const maxDescriptionLength = 255; + return (<> +
+
+ + + {errors.name &&

{errors.name.message}

} +
+
+ + +
+ {maxDescriptionLength - descriptionLength} characters left +
+ {errors.description && ( +

{errors.description.message}

+ )} +
+ +
+ + +
+ +
+ + + ) +} + +export default EditContactCategory; \ No newline at end of file From 5a701ca9d71893efef412b9b341e04978a517f3b Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sat, 24 May 2025 17:27:47 +0530 Subject: [PATCH 327/433] added EditcontactCatgorry component in modal for update --- src/components/master/MasterModal.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/master/MasterModal.jsx b/src/components/master/MasterModal.jsx index fc8209fe..fd1a0461 100644 --- a/src/components/master/MasterModal.jsx +++ b/src/components/master/MasterModal.jsx @@ -131,6 +131,9 @@ const MasterModal = ({ modaldata, closeModal }) => { )} {modaldata.modalType === "Contact Category" && ( + )} + {modaldata.modalType === "Edit-Contact Category" && ( + )} {modaldata.modalType === "Contact Tag" && ( From a965116aa520f703cfabee8ea665082cd1962a20 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sun, 25 May 2025 00:21:33 +0530 Subject: [PATCH 328/433] added schema for bucket form validation --- src/components/Directory/DirectorySchema.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/Directory/DirectorySchema.js b/src/components/Directory/DirectorySchema.js index a9743829..3d30292f 100644 --- a/src/components/Directory/DirectorySchema.js +++ b/src/components/Directory/DirectorySchema.js @@ -53,5 +53,13 @@ export const ContactSchema = z // return hasValidEmail || hasValidPhone; // }, { // message: "At least one contact (email or phone) is required", -// path: ["contactPhone"], +// path: ["contactPhone"], // }); + + +// Buckets + +export const bucketScheam = z.object( { + name: z.string().min( 1, {message: "Name is required"} ), + description:z.string().min(1,{message:"Description is required"}) +}) \ No newline at end of file From 3ff8066803894cbb8ddc830f472ac9c0fde04767 Mon Sep 17 00:00:00 2001 From: Pramod Mahajan Date: Sun, 25 May 2025 00:22:12 +0530 Subject: [PATCH 329/433] created new component for bucket managment --- src/components/Directory/ManageBucket.jsx | 226 ++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 src/components/Directory/ManageBucket.jsx diff --git a/src/components/Directory/ManageBucket.jsx b/src/components/Directory/ManageBucket.jsx new file mode 100644 index 00000000..d3a5d5c3 --- /dev/null +++ b/src/components/Directory/ManageBucket.jsx @@ -0,0 +1,226 @@ +import React, { useEffect, useState } from "react"; +import IconButton from "../common/IconButton"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { bucketScheam } from "./DirectorySchema"; +import showToast from "../../services/toastService"; +import Directory from "../../pages/Directory/Directory"; +import { DirectoryRepository } from "../../repositories/DirectoryRepository"; +import { cacheData, getCachedData } from "../../slices/apiDataManager"; +import { useBuckets } from "../../hooks/useDirectory"; + +const ManageBucket = () => +{ + const [bucketList, setBucketList] = useState([]); + + const { buckets } = useBuckets(); + const [action_bucket, setAction_bucket] = useState(false); + const [isSubmitting, setSubmitting] = useState(false); + const [selected_bucket, select_bucket] = useState(null); + + const { + register, + handleSubmit, + reset, + formState: { errors }, + } = useForm({ + resolver: zodResolver(bucketScheam), + defaultValues: { + 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"); + } + + handleBack(); + } 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 ] ); + + useEffect( () => + { + setBucketList( buckets ) + }, [ buckets ] ) + + const handleBack = () => { + select_bucket(null); + setAction_bucket(false); + setSubmitting(false); + }; + + + return ( +
+
+

Manage Buckets

+
+
+ + +
+
+ {!action_bucket ? ( +
+ + + + + + + + + + + {bucketList.map((bucket) => ( + + + + + + ))} + +
+
+ + Name +
+
+
+ + Description +
+
+
+ + Action +
+
+ {bucket.name} + + {bucket.description} + +
+ { + select_bucket(bucket); + setAction_bucket(true); + }} + > + + + +
+
+
+ ) : ( +
+
+ + + {errors.name && ( + {errors.name.message} + )} +
+
+ +