import { Component, Input, OnInit, OnChanges, SimpleChanges, Output, EventEmitter, ChangeDetectorRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { UserService } from '../../../../services/user.service'; import { AppointmentService } from '../../../../services/appointment.service'; import { ModalService } from '../../../../services/modal.service'; import { ChatService } from '../../../../services/chat.service'; import { LoggerService } from '../../../../services/logger.service'; @Component({ selector: 'app-patients', standalone: true, imports: [CommonModule, FormsModule], templateUrl: './patients.component.html', styleUrl: './patients.component.scss' }) export class PatientsComponent implements OnInit, OnChanges { @Input() patients: any[] = []; // Fallback from appointments @Output() patientRemoved = new EventEmitter(); @Output() patientSelected = new EventEmitter(); @Output() createAppointmentRequested = new EventEmitter(); @Output() startChatRequested = new EventEmitter(); loading = false; loadingPatients = false; searchTerm: string = ''; filteredPatients: any[] = []; allPatients: any[] = []; // All patients from system constructor( private userService: UserService, private appointmentService: AppointmentService, private modalService: ModalService, private chatService: ChatService, private cdr: ChangeDetectorRef, private logger: LoggerService ) {} async ngOnInit() { this.logger.debug('[PatientsComponent] ngOnInit - patients:', this.patients.length); // Load all patients from system await this.loadAllPatients(); // Initialize filtered patients this.applySearch(); } async loadAllPatients() { try { this.loadingPatients = true; const patients = await this.userService.getAllPatients(); this.logger.debug('[PatientsComponent] Loaded all patients from system:', patients.length); // Map PatientProfile to the format we need // PatientProfile has firstName, lastName, email, phoneNumber directly (not nested in user) // Now also includes userId for chat functionality this.allPatients = patients.map(p => ({ id: p.id, userId: p.userId, // Include userId for chat functionality firstName: p.firstName || '', lastName: p.lastName || '', email: p.email || '', phoneNumber: p.phoneNumber || '', displayName: `${p.firstName || ''} ${p.lastName || ''}`.trim(), bloodType: p.bloodType, allergies: p.allergies || [] })); // Merge with patients from appointments (if any) to avoid duplicates const existingIds = new Set(this.allPatients.map(p => p.id)); const newPatients = this.patients.filter(p => p.id && !existingIds.has(p.id)); this.allPatients = [...this.allPatients, ...newPatients]; // Update patients array to include all this.patients = this.allPatients; this.applySearch(); } catch (error: any) { this.logger.error('[PatientsComponent] Error loading all patients:', error); // Fallback to patients from input this.patients = this.patients || []; this.applySearch(); } finally { this.loadingPatients = false; this.cdr.detectChanges(); } } ngOnChanges(changes: SimpleChanges) { if (changes['patients'] && !changes['patients'].firstChange) { this.logger.debug('[PatientsComponent] ngOnChanges - patients changed:', { previousValue: changes['patients'].previousValue?.length, currentValue: changes['patients'].currentValue?.length }); // Merge with all patients if needed if (this.allPatients.length > 0) { const existingIds = new Set(this.allPatients.map(p => p.id)); const newPatients = this.patients.filter(p => p.id && !existingIds.has(p.id)); this.allPatients = [...this.allPatients, ...newPatients]; this.patients = this.allPatients; } this.applySearch(); } } applySearch() { this.logger.debug('[PatientsComponent] applySearch called', { searchTerm: this.searchTerm, patientsCount: this.patients.length, filteredCount: this.filteredPatients.length }); // If no search term, show all patients if (!this.searchTerm || this.searchTerm.trim() === '') { this.filteredPatients = [...this.patients]; this.logger.debug('[PatientsComponent] No search term, showing all patients:', this.filteredPatients.length); return; } // Filter patients based on search term const search = this.searchTerm.toLowerCase().trim(); this.logger.debug('[PatientsComponent] Filtering with search term:', search); this.filteredPatients = this.patients.filter(patient => { // Search by name (first name + last name) const fullName = `${patient.firstName || ''} ${patient.lastName || ''}`.toLowerCase(); const nameMatch = fullName.includes(search); // Search by email const emailMatch = patient.email && patient.email.toLowerCase().includes(search); // Search by phone number const phoneMatch = patient.phoneNumber && patient.phoneNumber.toLowerCase().includes(search); const matches = nameMatch || emailMatch || phoneMatch; if (matches) { this.logger.debug('[PatientsComponent] Patient matches:', patient.firstName, patient.lastName); } return matches; }); this.logger.debug('[PatientsComponent] Filtered results:', this.filteredPatients.length); } onSearchChange() { this.logger.debug('[PatientsComponent] onSearchChange called, searchTerm:', this.searchTerm); this.applySearch(); this.cdr.detectChanges(); // Force change detection } clearSearch() { this.searchTerm = ''; this.applySearch(); } async loadPatients() { // Reload all patients from system await this.loadAllPatients(); } async createAppointment(patient: any) { this.createAppointmentRequested.emit(patient); } async startChat(patient: any) { try { // Get the user ID for the patient // PatientProfile now includes userId directly let userId: string | null = patient.userId || null; // If userId is not available, try to find it by searching (fallback) if (!userId) { this.logger.warn('[PatientsComponent] userId not found in patient object, searching...', patient); // Try searching by full name if (patient.firstName && patient.lastName) { try { const fullName = `${patient.firstName} ${patient.lastName}`; const chatUsers = await this.chatService.searchPatients(fullName); const matchingUser = chatUsers.find(u => u.firstName?.toLowerCase() === patient.firstName?.toLowerCase() && u.lastName?.toLowerCase() === patient.lastName?.toLowerCase() ); if (matchingUser) { userId = matchingUser.userId; this.logger.debug('[PatientsComponent] Found user ID by name search:', userId); } } catch (e) { this.logger.warn('[PatientsComponent] Could not search by name:', e); } } } if (!userId) { this.logger.error('[PatientsComponent] Could not find user ID for patient:', { id: patient.id, userId: patient.userId, firstName: patient.firstName, lastName: patient.lastName, email: patient.email }); await this.modalService.alert( `Could not find patient user information for ${patient.firstName} ${patient.lastName}. The patient may not be available for chat.`, 'error', 'Error' ); return; } // Start chat with patient this.logger.debug('[PatientsComponent] Starting chat with userId:', userId); this.startChatRequested.emit({ userId: userId, patientId: patient.id, patientName: `${patient.firstName} ${patient.lastName}` }); } catch (error: any) { this.logger.error('[PatientsComponent] Error starting chat:', error); await this.modalService.alert( error?.message || 'Failed to start chat with patient', 'error', 'Error' ); } } viewPatient(patient: any) { this.patientSelected.emit(patient); } async removePatientFromHistory(patient: any) { const confirmed = await this.modalService.confirm( `Are you sure you want to remove ${patient.firstName} ${patient.lastName} from your history? This will hide all appointments with this patient from your view. This action cannot be undone.`, 'Remove Patient from History', 'warning' ); if (!confirmed) return; try { this.loading = true; await this.appointmentService.removePatientFromHistory(patient.id); await this.modalService.alert( 'Patient removed from history successfully', 'success', 'Success' ); // Remove from local list immediately (optimistic update) this.patients = this.patients.filter(p => p.id !== patient.id); this.filteredPatients = this.filteredPatients.filter(p => p.id !== patient.id); // Emit event to parent to refresh data (will update the @Input binding) this.patientRemoved.emit(); } catch (error: any) { this.logger.error('Error removing patient from history:', error); await this.modalService.alert( error?.message || 'Failed to remove patient from history', 'error', 'Error' ); } finally { this.loading = false; } } }