This commit is contained in:
Iliyan Angelov
2025-11-16 02:44:17 +02:00
parent 190f478327
commit c15a7bdbde
438 changed files with 85094 additions and 0 deletions

View File

@@ -0,0 +1,264 @@
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<void>();
@Output() patientSelected = new EventEmitter<any>();
@Output() createAppointmentRequested = new EventEmitter<any>();
@Output() startChatRequested = new EventEmitter<any>();
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;
}
}
}