Files
schedtxt/src/components/ViewContactWizard/ViewContact.jsx
T
phoenixandphoenix 0202425a09 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>
2026-01-29 22:20:28 +00:00

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;