Merge pull request 'Added new field in contact create and update form' (#263) from Ashutosh_Enhancement_#632 into Issues_July_4W
Reviewed-on: #263
This commit is contained in:
commit
bc18cf9cc3
@ -123,6 +123,16 @@ const CardViewDirectory = ({
|
||||
}}
|
||||
>
|
||||
<hr className="my-0" />
|
||||
{contact.designation && (
|
||||
<ul className="list-unstyled my-1 d-flex align-items-start ms-2">
|
||||
<li className="me-2">
|
||||
<i class="fa-solid fa-id-badge ms-1"></i>
|
||||
</li>
|
||||
<li className="flex-grow-1 text-break small">
|
||||
{contact.designation}
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
{contact.contactEmails[0] && (
|
||||
<ul className="list-unstyled my-1 d-flex align-items-start ms-2">
|
||||
<li className="me-2">
|
||||
|
@ -6,6 +6,7 @@ export const ContactSchema = z
|
||||
contactCategoryId: z.string().nullable().optional(),
|
||||
address: z.string().optional(),
|
||||
description: z.string().min(1, { message: "Description is required" }),
|
||||
designation: z.string().min(1, {message:"Designation is requried"}),
|
||||
projectIds: z.array(z.string()).nullable().optional(), // min(1, "Project is required")
|
||||
contactEmails: z
|
||||
.array(
|
||||
|
@ -14,7 +14,11 @@ import useMaster, {
|
||||
} from "../../hooks/masterHook/useMaster";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { changeMaster } from "../../slices/localVariablesSlice";
|
||||
import { useBuckets, useOrganization } from "../../hooks/useDirectory";
|
||||
import {
|
||||
useBuckets,
|
||||
useDesignation,
|
||||
useOrganization,
|
||||
} from "../../hooks/useDirectory";
|
||||
import { useProjects } from "../../hooks/useProjects";
|
||||
import SelectMultiple from "../common/SelectMultiple";
|
||||
import { ContactSchema } from "./DirectorySchema";
|
||||
@ -33,8 +37,11 @@ const ManageDirectory = ({ submitContact, onCLosed }) => {
|
||||
const { contactCategory, loading: contactCategoryLoading } =
|
||||
useContactCategory();
|
||||
const { organizationList, loading: orgLoading } = useOrganization();
|
||||
const { designationList, loading: designloading } = useDesignation();
|
||||
const { contactTags, loading: Tagloading } = useContactTags();
|
||||
const [IsSubmitting, setSubmitting] = useState(false);
|
||||
const [showSuggestions,setShowSuggestions] = useState(false);
|
||||
const [filteredDesignationList, setFilteredDesignationList] = useState([]);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const methods = useForm({
|
||||
@ -45,6 +52,7 @@ const ManageDirectory = ({ submitContact, onCLosed }) => {
|
||||
contactCategoryId: null,
|
||||
address: "",
|
||||
description: "",
|
||||
designation: "",
|
||||
projectIds: [],
|
||||
contactEmails: [],
|
||||
contactPhones: [],
|
||||
@ -106,6 +114,25 @@ const ManageDirectory = ({ submitContact, onCLosed }) => {
|
||||
|
||||
const watchBucketIds = watch("bucketIds");
|
||||
|
||||
// handle logic when input of desgination is changed
|
||||
const handleDesignationChange = (e) => {
|
||||
const val = e.target.value;
|
||||
|
||||
const matches = designationList.filter((org) =>
|
||||
org.toLowerCase().includes(val.toLowerCase())
|
||||
);
|
||||
setFilteredDesignationList(matches);
|
||||
setShowSuggestions(true);
|
||||
setTimeout(() => setShowSuggestions(false), 5000);
|
||||
};
|
||||
|
||||
// handle logic when designation is selected
|
||||
const handleSelectDesignation = (val) => {
|
||||
setShowSuggestions(false);
|
||||
setValue("designation", val);
|
||||
};
|
||||
|
||||
|
||||
const toggleBucketId = (id) => {
|
||||
const updated = watchBucketIds?.includes(id)
|
||||
? watchBucketIds.filter((val) => val !== id)
|
||||
@ -168,6 +195,55 @@ const ManageDirectory = ({ submitContact, onCLosed }) => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mt-1">
|
||||
<div className="col-md-6 text-start">
|
||||
<label className="form-label">Designation</label>
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
{...register("designation")}
|
||||
onChange={handleDesignationChange}
|
||||
/>
|
||||
{showSuggestions && filteredDesignationList.length > 0 && (
|
||||
<ul
|
||||
className="list-group shadow-sm position-absolute bg-white border w-50 zindex-tooltip"
|
||||
style={{
|
||||
maxHeight: "180px",
|
||||
overflowY: "auto",
|
||||
marginTop: "2px",
|
||||
zIndex: 1000,
|
||||
borderRadius: "0px",
|
||||
}}
|
||||
>
|
||||
{filteredDesignationList.map((designation) => (
|
||||
<li
|
||||
key={designation}
|
||||
className="list-group-item list-group-item-action border-none "
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
padding: "5px 12px",
|
||||
fontSize: "14px",
|
||||
transition: "background-color 0.2s",
|
||||
}}
|
||||
onMouseDown={() => handleSelectDesignation(designation)}
|
||||
onMouseEnter={(e) =>
|
||||
(e.currentTarget.style.backgroundColor = "#f8f9fa")
|
||||
}
|
||||
onMouseLeave={(e) =>
|
||||
(e.currentTarget.style.backgroundColor = "transparent")
|
||||
}
|
||||
>
|
||||
{designation}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
{errors.designation && (
|
||||
<small className="danger-text">
|
||||
{errors.designation.message}
|
||||
</small>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mt-1">
|
||||
<div className="col-md-6">
|
||||
{emailFields.map((field, index) => (
|
||||
@ -381,13 +457,12 @@ const ManageDirectory = ({ submitContact, onCLosed }) => {
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
|
||||
</ul>
|
||||
{errors.bucketIds && (
|
||||
<small className="danger-text mt-0">
|
||||
{errors.bucketIds.message}
|
||||
</small>
|
||||
)}
|
||||
{errors.bucketIds && (
|
||||
<small className="danger-text mt-0">
|
||||
{errors.bucketIds.message}
|
||||
</small>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -47,7 +47,8 @@ const ProfileContactDirectory = ({ contact, setOpen_contact, closeModal }) => {
|
||||
<div className="d-flex flex-column text-start ms-1">
|
||||
<span className="m-0 fw-semibold">{contact?.name}</span>
|
||||
<small className="text-secondary small-text">
|
||||
{contactProfile?.tags?.map((tag) => tag.name).join(" | ")}
|
||||
{/* {contactProfile?.tags?.map((tag) => tag.name).join(" | ")} */}
|
||||
{contactProfile?.designation}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -14,7 +14,11 @@ import useMaster, {
|
||||
} from "../../hooks/masterHook/useMaster";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { changeMaster } from "../../slices/localVariablesSlice";
|
||||
import { useBuckets, useOrganization } from "../../hooks/useDirectory";
|
||||
import {
|
||||
useBuckets,
|
||||
useDesignation,
|
||||
useOrganization,
|
||||
} from "../../hooks/useDirectory";
|
||||
import { useProjects } from "../../hooks/useProjects";
|
||||
import SelectMultiple from "../common/SelectMultiple";
|
||||
import { ContactSchema } from "./DirectorySchema";
|
||||
@ -32,10 +36,13 @@ const UpdateContact = ({ submitContact, existingContact, onCLosed }) => {
|
||||
const { contactCategory, loading: contactCategoryLoading } =
|
||||
useContactCategory();
|
||||
const { contactTags, loading: Tagloading } = useContactTags();
|
||||
const [ IsSubmitting, setSubmitting ] = useState( false );
|
||||
const [IsSubmitting, setSubmitting] = useState(false);
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const {organizationList} = useOrganization()
|
||||
const { organizationList } = useOrganization();
|
||||
const { designationList } = useDesignation();
|
||||
const [showSuggestions, setShowSuggestions] = useState(false);
|
||||
const [filteredDesignationList, setFilteredDesignationList] = useState([]);
|
||||
|
||||
const methods = useForm({
|
||||
resolver: zodResolver(ContactSchema),
|
||||
@ -45,6 +52,7 @@ const UpdateContact = ({ submitContact, existingContact, onCLosed }) => {
|
||||
contactCategoryId: null,
|
||||
address: "",
|
||||
description: "",
|
||||
designation: "",
|
||||
projectIds: [],
|
||||
contactEmails: [],
|
||||
contactPhones: [],
|
||||
@ -95,6 +103,24 @@ const UpdateContact = ({ submitContact, existingContact, onCLosed }) => {
|
||||
}
|
||||
};
|
||||
|
||||
// handle logic when input of desgination is changed
|
||||
const handleDesignationChange = (e) => {
|
||||
const val = e.target.value;
|
||||
|
||||
const matches = designationList.filter((org) =>
|
||||
org.toLowerCase().includes(val.toLowerCase())
|
||||
);
|
||||
setFilteredDesignationList(matches);
|
||||
setShowSuggestions(true);
|
||||
setTimeout(() => setShowSuggestions(false), 5000);
|
||||
};
|
||||
|
||||
// handle logic when designation is selected
|
||||
const handleSelectDesignation = (val) => {
|
||||
setShowSuggestions(false);
|
||||
setValue("designation", val);
|
||||
};
|
||||
|
||||
const watchBucketIds = watch("bucketIds");
|
||||
|
||||
const toggleBucketId = (id) => {
|
||||
@ -113,33 +139,28 @@ const UpdateContact = ({ submitContact, existingContact, onCLosed }) => {
|
||||
};
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
const cleaned = {
|
||||
...data,
|
||||
contactEmails: (data.contactEmails || [])
|
||||
.filter((e) => e.emailAddress?.trim() !== "")
|
||||
.map((email, index) => {
|
||||
const existingEmail = existingContact.contactEmails?.[index];
|
||||
return existingEmail
|
||||
? { ...email, id: existingEmail.id }
|
||||
: email;
|
||||
}),
|
||||
contactPhones: (data.contactPhones || [])
|
||||
.filter((p) => p.phoneNumber?.trim() !== "")
|
||||
.map((phone, index) => {
|
||||
const existingPhone = existingContact.contactPhones?.[index];
|
||||
return existingPhone
|
||||
? { ...phone, id: existingPhone.id }
|
||||
: phone;
|
||||
}),
|
||||
};
|
||||
const cleaned = {
|
||||
...data,
|
||||
contactEmails: (data.contactEmails || [])
|
||||
.filter((e) => e.emailAddress?.trim() !== "")
|
||||
.map((email, index) => {
|
||||
const existingEmail = existingContact.contactEmails?.[index];
|
||||
return existingEmail ? { ...email, id: existingEmail.id } : email;
|
||||
}),
|
||||
contactPhones: (data.contactPhones || [])
|
||||
.filter((p) => p.phoneNumber?.trim() !== "")
|
||||
.map((phone, index) => {
|
||||
const existingPhone = existingContact.contactPhones?.[index];
|
||||
return existingPhone ? { ...phone, id: existingPhone.id } : phone;
|
||||
}),
|
||||
};
|
||||
|
||||
setSubmitting(true);
|
||||
await submitContact({ ...cleaned, id: existingContact.id });
|
||||
setSubmitting(true);
|
||||
await submitContact({ ...cleaned, id: existingContact.id });
|
||||
|
||||
setSubmitting(false);
|
||||
|
||||
};
|
||||
const orgValue = watch("organization")
|
||||
const orgValue = watch("organization");
|
||||
const handleClosed = () => {
|
||||
onCLosed();
|
||||
};
|
||||
@ -149,7 +170,7 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
typeof existingContact === "object" &&
|
||||
!Array.isArray(existingContact);
|
||||
|
||||
if (!isInitialized &&isValidContact && TagsData) {
|
||||
if (!isInitialized && isValidContact && TagsData) {
|
||||
reset({
|
||||
name: existingContact.name || "",
|
||||
organization: existingContact.organization || "",
|
||||
@ -158,24 +179,30 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
contactCategoryId: existingContact.contactCategory?.id || null,
|
||||
address: existingContact.address || "",
|
||||
description: existingContact.description || "",
|
||||
designation: existingContact.designation || "",
|
||||
projectIds: existingContact.projectIds || null,
|
||||
tags: existingContact.tags || [],
|
||||
bucketIds: existingContact.bucketIds || [],
|
||||
} );
|
||||
|
||||
if (!existingContact.contactPhones || existingContact.contactPhones.length === 0) {
|
||||
appendPhone({ label: "Office", phoneNumber: "" });
|
||||
});
|
||||
|
||||
if (
|
||||
!existingContact.contactPhones ||
|
||||
existingContact.contactPhones.length === 0
|
||||
) {
|
||||
appendPhone({ label: "Office", phoneNumber: "" });
|
||||
}
|
||||
|
||||
if (
|
||||
!existingContact.contactEmails ||
|
||||
existingContact.contactEmails.length === 0
|
||||
) {
|
||||
appendEmail({ label: "Work", emailAddress: "" });
|
||||
}
|
||||
setIsInitialized(true);
|
||||
}
|
||||
|
||||
if (!existingContact.contactEmails || existingContact.contactEmails.length === 0) {
|
||||
appendEmail({ label: "Work", emailAddress: "" });
|
||||
}
|
||||
setIsInitialized(true)
|
||||
}
|
||||
|
||||
// return()=> reset()
|
||||
}, [ existingContact, buckets, projects ] );
|
||||
|
||||
}, [existingContact, buckets, projects]);
|
||||
|
||||
return (
|
||||
<FormProvider {...methods}>
|
||||
@ -195,15 +222,14 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
<div className="col-md-6 text-start">
|
||||
<label className="form-label">Organization</label>
|
||||
<InputSuggestions
|
||||
organizationList={organizationList}
|
||||
value={getValues("organization") || ""}
|
||||
onChange={(val) => setValue("organization", val)}
|
||||
error={errors.organization?.message}
|
||||
/>
|
||||
organizationList={organizationList}
|
||||
value={getValues("organization") || ""}
|
||||
onChange={(val) => setValue("organization", val)}
|
||||
error={errors.organization?.message}
|
||||
/>
|
||||
{errors.organization && (
|
||||
<small className="danger-text">
|
||||
{errors.organization.message}
|
||||
@ -211,6 +237,55 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mt-1">
|
||||
<div className="col-md-6 text-start">
|
||||
<label className="form-label">Designation</label>
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
{...register("designation")}
|
||||
onChange={handleDesignationChange}
|
||||
/>
|
||||
{showSuggestions && filteredDesignationList.length > 0 && (
|
||||
<ul
|
||||
className="list-group shadow-sm position-absolute w-50 bg-white border zindex-tooltip"
|
||||
style={{
|
||||
maxHeight: "180px",
|
||||
overflowY: "auto",
|
||||
marginTop: "2px",
|
||||
zIndex: 1000,
|
||||
borderRadius: "0px",
|
||||
}}
|
||||
>
|
||||
{filteredDesignationList.map((designation) => (
|
||||
<li
|
||||
key={designation}
|
||||
className="list-group-item list-group-item-action border-none "
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
padding: "5px 12px",
|
||||
fontSize: "14px",
|
||||
transition: "background-color 0.2s",
|
||||
}}
|
||||
onMouseDown={() => handleSelectDesignation(designation)}
|
||||
onMouseEnter={(e) =>
|
||||
(e.currentTarget.style.backgroundColor = "#f8f9fa")
|
||||
}
|
||||
onMouseLeave={(e) =>
|
||||
(e.currentTarget.style.backgroundColor = "transparent")
|
||||
}
|
||||
>
|
||||
{designation}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
{errors.designation && (
|
||||
<small className="danger-text">
|
||||
{errors.designation.message}
|
||||
</small>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mt-1">
|
||||
<div className="col-md-6">
|
||||
{emailFields.map((field, index) => (
|
||||
@ -247,11 +322,13 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
// <button
|
||||
// type="button"
|
||||
// className="btn btn-xs btn-primary ms-1"
|
||||
|
||||
|
||||
// style={{ width: "24px", height: "24px" }}
|
||||
// >
|
||||
<i className="bx bx-plus-circle bx-xs ms-1 cursor-pointer text-primary" onClick={handleAddEmail}/>
|
||||
|
||||
<i
|
||||
className="bx bx-plus-circle bx-xs ms-1 cursor-pointer text-primary"
|
||||
onClick={handleAddEmail}
|
||||
/>
|
||||
) : (
|
||||
// <button
|
||||
// type="button"
|
||||
@ -259,8 +336,10 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
// onClick={() => removeEmail(index)}
|
||||
// style={{ width: "24px", height: "24px" }}
|
||||
// >
|
||||
<i className="bx bx-minus-circle bx-xs ms-1 cursor-pointer text-danger" onClick={() => removeEmail(index)}/>
|
||||
|
||||
<i
|
||||
className="bx bx-minus-circle bx-xs ms-1 cursor-pointer text-danger"
|
||||
onClick={() => removeEmail(index)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{errors.contactEmails?.[index]?.emailAddress && (
|
||||
@ -310,7 +389,10 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
// onClick={handleAddPhone}
|
||||
// style={{ width: "24px", height: "24px" }}
|
||||
// >
|
||||
<i className="bx bx-plus-circle bx-xs ms-1 cursor-pointer text-primary" onClick={handleAddPhone} />
|
||||
<i
|
||||
className="bx bx-plus-circle bx-xs ms-1 cursor-pointer text-primary"
|
||||
onClick={handleAddPhone}
|
||||
/>
|
||||
) : (
|
||||
// <button
|
||||
// type="button"
|
||||
@ -318,7 +400,10 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
// onClick={() => removePhone(index)}
|
||||
// style={{ width: "24px", height: "24px" }}
|
||||
// >
|
||||
<i className="bx bx-minus-circle bx-xs ms-1 cursor-pointer text-danger" onClick={() => removePhone(index)} />
|
||||
<i
|
||||
className="bx bx-minus-circle bx-xs ms-1 cursor-pointer text-danger"
|
||||
onClick={() => removePhone(index)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{errors.contactPhones?.[index]?.phoneNumber && (
|
||||
@ -348,7 +433,7 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
</option>
|
||||
) : (
|
||||
<>
|
||||
<option disabled value="">
|
||||
<option disabled value="">
|
||||
Select Category
|
||||
</option>
|
||||
{contactCategory?.map((cate) => (
|
||||
@ -387,7 +472,7 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
)}
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-md-12 mt-1 text-start">
|
||||
<div className="col-md-12 mt-1 text-start">
|
||||
<label className="form-label ">Select Label</label>
|
||||
|
||||
<ul className="d-flex flex-wrap px-1 list-unstyled mb-0">
|
||||
@ -445,7 +530,11 @@ await submitContact({ ...cleaned, id: existingContact.id });
|
||||
</div>
|
||||
|
||||
<div className="d-flex justify-content-center gap-1 py-2">
|
||||
<button className="btn btn-sm btn-primary" type="submit" disabled={IsSubmitting}>
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
type="submit"
|
||||
disabled={IsSubmitting}
|
||||
>
|
||||
{IsSubmitting ? "Please Wait..." : "Update"}
|
||||
</button>
|
||||
<button
|
||||
|
@ -177,3 +177,36 @@ export const useOrganization = () => {
|
||||
|
||||
return { organizationList, loading, error };
|
||||
};
|
||||
|
||||
export const useDesignation = () => {
|
||||
const [designationList, setDesignationList] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const fetchOrg = async () => {
|
||||
const cacheOrg = getCachedData("designation");
|
||||
if (cacheOrg?.length != 0) {
|
||||
setLoading(true);
|
||||
try {
|
||||
const resp = await DirectoryRepository.GetDesignations();
|
||||
cacheData("designation", resp.data);
|
||||
setDesignationList(resp.data);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
const msg =
|
||||
error?.response?.data?.message ||
|
||||
error?.message ||
|
||||
"Something went wrong";
|
||||
setError(msg);
|
||||
}
|
||||
} else {
|
||||
setDesignationList(cacheOrg);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchOrg();
|
||||
}, []);
|
||||
|
||||
return { designationList, loading, error };
|
||||
};
|
||||
|
@ -2,6 +2,7 @@ import { api } from "../utils/axiosClient";
|
||||
|
||||
export const DirectoryRepository = {
|
||||
GetOrganizations: () => api.get("/api/directory/organization"),
|
||||
GetDesignations: () => api.get("/api/directory/designations"),
|
||||
GetContacts: (isActive, projectId) => {
|
||||
const params = new URLSearchParams();
|
||||
params.append("active", isActive);
|
||||
|
Loading…
x
Reference in New Issue
Block a user