Closes #10 Reviewed-on: phoenix/textsender#23 Co-authored-by: phoenix <kundeng00@pm.me> Co-committed-by: phoenix <kundeng00@pm.me>
151 lines
4.6 KiB
React
151 lines
4.6 KiB
React
import { React, useState, useEffect } from 'react';
|
|
|
|
import { tokenService } from '../../services/TokenService';
|
|
import { contactApi } from '../../api/contactApi';
|
|
import styles from './ViewContact.module.css';
|
|
|
|
function ViewContact({ selectedContact, onSelectedContact }) {
|
|
const [contacts, setContacts] = useState([]);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [error, setError] = useState(null);
|
|
const [searchTerm, setSearchTerm] = useState('');
|
|
|
|
useEffect(() => {
|
|
try {
|
|
console.log('Going');
|
|
fetchContacts();
|
|
} catch (err) {
|
|
setError(err.message);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
const fetchContacts = async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
console.log('Loading contacts');
|
|
|
|
const userId = tokenService.getUserId();
|
|
const response = await contactApi.getContactsWithUserId(
|
|
userId,
|
|
tokenService.bearerToken()
|
|
);
|
|
if (!response) {
|
|
throw new Error('Failed to fetch contacts');
|
|
} else {
|
|
const data = response.data;
|
|
setContacts(data);
|
|
setError(null);
|
|
}
|
|
} catch (err) {
|
|
setError(err.message);
|
|
console.error('Error fetching contacts:', err);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const filteredContacts = contacts.filter(
|
|
(contact) =>
|
|
contact.phone_number.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
contact.first_name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
contact.last_name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
contact.nickname?.toLowerCase().includes(searchTerm.toLowerCase())
|
|
);
|
|
|
|
const formatContactDisplay = (contact) => {
|
|
const phone = contact.phone_number || 'No phone';
|
|
const name =
|
|
[contact.first_name, contact.last_name].filter(Boolean).join(' ') ||
|
|
'Unnamed';
|
|
const nickname = contact.nickname ? `"${contact.nickname}"` : '';
|
|
|
|
return `${phone} - ${name} ${nickname}`.trim();
|
|
};
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className={styles.loading}>
|
|
<div className={styles.spinner}></div>
|
|
<p>Loading contacts...</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className={styles.errorState}>
|
|
<p>Error loading contacts: {error}</p>
|
|
<button onClick={() => window.location.reload()}>Retry</button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className={styles.contactSelection}>
|
|
<div className={styles.selectionHeader}>
|
|
<div className={styles.searchBox}>
|
|
<input
|
|
type="text"
|
|
placeholder="Search by phone, name, or nickname..."
|
|
value={searchTerm}
|
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
className={styles.searchInput}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className={styles.contactsList}>
|
|
{filteredContacts.length === 0 ? (
|
|
<div className={styles.emptyState}>
|
|
<div className={styles.emptyStateIcon}>👤</div>
|
|
<p className={styles.emptyStateTitle}>No contacts found</p>
|
|
<p className={styles.emptyStateDescription}>
|
|
{searchTerm
|
|
? 'Try a different search term'
|
|
: 'No contacts available'}
|
|
</p>
|
|
</div>
|
|
) : (
|
|
filteredContacts.map((contact) => (
|
|
<div
|
|
key={contact.id}
|
|
className={`${styles.contactItem} ${
|
|
selectedContact?.id === contact.id ? styles.selected : ''
|
|
}`}
|
|
onClick={() => onSelectedContact(contact)}
|
|
>
|
|
<div className={styles.contactAvatar}>
|
|
{contact.first_name?.[0]?.toUpperCase() || '?'}
|
|
</div>
|
|
<div className={styles.contactInfo}>
|
|
<div className={styles.contactMainInfo}>
|
|
<span className={styles.contactPhone}>
|
|
{contact.phone_number}
|
|
</span>
|
|
{contact.nickname && (
|
|
<span className={styles.contactNickname}>
|
|
"{contact.nickname}"
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div className={styles.contactName}>
|
|
{[contact.first_name, contact.last_name]
|
|
.filter(Boolean)
|
|
.join(' ') || 'Unnamed Contact'}
|
|
</div>
|
|
{selectedContact?.id === contact.id && (
|
|
<div className={styles.selectedIndicator}>✓ Selected</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default ViewContact;
|