136 lines
5.0 KiB
TypeScript
136 lines
5.0 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { ContactTypeObj } from './RecipientAddCard';
|
|
import { Recipient } from '../../../store/slices/selectedRecipientsSlice';
|
|
|
|
interface ContactDataProps {
|
|
contactData: ContactTypeObj;
|
|
inpFirstName: string;
|
|
inpLastName: string;
|
|
inpEmail: string;
|
|
openContactModal: boolean;
|
|
setOpenContactModal: React.Dispatch<React.SetStateAction<boolean>>;
|
|
setRecipients: React.Dispatch<React.SetStateAction<Recipient>>;
|
|
topRef?: React.RefObject<HTMLDivElement>;
|
|
setManualFocusTriggered: React.Dispatch<React.SetStateAction<boolean>>
|
|
}
|
|
|
|
// 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) => {
|
|
if (!contactData.contacts.length) return;
|
|
|
|
setRecipients((prev) => ({
|
|
...prev, // Preserve other properties like uuid, newOne, etc.
|
|
firstName: contactData.contacts[indx].firstName,
|
|
lastName: contactData.contacts[indx].lastName,
|
|
email: contactData.contacts[indx].email,
|
|
}));
|
|
|
|
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 (
|
|
<div className="max-h-60 overflow-y-auto">
|
|
{contactData.contacts.map((contact, indx) => (
|
|
<div
|
|
key={contact.id || indx}
|
|
tabIndex={0}
|
|
onMouseDown={() => handleContactClick(indx)}
|
|
onKeyDown={(e) => handleKeyDown(e, indx)}
|
|
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"
|
|
role="button"
|
|
aria-label={`Select contact ${contact.firstName} ${contact.lastName}`}
|
|
>
|
|
{/* Avatar */}
|
|
<div className="w-8 h-8 bg-[var(--primaryLight2)] rounded-full flex items-center justify-center text-white text-sm font-medium">
|
|
{(contact.firstName?.[0] || '') + (contact.lastName?.[0] || '')}
|
|
</div>
|
|
|
|
{/* Contact Info */}
|
|
<div className="ml-3 flex-1 min-w-0">
|
|
<div className="flex gap-1.5 items-center">
|
|
<p className="text-sm font-semibold text-gray-900 truncate">
|
|
{highlightText(
|
|
contact.firstName,
|
|
inpFirstName,
|
|
contactData.type === "firstName"
|
|
)}
|
|
</p>
|
|
<p className="text-sm font-semibold text-gray-900 truncate">
|
|
{highlightText(
|
|
contact.lastName,
|
|
inpLastName,
|
|
contactData.type === "lastName"
|
|
)}
|
|
</p>
|
|
</div>
|
|
<p className="text-xs text-gray-500 truncate">
|
|
{highlightText(
|
|
contact.email,
|
|
inpEmail,
|
|
contactData.type === "email"
|
|
)}
|
|
</p>
|
|
</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>
|
|
);
|
|
};
|
|
|
|
export default ContactModal; |