Compare commits
4 Commits
6344d015db
...
99f40a3689
| Author | SHA1 | Date |
|---|---|---|
|
|
99f40a3689 | |
|
|
3f859a42de | |
|
|
95d4a5ccff | |
|
|
bdaa3dc263 |
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react';
|
||||||
import { ContactTypeObj } from './RecipientAddCard'
|
import { ContactTypeObj } from './RecipientAddCard';
|
||||||
import { Recipient } from '../../../store/slices/selectedRecipientsSlice';
|
import { Recipient } from '../../../store/slices/selectedRecipientsSlice';
|
||||||
|
|
||||||
interface ContactDataProps {
|
interface ContactDataProps {
|
||||||
|
|
@ -10,66 +10,127 @@ interface ContactDataProps {
|
||||||
openContactModal: boolean;
|
openContactModal: boolean;
|
||||||
setOpenContactModal: React.Dispatch<React.SetStateAction<boolean>>;
|
setOpenContactModal: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
setRecipients: React.Dispatch<React.SetStateAction<Recipient>>;
|
setRecipients: React.Dispatch<React.SetStateAction<Recipient>>;
|
||||||
|
topRef?: React.RefObject<HTMLDivElement>;
|
||||||
|
setManualFocusTriggered: React.Dispatch<React.SetStateAction<boolean>>
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContactModal: React.FC<ContactDataProps> = ({ contactData, inpFirstName, inpLastName, inpEmail, openContactModal, setRecipients, setOpenContactModal }) => {
|
// Helper function to highlight matching text
|
||||||
|
const highlightText = (text: string, searchTerm: string, shouldHighlight: boolean) => {
|
||||||
|
if (!shouldHighlight || !searchTerm.trim()) {
|
||||||
|
return <span>{text}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchTermLower = searchTerm.toLowerCase();
|
||||||
|
const textLower = text.toLowerCase();
|
||||||
|
const index = textLower.indexOf(searchTermLower);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
return <span>{text}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const beforeMatch = text.slice(0, index);
|
||||||
|
const match = text.slice(index, index + searchTerm.length);
|
||||||
|
const afterMatch = text.slice(index + searchTerm.length);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{beforeMatch}
|
||||||
|
<span className="bg-yellow-200 font-medium">{match}</span>
|
||||||
|
{afterMatch}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ContactModal: React.FC<ContactDataProps> = ({
|
||||||
|
contactData,
|
||||||
|
inpFirstName,
|
||||||
|
inpLastName,
|
||||||
|
inpEmail,
|
||||||
|
openContactModal,
|
||||||
|
setRecipients,
|
||||||
|
setOpenContactModal ,
|
||||||
|
setManualFocusTriggered
|
||||||
|
}) => {
|
||||||
const handleContactClick = (indx: number) => {
|
const handleContactClick = (indx: number) => {
|
||||||
if (!contactData.contacts.length) return;
|
if (!contactData.contacts.length) return;
|
||||||
|
|
||||||
setRecipients((prev) => {
|
setRecipients((prev) => ({
|
||||||
return {
|
...prev, // Preserve other properties like uuid, newOne, etc.
|
||||||
firstName: contactData.contacts[indx].firstName,
|
firstName: contactData.contacts[indx].firstName,
|
||||||
lastName: contactData.contacts[indx].lastName,
|
lastName: contactData.contacts[indx].lastName,
|
||||||
email: contactData.contacts[indx].email,
|
email: contactData.contacts[indx].email,
|
||||||
type: prev.type
|
}));
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setOpenContactModal(false);
|
setOpenContactModal(false);
|
||||||
|
setManualFocusTriggered(false)
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = (event: React.KeyboardEvent, indx: number) => {
|
||||||
|
if (event.key === 'Enter' || event.key === ' ') {
|
||||||
|
event.preventDefault();
|
||||||
|
handleContactClick(indx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!openContactModal || !contactData.contacts.length) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="max-h-60 overflow-y-auto">
|
||||||
{
|
{contactData.contacts.map((contact, indx) => (
|
||||||
contactData.contacts.map((recipient, indx) => {
|
<div
|
||||||
return <div
|
key={contact.id || indx}
|
||||||
tabIndex={1}
|
tabIndex={0}
|
||||||
onClick={() => handleContactClick(indx)}
|
onMouseDown={() => handleContactClick(indx)}
|
||||||
style={{display: `${openContactModal ? "flex" : "none"}`}}
|
onKeyDown={(e) => handleKeyDown(e, indx)}
|
||||||
className='flex items-center px-3 py-2 cursor-pointer hover:bg-slate-100 transiton-all w-full'>
|
className="flex items-center px-3 py-2 cursor-pointer hover:bg-gray-50 transition-colors duration-150 focus:bg-gray-100 focus:outline-none rounded-md"
|
||||||
<div
|
role="button"
|
||||||
key={indx}
|
aria-label={`Select contact ${contact.firstName} ${contact.lastName}`}
|
||||||
className='w-8 h-8 bg-gray-200 rounded-full'></div>
|
>
|
||||||
<div className='ml-2'>
|
{/* Avatar */}
|
||||||
<div className='flex gap-1.5'>
|
<div className="w-8 h-8 bg-[var(--primaryLight2)] rounded-full flex items-center justify-center text-white text-sm font-medium">
|
||||||
<p className='text-sm font-semibold'
|
{(contact.firstName?.[0] || '') + (contact.lastName?.[0] || '')}
|
||||||
style={{ background: `${contactData.type === "firstName" ? `linear-gradient(to right, yellow 0ch, yellow ${inpFirstName.length}ch, white ${inpFirstName.length}ch, white 100%)` : "white"}` }}
|
</div>
|
||||||
>
|
|
||||||
{recipient.firstName}
|
{/* Contact Info */}
|
||||||
</p>
|
<div className="ml-3 flex-1 min-w-0">
|
||||||
<p
|
<div className="flex gap-1.5 items-center">
|
||||||
style={{ background: `${contactData.type === "lastName" ? `linear-gradient(to right, yellow 0ch, yellow ${inpLastName.length}ch, white ${inpLastName.length}ch, white 100%)` : "white"}` }}
|
<p className="text-sm font-semibold text-gray-900 truncate">
|
||||||
className='text-sm font-semibold'>
|
{highlightText(
|
||||||
{recipient.lastName}
|
contact.firstName,
|
||||||
</p>
|
inpFirstName,
|
||||||
</div>
|
contactData.type === "firstName"
|
||||||
<p className='text-xs text-gray-500'
|
)}
|
||||||
style={{ background: `${contactData.type === "email" ? `linear-gradient(to right, yellow 0ch, yellow ${inpEmail.length}ch, white ${inpEmail.length}ch, white 100%)` : "white"}` }}
|
</p>
|
||||||
>
|
<p className="text-sm font-semibold text-gray-900 truncate">
|
||||||
{recipient.email}
|
{highlightText(
|
||||||
|
contact.lastName,
|
||||||
|
inpLastName,
|
||||||
|
contactData.type === "lastName"
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-xs text-gray-500 truncate">
|
||||||
|
{highlightText(
|
||||||
|
contact.email,
|
||||||
|
inpEmail,
|
||||||
|
contactData.type === "email"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
})
|
</div>
|
||||||
}
|
))}
|
||||||
</>
|
|
||||||
)
|
{/* No results message */}
|
||||||
}
|
{contactData.contacts.length === 0 && (
|
||||||
|
<div className="px-3 py-4 text-center text-gray-500 text-sm">
|
||||||
|
No contacts found
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// return <p
|
export default ContactModal;
|
||||||
// key={indx}
|
|
||||||
// className='w-full hover:bg-slate-100 cursor-pointer px-2 py-1'>
|
|
||||||
// {val.firstName}
|
|
||||||
// </p>
|
|
||||||
export default ContactModal
|
|
||||||
|
|
@ -6,7 +6,7 @@ import SelectedDocuments from "./SelectedDocuments";
|
||||||
import api from "../../../config/api";
|
import api from "../../../config/api";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { RootState } from "../../../store/store";
|
import { RootState } from "../../../store/store";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { setImages } from "../../../store/slices/selectedDocsSlice";
|
import { setImages } from "../../../store/slices/selectedDocsSlice";
|
||||||
import { addRecipient, addRecipientAsync, fetchRecipients, recipientType, resetRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE, updateRecipientIds } from "../../../store/slices/selectedRecipientsSlice";
|
import { addRecipient, addRecipientAsync, fetchRecipients, recipientType, resetRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE, updateRecipientIds } from "../../../store/slices/selectedRecipientsSlice";
|
||||||
|
|
@ -39,10 +39,10 @@ const ProcessStepOne = () => {
|
||||||
dispatch(toggleToTemplates());
|
dispatch(toggleToTemplates());
|
||||||
}
|
}
|
||||||
|
|
||||||
const [editCase , setEditCase] = useState<string>("")
|
const [editCase, setEditCase] = useState<string>("")
|
||||||
const [editRecipient , setEditRecipient] = useState({})
|
const [editRecipient, setEditRecipient] = useState({})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// if (!accountId || !transactionId) {
|
// if (!accountId || !transactionId) {
|
||||||
// window.location.href = "/";
|
// window.location.href = "/";
|
||||||
|
|
@ -55,6 +55,9 @@ const ProcessStepOne = () => {
|
||||||
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
|
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
|
||||||
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients)
|
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients)
|
||||||
const userData = useSelector((state: RootState) => state.user)
|
const userData = useSelector((state: RootState) => state.user)
|
||||||
|
// ProcessStepOne.tsx
|
||||||
|
const topRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
// const userData = useSelector((state: RootState) => state.user)
|
// const userData = useSelector((state: RootState) => state.user)
|
||||||
|
|
||||||
const [transactionDetails, setTransactionDetails] = useState<any>([]);
|
const [transactionDetails, setTransactionDetails] = useState<any>([]);
|
||||||
|
|
@ -62,24 +65,10 @@ const ProcessStepOne = () => {
|
||||||
console.log("selectedRecipients", selectedRecipients)
|
console.log("selectedRecipients", selectedRecipients)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
<<<<<<< HEAD
|
|
||||||
// Fetching Transaction Details - START
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
const res = await api.get(`/api/accounts/${accountId}/transactions/${transactionId}`);
|
|
||||||
setTransactionDetails(res.data)
|
|
||||||
} catch (e) {
|
|
||||||
console.log("Error - transactions details")
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
// Fetching Transaction Details - END
|
|
||||||
|
|
||||||
=======
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const res = await api.get(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`)
|
const res = await api.get(`/api/accounts/${accountId}/${transactionTemplateToggle}/${transactionId}`)
|
||||||
console.log(res.data);
|
console.log(res.data);
|
||||||
})()
|
})()
|
||||||
>>>>>>> origin/main
|
|
||||||
|
|
||||||
selectedRecipients.length === 0 && dispatch(fetchRecipients({ accountId, transactionId }));
|
selectedRecipients.length === 0 && dispatch(fetchRecipients({ accountId, transactionId }));
|
||||||
selectedDocuments.length === 0 && dispatch(fetchDocuments({ accountId, transactionId }));
|
selectedDocuments.length === 0 && dispatch(fetchDocuments({ accountId, transactionId }));
|
||||||
|
|
@ -177,7 +166,7 @@ const ProcessStepOne = () => {
|
||||||
language: "en",
|
language: "en",
|
||||||
name: `${recipient.firstName} ${recipient.lastName}`,
|
name: `${recipient.firstName} ${recipient.lastName}`,
|
||||||
roleName: recipient.roleName,
|
roleName: recipient.roleName,
|
||||||
allowSenderToEdit: recipient.allowSenderToEdit ,
|
allowSenderToEdit: recipient.allowSenderToEdit,
|
||||||
allowSenderToDelete: recipient.allowSenderToDelete,
|
allowSenderToDelete: recipient.allowSenderToDelete,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -348,9 +337,17 @@ const ProcessStepOne = () => {
|
||||||
{
|
{
|
||||||
signType !== SignTypeEnum.selfSign &&
|
signType !== SignTypeEnum.selfSign &&
|
||||||
<div className="flex flex-col gap-4 align-start">
|
<div className="flex flex-col gap-4 align-start">
|
||||||
<RecipientCards transactionTemplateToggle={transactionTemplateToggle} setEditRecipient={setEditRecipient} setEditCase={setEditCase}/>
|
<RecipientCards transactionTemplateToggle={transactionTemplateToggle} setEditRecipient={setEditRecipient} setEditCase={setEditCase} />
|
||||||
{/* {signType !== "self-sign" && <RecipientAddCard />} */}
|
{/* {signType !== "self-sign" && <RecipientAddCard />} */}
|
||||||
<RecipientAddCard transactionTemplateToggle={transactionTemplateToggle} editRecipient={editRecipient} editCase={editCase} setEditRecipient={setEditRecipient} setEditCase={setEditCase}/>
|
|
||||||
|
<div
|
||||||
|
ref={topRef}
|
||||||
|
className={`relative w-full bg-[var(--mainViewBg)] ${globalLoader.isLoading ? 'pointer-events-none h-screen overflow-hidden' : ''}`}
|
||||||
|
>
|
||||||
|
|
||||||
|
<RecipientAddCard transactionTemplateToggle={transactionTemplateToggle} editRecipient={editRecipient} editCase={editCase} setEditRecipient={setEditRecipient} setEditCase={setEditCase} topRef={topRef} />
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,11 @@ export interface ContactTypeObj {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,setEditRecipient, setEditCase }) => {
|
const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase, setEditRecipient, setEditCase, topRef }) => {
|
||||||
|
const [activeAccountId, setActiveAccountId] = useState<string | null>(
|
||||||
|
JSON.parse(sessionStorage.getItem('activeAccountId') || 'null')
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
const [contactData, setContactData] = useState<ContactTypeObj>({ type: "", contacts: [] });
|
const [contactData, setContactData] = useState<ContactTypeObj>({ type: "", contacts: [] });
|
||||||
const [openContactModal, setOpenContactModal] = useState(false);
|
const [openContactModal, setOpenContactModal] = useState(false);
|
||||||
|
|
@ -32,11 +36,31 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
const formRef = useRef<HTMLFormElement | null>(null);
|
const formRef = useRef<HTMLFormElement | null>(null);
|
||||||
|
|
||||||
const [recipient, setRecipients] = useState<Recipient>({ uuid:null, newOne:false, firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", roleName: "", type: recipientType.SIGNER, allowSenderToEdit: false, allowSenderToDelete: false });
|
const [recipient, setRecipients] = useState<Recipient>({ uuid: null, newOne: false, firstName: "", lastName: "", email: "", hostName: "", hostEmail: "", roleName: "", type: recipientType.SIGNER, allowSenderToEdit: false, allowSenderToDelete: false });
|
||||||
|
|
||||||
const [showAdvanceSettings, setShowAdvanceSettings] = useState(false)
|
const [showAdvanceSettings, setShowAdvanceSettings] = useState(false)
|
||||||
|
|
||||||
|
const [cachedContacts, setCachedContacts] = useState<ContactType[]>([]);
|
||||||
|
const [filteredContacts, setFilteredContacts] = useState<ContactType[]>([]);
|
||||||
|
const [activeField, setActiveField] = useState<"firstName" | "lastName" | "email" | null>(null);
|
||||||
|
const [hasFetchedInitial, setHasFetchedInitial] = useState(false);
|
||||||
|
const [lastQuery, setLastQuery] = useState<string | null>(null);
|
||||||
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const [manualFocusTriggered, setManualFocusTriggered] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
console.log(recipient)
|
console.log(recipient)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (openContactModal) {
|
||||||
|
if (topRef?.current) {
|
||||||
|
topRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
} else {
|
||||||
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [openContactModal]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editCase === "Edit" && editRecipient) {
|
if (editCase === "Edit" && editRecipient) {
|
||||||
setRecipients({
|
setRecipients({
|
||||||
|
|
@ -51,56 +75,55 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
allowSenderToEdit: editRecipient.allowSenderToEdit ?? false,
|
allowSenderToEdit: editRecipient.allowSenderToEdit ?? false,
|
||||||
allowSenderToDelete: editRecipient.allowSenderToDelete ?? false,
|
allowSenderToDelete: editRecipient.allowSenderToDelete ?? false,
|
||||||
uuid: String(editRecipient?.id ?? editRecipient?.uuid ?? ''),
|
uuid: String(editRecipient?.id ?? editRecipient?.uuid ?? ''),
|
||||||
newOne:editRecipient.newOne ?? false
|
newOne: editRecipient.newOne ?? false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [editCase, editRecipient]);
|
}, [editCase, editRecipient]);
|
||||||
|
|
||||||
|
|
||||||
const handleRecipientAdd = (e: React.FormEvent<HTMLFormElement>) => {
|
const handleRecipientAdd = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
console.log("recipientrecipient",recipient)
|
console.log("recipientrecipient", recipient)
|
||||||
|
|
||||||
if (!recipient.lastName || !recipient.email || !recipient.type) return;
|
if (!recipient.lastName || !recipient.email || !recipient.type) return;
|
||||||
|
|
||||||
// ✅ Email format validation
|
// ✅ Email format validation
|
||||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
if (!emailRegex.test(recipient.email)) {
|
if (!emailRegex.test(recipient.email)) {
|
||||||
console.log("Invalid email format");
|
console.log("Invalid email format");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editCase === 'Edit') {
|
if (editCase === 'Edit') {
|
||||||
dispatch(updateRecipient(recipient));
|
dispatch(updateRecipient(recipient));
|
||||||
} else {
|
} else {
|
||||||
dispatch(addRecipient({
|
dispatch(addRecipient({
|
||||||
...recipient,
|
...recipient,
|
||||||
uuid:uuidv4(),
|
uuid: uuidv4(),
|
||||||
newOne:true
|
newOne: true
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Reset state and form
|
setRecipients({
|
||||||
setRecipients({
|
firstName: "",
|
||||||
firstName: "",
|
lastName: "",
|
||||||
lastName: "",
|
email: "",
|
||||||
email: "",
|
hostName: "",
|
||||||
hostName: "",
|
hostEmail: "",
|
||||||
hostEmail: "",
|
roleName: "",
|
||||||
roleName: "",
|
type: recipientType.SIGNER,
|
||||||
type: recipientType.SIGNER,
|
allowSenderToEdit: false,
|
||||||
allowSenderToEdit: false,
|
allowSenderToDelete: false,
|
||||||
allowSenderToDelete: false,
|
uuid: "",
|
||||||
uuid:"",
|
newOne: false
|
||||||
newOne:false
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// setRecipients(null),
|
// setRecipients(null),
|
||||||
setEditCase("")
|
setEditCase("")
|
||||||
|
setManualFocusTriggered(false)
|
||||||
formRef.current?.reset();
|
formRef.current?.reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -155,7 +178,97 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
}, 350)
|
}, 350)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const handleFocus = async (field: "firstName" | "lastName" | "email") => {
|
||||||
|
setManualFocusTriggered(true);
|
||||||
|
setOpenContactModal(true);
|
||||||
|
setActiveField(field);
|
||||||
|
setLastQuery(null); // Reset to allow fresh input fetch if needed
|
||||||
|
|
||||||
|
if (!hasFetchedInitial) {
|
||||||
|
try {
|
||||||
|
const res = await api.get(`/api/accounts/${activeAccountId}/contacts?pageSize=10`);
|
||||||
|
const contacts = res.data?.content || [];
|
||||||
|
setCachedContacts(contacts);
|
||||||
|
setFilteredContacts(contacts);
|
||||||
|
setContactData({ type: field, contacts });
|
||||||
|
setHasFetchedInitial(true);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Initial contact fetch failed:", err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setFilteredContacts(cachedContacts);
|
||||||
|
setContactData({ type: field, contacts: cachedContacts });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
useDebouncedEffect(() => {
|
||||||
|
console.log("useDebouncedEffect")
|
||||||
|
if (!activeField || !manualFocusTriggered) return;
|
||||||
|
|
||||||
|
const inputValue = recipient[activeField].trim().toLowerCase();
|
||||||
|
|
||||||
|
if (inputValue.length === 0) {
|
||||||
|
setFilteredContacts(cachedContacts);
|
||||||
|
setContactData({ type: activeField, contacts: cachedContacts });
|
||||||
|
setOpenContactModal(true); // Keep showing modal with all options
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local match
|
||||||
|
const localMatches = cachedContacts.filter(contact =>
|
||||||
|
contact[activeField]?.toLowerCase().includes(inputValue)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (localMatches.length > 0) {
|
||||||
|
setFilteredContacts(localMatches);
|
||||||
|
setContactData({ type: activeField, contacts: localMatches });
|
||||||
|
setOpenContactModal(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastQuery !== inputValue) {
|
||||||
|
setLastQuery(inputValue);
|
||||||
|
api
|
||||||
|
.get(`/api/accounts/${activeAccountId}/contacts?pageSize=5&${activeField}=${inputValue}`)
|
||||||
|
.then((res) => {
|
||||||
|
const fetched = res.data?.content || [];
|
||||||
|
setFilteredContacts(fetched);
|
||||||
|
setContactData({ type: activeField, contacts: fetched });
|
||||||
|
setOpenContactModal(true);
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
|
}, [recipient.firstName, recipient.lastName, recipient.email], 300);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (e: MouseEvent | FocusEvent) => {
|
||||||
|
if (
|
||||||
|
containerRef.current &&
|
||||||
|
!containerRef.current.contains(e.target as Node)
|
||||||
|
) {
|
||||||
|
setOpenContactModal(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("mousedown", handleClickOutside);
|
||||||
|
document.addEventListener("focusin", handleClickOutside); // Also handles tabbing out
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousedown", handleClickOutside);
|
||||||
|
document.removeEventListener("focusin", handleClickOutside);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div ref={containerRef}>
|
||||||
<form
|
<form
|
||||||
ref={formRef}
|
ref={formRef}
|
||||||
onSubmit={handleRecipientAdd}
|
onSubmit={handleRecipientAdd}
|
||||||
|
|
@ -291,7 +404,7 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
<div className="grid grid-cols-12 gap-4 mt-3">
|
<div className="grid grid-cols-12 gap-4 mt-3">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
onFocus={() => setOpenContactModal(true)}
|
onFocus={() => handleFocus("firstName")}
|
||||||
onBlur={handleContactModalBlur}
|
onBlur={handleContactModalBlur}
|
||||||
value={recipient.firstName}
|
value={recipient.firstName}
|
||||||
placeholder="First Name"
|
placeholder="First Name"
|
||||||
|
|
@ -301,7 +414,7 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
<input
|
<input
|
||||||
required
|
required
|
||||||
type="text"
|
type="text"
|
||||||
onFocus={() => setOpenContactModal(true)}
|
onFocus={() => handleFocus("lastName")}
|
||||||
onBlur={handleContactModalBlur}
|
onBlur={handleContactModalBlur}
|
||||||
value={recipient.lastName}
|
value={recipient.lastName}
|
||||||
placeholder="Last Name"
|
placeholder="Last Name"
|
||||||
|
|
@ -311,7 +424,7 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
<input
|
<input
|
||||||
required
|
required
|
||||||
type="email"
|
type="email"
|
||||||
onFocus={() => setOpenContactModal(true)}
|
onFocus={() => handleFocus("email")}
|
||||||
onBlur={handleContactModalBlur}
|
onBlur={handleContactModalBlur}
|
||||||
value={recipient.email}
|
value={recipient.email}
|
||||||
placeholder="Recipient Email"
|
placeholder="Recipient Email"
|
||||||
|
|
@ -345,8 +458,8 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
{/* Contact Modal */}
|
{/* Contact Modal */}
|
||||||
{contactData.contacts.length > 0 && openContactModal && (
|
{openContactModal && contactData.contacts.length > 0 && (
|
||||||
<div className="absolute z-50 mt-1 w-full max-w-2xl bg-white shadow-lg rounded-md p-3 border border-gray-100">
|
<div className="absolute z-50 mt-1 w-2/3 max-h-[50vh] bg-white shadow-lg rounded-md p-3 border border-gray-100 overflow-y-auto no-scrollbar">
|
||||||
<ContactModal
|
<ContactModal
|
||||||
contactData={contactData}
|
contactData={contactData}
|
||||||
inpFirstName={recipient.firstName}
|
inpFirstName={recipient.firstName}
|
||||||
|
|
@ -355,10 +468,14 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
openContactModal={openContactModal}
|
openContactModal={openContactModal}
|
||||||
setRecipients={setRecipients}
|
setRecipients={setRecipients}
|
||||||
setOpenContactModal={setOpenContactModal}
|
setOpenContactModal={setOpenContactModal}
|
||||||
|
topRef={topRef}
|
||||||
|
setManualFocusTriggered={setManualFocusTriggered}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Signing options + Add button in one line */}
|
{/* Signing options + Add button in one line */}
|
||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
||||||
<div className="flex flex-wrap gap-6">
|
<div className="flex flex-wrap gap-6">
|
||||||
|
|
@ -385,11 +502,11 @@ const RecipientAddCard = ({ transactionTemplateToggle, editRecipient, editCase ,
|
||||||
type="submit"
|
type="submit"
|
||||||
className="inline-flex items-center gap-2 bg-[var(--primary)] hover:bg-[var(--primaryLight2)] text-white text-sm font-semibold px-5 py-2 rounded-md transition"
|
className="inline-flex items-center gap-2 bg-[var(--primary)] hover:bg-[var(--primaryLight2)] text-white text-sm font-semibold px-5 py-2 rounded-md transition"
|
||||||
>
|
>
|
||||||
<FaPlus size={14} /> {editCase === "Edit" ? "Edit Recipient" : "Add Recipient"}
|
<FaPlus size={14} /> {editCase === "Edit" ? "Edit Recipient" : "Add Recipient"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,15 +59,6 @@ const RecipientCards = ({ setEditRecipient, setEditCase, transactionTemplateTogg
|
||||||
|
|
||||||
const handleRemoveRecipient = (recipient) => {
|
const handleRemoveRecipient = (recipient) => {
|
||||||
if (!accountId || !transactionId) return;
|
if (!accountId || !transactionId) return;
|
||||||
<<<<<<< HEAD
|
|
||||||
// dispatch(removeSelectedRecipient(recipient))
|
|
||||||
dispatch(removeRecipientAsync([{
|
|
||||||
recipientId: recipient.id,
|
|
||||||
email: recipient.email,
|
|
||||||
accountId: accountId,
|
|
||||||
transactionId: transactionId,
|
|
||||||
}]))
|
|
||||||
=======
|
|
||||||
if (recipient.uuid) {
|
if (recipient.uuid) {
|
||||||
dispatch(removeSelectedRecipient(recipient))
|
dispatch(removeSelectedRecipient(recipient))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -78,7 +69,6 @@ const RecipientCards = ({ setEditRecipient, setEditCase, transactionTemplateTogg
|
||||||
transactionId: transactionId,
|
transactionId: transactionId,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
>>>>>>> origin/main
|
|
||||||
if (recipients.length === 2) {
|
if (recipients.length === 2) {
|
||||||
let leftRecipient = recipients.filter((obj) => {
|
let leftRecipient = recipients.filter((obj) => {
|
||||||
if ("id" in obj && "id" in recipient) {
|
if ("id" in obj && "id" in recipient) {
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ const DirectionalControls: React.FC<DirectionalControlsProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Center Panel */}
|
{/* Center Panel */}
|
||||||
<div className="relative flex items-center justify-center bg-white rounded-lg px-1 py-2 w-24">
|
<div className="relative flex items-center justify-center bg-white rounded-lg px-1 py-2 w-24 ">
|
||||||
{/* Floating dimensions tooltip */}
|
{/* Floating dimensions tooltip */}
|
||||||
<div className="absolute -top-2 left-1/2 -translate-x-1/2 px-2 py-[1px] text-[10px] font-mono text-black z-10 rounded-sm flex items-center gap-[2px]">
|
<div className="absolute -top-2 left-1/2 -translate-x-1/2 px-2 py-[1px] text-[10px] font-mono text-black z-10 rounded-sm flex items-center gap-[2px]">
|
||||||
<input
|
<input
|
||||||
|
|
@ -167,7 +167,7 @@ const DirectionalControls: React.FC<DirectionalControlsProps> = ({
|
||||||
onBlur={() => handlePositionInputBlur('left')}
|
onBlur={() => handlePositionInputBlur('left')}
|
||||||
onKeyPress={(e) => handleKeyPress(e, 'left')}
|
onKeyPress={(e) => handleKeyPress(e, 'left')}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className="w-[40px] h-[16px] text-[10px] text-center border-none focus:outline-none text-black font-mono"
|
className="w-[40px] h-[16px] text-[10px] text-center border-none focus:outline-none text-black font-mono"
|
||||||
/>
|
/>
|
||||||
<span className="mx-[1px]">×</span>
|
<span className="mx-[1px]">×</span>
|
||||||
<input
|
<input
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue