tsk-10: Edit Contact (#23)
Closes #10 Reviewed-on: phoenix/textsender#23 Co-authored-by: phoenix <kundeng00@pm.me> Co-committed-by: phoenix <kundeng00@pm.me>
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user