diff --git a/app/booking-ui/src/app/admin/pages/doctors/doctors.html b/app/booking-ui/src/app/admin/pages/doctors/doctors.html index 9f96837..c463605 100644 --- a/app/booking-ui/src/app/admin/pages/doctors/doctors.html +++ b/app/booking-ui/src/app/admin/pages/doctors/doctors.html @@ -26,13 +26,13 @@

Doctors Management

avatar } @else {
- {{ (doc.fullName || doc.name || '?').charAt(0).toUpperCase() }} + {{ (doc.name || '?').charAt(0).toUpperCase() }}
}
- {{ doc.fullName || doc.name + ' ' + doc.lastname }} + {{ doc.name + ' ' + doc.lastname }} {{ doc.specialtyName || 'No Specialty' }}
diff --git a/app/booking-ui/src/app/admin/pages/doctors/doctors.ts b/app/booking-ui/src/app/admin/pages/doctors/doctors.ts index 38e953d..9f31476 100644 --- a/app/booking-ui/src/app/admin/pages/doctors/doctors.ts +++ b/app/booking-ui/src/app/admin/pages/doctors/doctors.ts @@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common'; import { SpecialtyDto } from '@core/models/specialty.model'; import { Component, OnInit, inject, signal } from '@angular/core'; import { SpecialtyService, DoctorService } from '@core/services/index'; -import { CreateDoctorRequest, DoctorDto } from '@core/models/doctor.model'; +import { CreateDoctorRequest, DoctorResponse } from '@core/models/doctor.model'; import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; @Component({ @@ -17,17 +17,18 @@ export class DoctorsComponent implements OnInit { private specialtyService = inject(SpecialtyService); private fb = inject(FormBuilder); - doctors = signal([]); + doctors = signal([]); specialties = signal([]); isLoading = signal(false); isModalOpen = signal(false); doctorForm: FormGroup = this.fb.group({ - name: ['', Validators.required], - lastname: ['', Validators.required], email: ['', [Validators.required, Validators.email]], password: ['', [Validators.required, Validators.minLength(6)]], + name: ['', Validators.required], + lastname: ['', Validators.required], + phoneNumber: ['', Validators.required], specialtyId: ['', Validators.required], consultationFee: [50, [Validators.required, Validators.min(1)]], experienceYears: [1, [Validators.required, Validators.min(0)]], @@ -111,7 +112,7 @@ export class DoctorsComponent implements OnInit { email: formValue.email, password: formValue.password, specialtyId: formValue.specialtyId, - isActive: true, + phoneNumber: formValue.phoneNumber, consultationFee: Number(formValue.consultationFee), experienceYears: Number(formValue.experienceYears), bio: formValue.bio || null, diff --git a/app/booking-ui/src/app/core/models/appointmnet.models.ts b/app/booking-ui/src/app/core/models/appointmnet.models.ts index 02357c5..2d7a133 100644 --- a/app/booking-ui/src/app/core/models/appointmnet.models.ts +++ b/app/booking-ui/src/app/core/models/appointmnet.models.ts @@ -5,32 +5,59 @@ export enum AppointmentStatus { Completed = 'Completed', } -export interface AppointmentDto { +export enum AttachmentType { + General = 0, + LabResult = 1, + XRay = 2, + Prescription = 3, +} + +// --- DTOs --- +export interface AppointmentAttachment { + id: string; + fileName: string; + fileType: string; + createdAt: string; +} + +export interface AppointmentResponse { id: string; doctorId: string; doctorName: string; - customerId: string; + specialty: string; + doctorPhotoUrl?: string | null; + doctorPhoneNumber?: string | null; + patientId: string; patientName: string; - startTime: string; // Date ISO - endTime: string; // Date ISO + startTime: string; // ISO Date + endTime: string; // ISO Date + price: number; status: AppointmentStatus; - medicalNotes?: string; + medicalNotes?: string | null; + attachments: AppointmentAttachment[]; } -export interface CreateReviewRequest { +export interface CreateAppointmentRequest { doctorId: string; - rating: number; - text: string; + startTime: string; + endTime: string; } -export interface CreateAppointmentRequest { - doctorId: string; - startTime: string; // UTC +export interface RescheduleRequest { + startTime: string; endTime: string; } -export interface TimeSlot { - start: string; // "2026-02-12T11:30:00Z" - end: string; // "2026-02-12T12:00:00Z" - isAvailable: boolean; +export interface CompleteAppointmentRequest { + appointmentId: string; + diagnosis: string; + medicalNotes?: string; + treatmentPlan?: string; + prescribedMedications?: string; +} + +export interface FileDownloadResponse { + fileContents: string; + contentType: string; + fileDownloadName: string; } diff --git a/app/booking-ui/src/app/core/models/doctor.model.ts b/app/booking-ui/src/app/core/models/doctor.model.ts index eab50aa..a5a9c2c 100644 --- a/app/booking-ui/src/app/core/models/doctor.model.ts +++ b/app/booking-ui/src/app/core/models/doctor.model.ts @@ -1,53 +1,53 @@ -export interface DoctorDetailsDto { +export interface DoctorResponse { id: string; + userId?: string | null; name: string; lastname: string; + specialtyId: string; specialtyName: string; - imageUrl?: string; + phoneNumber: string; + imageUrl?: string | null; averageRating: number; reviewCount: number; consultationFee: number; experienceYears: number; - bio?: string; + bio?: string | null; } -export interface DoctorDto { - id: string; - userId: string; +export interface CreateDoctorRequest { + email: string; + password: string; name: string; lastname: string; - fullName: string; - specialty: string; - photoUrl: string; - specialtyName: string; + phoneNumber: string; specialtyId: string; - averageRating: number; - reviewCount: number; - totalReviews: number; - imageUrl?: string; consultationFee: number; experienceYears: number; - bio: string; + bio?: string; + imageUrl?: string; } -export interface CreateDoctorRequest { - email: string; - password: string; +export interface UpdateDoctorRequest { + userId: string; name: string; lastname: string; - specialtyId: string; - isActive: boolean; - consultationFee: number; + specialty: string; + phoneNumber: string; + bio?: string; experienceYears: number; - bio?: string | null; - imageUrl?: string | null; + imageUrl?: string; + consultationFee: number; + isActive: boolean; } -export interface DoctorStatsDto { - totalPatients: number; - completedAppointments: number; - totalEarnings: number; - period: string; +export interface ScheduleConfig { + dayStart: string; + dayEnd: string; + lunchStart: string; + lunchEnd: string; + workingDays: number[]; + slotDurationMinutes: number; + bufferMinutes: number; } export interface CreateReviewRequest { @@ -63,3 +63,16 @@ export interface ReviewDto { text: string; createdAt: string; } + +export interface TimeSlot { + start: string; // "2026-02-12T11:30:00Z" + end: string; // "2026-02-12T12:00:00Z" + isAvailable: boolean; +} + +export interface DoctorStatsDto { + totalPatients: number; + completedAppointments: number; + totalEarnings: number; + period: string; +} diff --git a/app/booking-ui/src/app/core/models/patient.models.ts b/app/booking-ui/src/app/core/models/patient.models.ts index c759ad5..fb3963f 100644 --- a/app/booking-ui/src/app/core/models/patient.models.ts +++ b/app/booking-ui/src/app/core/models/patient.models.ts @@ -2,9 +2,9 @@ export interface PatientDto { id: string; fullName: string; email: string; - photoUrl: string | null; - phoneNumber: string | null; + photoUrl?: string | null; + phoneNumber?: string | null; dateOfBirth: string; gender: string; - address: string | null; + address?: string | null; } diff --git a/app/booking-ui/src/app/core/models/review.model.ts b/app/booking-ui/src/app/core/models/review.model.ts new file mode 100644 index 0000000..cce4acc --- /dev/null +++ b/app/booking-ui/src/app/core/models/review.model.ts @@ -0,0 +1,13 @@ +export interface ReviewDto { + id: string; + patientName: string; + rating: number; + text: string; + createdAt: string; +} + +export interface CreateReviewRequest { + doctorId: string; + rating: number; + text: string; +} diff --git a/app/booking-ui/src/app/core/models/specialty.model.ts b/app/booking-ui/src/app/core/models/specialty.model.ts index a0d4f79..4fd75b9 100644 --- a/app/booking-ui/src/app/core/models/specialty.model.ts +++ b/app/booking-ui/src/app/core/models/specialty.model.ts @@ -6,3 +6,8 @@ export interface SpecialtyDto { id: string; name: string; } + +export interface UpdateSpecialtyRequest { + id: string; + name: string; +} diff --git a/app/booking-ui/src/app/core/services/appointment/appointment.service.ts b/app/booking-ui/src/app/core/services/appointment/appointment.service.ts index 0e53705..8b2d5b1 100644 --- a/app/booking-ui/src/app/core/services/appointment/appointment.service.ts +++ b/app/booking-ui/src/app/core/services/appointment/appointment.service.ts @@ -1,7 +1,14 @@ import { HttpClient, HttpParams } from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; import { environment } from '@env/environment'; -import { AppointmentDto, CreateAppointmentRequest } from '@core/models/appointmnet.models'; +import { + AppointmentResponse, + AttachmentType, + CompleteAppointmentRequest, + CreateAppointmentRequest, + RescheduleRequest, +} from '@core/models/appointmnet.models'; +import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root', @@ -10,49 +17,67 @@ export class AppointmentService { private http = inject(HttpClient); private apiUrl = `${environment.apiUrl}/Appointments`; - getDoctorSchedule(doctorId?: string) { - let params = new HttpParams(); - if (doctorId) { - params = params.set('doctorId', doctorId); - } + // --- Patient + Doctor --- - return this.http.get(`${this.apiUrl}/doctor-schedule`, { params }); + createAppointment(request: CreateAppointmentRequest): Observable { + return this.http.post(this.apiUrl, request); } - getPatientHistory() { - return this.http.get(`${this.apiUrl}/patient-history`); + getAppointmentById(id: string): Observable { + return this.http.get(`${this.apiUrl}/${id}`); } - createAppointment(request: CreateAppointmentRequest) { - return this.http.post(this.apiUrl, request); + cancelAppointment(id: string): Observable { + return this.http.delete(`${this.apiUrl}/${id}`); } - completeAppointment(id: string, medicalNotes: string) { - return this.http.post(`${this.apiUrl}/${id}/complete`, JSON.stringify(medicalNotes), { - headers: { 'Content-Type': 'application/json' }, - }); + // --- DOCTORS METHODS --- + + getDoctorSchedule(start?: string, end?: string): Observable { + let params = new HttpParams(); + if (start) params = params.set('start', start); + if (end) params = params.set('end', end); + + return this.http.get(`${this.apiUrl}/doctor-schedule`, { params }); } - confirmAppointment(id: string) { + confirmAppointment(id: string): Observable { return this.http.post(`${this.apiUrl}/${id}/confirm`, {}); } - cancelAppointment(id: string) { - return this.http.delete(`${this.apiUrl}/${id}`); + rescheduleAppointment(id: string, request: RescheduleRequest): Observable { + return this.http.put(`${this.apiUrl}/${id}/reschedule`, request); } - getAppointmentList(doctorId?: string, start?: string, end?: string) { - let params = new HttpParams(); - if (doctorId) { - params = params.set('doctorId', doctorId); - } - if (start) { - params = params.set('start', start); - } - if (end) { - params = params.set('end', end); - } - - return this.http.get(`${this.apiUrl}`, { params }); + completeAppointment(id: string, data: CompleteAppointmentRequest): Observable { + return this.http.post(`${this.apiUrl}/${id}/complete`, data); + } + + // --- PATIENTS METHODS --- + + getPatientHistory(): Observable { + return this.http.get(`${this.apiUrl}/patient-history`); + } + + // --- Attachments --- + + uploadAttachment(appointmentId: string, file: File, type: AttachmentType): Observable { + const formData = new FormData(); + formData.append('file', file); // Имя поля должно совпадать с IFormFile в контроллере + formData.append('type', type.toString()); + + return this.http.post(`${this.apiUrl}/${appointmentId}/attachments`, formData); + } + + // DELETE: + deleteAttachment(appointmentId: string, attachmentId: string): Observable { + return this.http.delete(`${this.apiUrl}/${appointmentId}/attachments/${attachmentId}`); + } + + // GET: Report + downloadReport(appointmentId: string): Observable { + return this.http.get(`${this.apiUrl}/${appointmentId}/report`, { + responseType: 'blob', + }); } } diff --git a/app/booking-ui/src/app/core/services/doctor/doctor.service.ts b/app/booking-ui/src/app/core/services/doctor/doctor.service.ts index ddb6e19..8f545db 100644 --- a/app/booking-ui/src/app/core/services/doctor/doctor.service.ts +++ b/app/booking-ui/src/app/core/services/doctor/doctor.service.ts @@ -1,16 +1,15 @@ import { environment } from '@env/environment'; -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpParams } from '@angular/common/http'; import { inject, Injectable, signal } from '@angular/core'; import { map, Observable, tap, catchError, of } from 'rxjs'; import { CreateDoctorRequest, - CreateReviewRequest, - DoctorDetailsDto, - DoctorDto, + DoctorResponse, DoctorStatsDto, - ReviewDto, + ScheduleConfig, + UpdateDoctorRequest, } from '@core/models/doctor.model'; -import { TimeSlot } from '@core/models/appointmnet.models'; +import { TimeSlot } from '@core/models/doctor.model'; @Injectable({ providedIn: 'root', @@ -19,34 +18,31 @@ export class DoctorService { private http = inject(HttpClient); private apiUrl = `${environment.apiUrl}/Doctors`; - currentDoctor = signal(null); + currentDoctor = signal(null); currentDoctorId = signal(null); - getDoctors() { - return this.http.get(this.apiUrl); + getDoctors(searchTerm = '', specialtyId = ''): Observable { + let params = new HttpParams(); + if (searchTerm) params = params.set('SearchTerm', searchTerm); + if (specialtyId) params = params.set('SpecialtyId', specialtyId); + + return this.http.get(this.apiUrl, { params }); } - getDoctorProfile(id: string) { - return this.http.get(`${this.apiUrl}/${id}`).pipe( - map((data) => { - return { - id: data.id, - name: data.name, - lastname: data.lastname, - specialtyName: data.specialtyName || data.specialty, - imageUrl: data.imageUrl || data.photoUrl || 'assets/default-doctor.png', - averageRating: data.averageRating || 0, - reviewCount: data.reviewCount || data.totalReviews || 0, - consultationFee: data.consultationFee || 50, - experienceYears: data.experienceYears || 0, - bio: data.bio || '', - } as DoctorDetailsDto; - }), - ); + getDoctorById(id: string): Observable { + return this.http.get(`${this.apiUrl}/${id}`); + } + + createDoctor(doctor: CreateDoctorRequest): Observable { + return this.http.post(this.apiUrl, doctor); + } + + updateDoctor(id: string, doctor: UpdateDoctorRequest): Observable { + return this.http.put(`${this.apiUrl}/${id}`, doctor); } - createDoctor(request: CreateDoctorRequest) { - return this.http.post(this.apiUrl, request); + deleteDoctor(id: string): Observable { + return this.http.delete(`${this.apiUrl}/${id}`); } resolveDoctorId(authUserId: string): Observable { @@ -72,20 +68,24 @@ export class DoctorService { ); } - addReview(review: CreateReviewRequest) { - return this.http.post(`${environment.apiUrl}/Reviews`, review); + uploadProfilePhoto(file: File): Observable<{ imageUrl: string }> { + const formData = new FormData(); + formData.append('file', file); + + return this.http.post<{ imageUrl: string }>(`${this.apiUrl}/profile-photo`, formData); } - getDoctorStats(doctorId: string, period: string) { - return this.http.get(`${this.apiUrl}/${doctorId}/stats?period=${period}`); + getDoctorStats(id: string, period = 'month'): Observable { + const params = new HttpParams().set('period', period); + return this.http.get(`${this.apiUrl}/${id}/stats`, { params }); } - getDoctorSlots(doctorId: string, date: string) { - const isoDate = `${date}T00:00:00Z`; - return this.http.get(`${this.apiUrl}/${doctorId}/slots?date=${isoDate}`); + updateSchedule(id: string, config: ScheduleConfig): Observable { + return this.http.put(`${this.apiUrl}/${id}/schedule`, config); } - getDoctorReviews(doctorId: string) { - return this.http.get(`${environment.apiUrl}/Reviews?doctorId=${doctorId}`); + getAvailableSlots(id: string, date: string): Observable { + const params = new HttpParams().set('date', date); + return this.http.get(`${this.apiUrl}/${id}/slots`, { params }); } } diff --git a/app/booking-ui/src/app/core/services/reviews/reviews.service.ts b/app/booking-ui/src/app/core/services/reviews/reviews.service.ts new file mode 100644 index 0000000..e3d2306 --- /dev/null +++ b/app/booking-ui/src/app/core/services/reviews/reviews.service.ts @@ -0,0 +1,22 @@ +import { Injectable, inject } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { environment } from '@env/environment'; +import { CreateReviewRequest, ReviewDto } from '@core/models/doctor.model'; + +@Injectable({ + providedIn: 'root', +}) +export class ReviewService { + private http = inject(HttpClient); + private apiUrl = `${environment.apiUrl}/Reviews`; + + getDoctorReviews(doctorId: string): Observable { + const params = new HttpParams().set('doctorId', doctorId); + return this.http.get(this.apiUrl, { params }); + } + + createReview(review: CreateReviewRequest): Observable { + return this.http.post(this.apiUrl, review); + } +} diff --git a/app/booking-ui/src/app/core/services/specialty/specialty.service.ts b/app/booking-ui/src/app/core/services/specialty/specialty.service.ts index 37c59b8..3c0fdbe 100644 --- a/app/booking-ui/src/app/core/services/specialty/specialty.service.ts +++ b/app/booking-ui/src/app/core/services/specialty/specialty.service.ts @@ -1,7 +1,12 @@ import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { environment } from '@env/environment'; -import { CreateSpecialtyRequest, SpecialtyDto } from '@core/models/specialty.model'; +import { + CreateSpecialtyRequest, + SpecialtyDto, + UpdateSpecialtyRequest, +} from '@core/models/specialty.model'; +import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root', @@ -14,10 +19,19 @@ export class SpecialtyService { return this.http.get(this.apiUrl); } + getById(id: string): Observable { + return this.http.get(`${this.apiUrl}/${id}`); + } + create(name: string) { return this.http.post(this.apiUrl, { name }); } + update(id: string, name: string): Observable { + const request: UpdateSpecialtyRequest = { id, name }; + return this.http.put(`${this.apiUrl}/${id}`, request); + } + delete(id: string) { return this.http.delete(`${this.apiUrl}/${id}`); } diff --git a/app/booking-ui/src/app/doctor/components/appointment-details/appointment-details.html b/app/booking-ui/src/app/doctor/components/appointment-details/appointment-details.html index 59f50b1..4359993 100644 --- a/app/booking-ui/src/app/doctor/components/appointment-details/appointment-details.html +++ b/app/booking-ui/src/app/doctor/components/appointment-details/appointment-details.html @@ -5,7 +5,7 @@

@if (appointment.patientName) { {{ appointment.patientName }} } @else { - Patient #{{ appointment.customerId | slice: 0 : 5 }} + Patient #{{ appointment.patientId | slice: 0 : 5 }} }

diff --git a/app/booking-ui/src/app/doctor/components/appointment-details/appointment-details.ts b/app/booking-ui/src/app/doctor/components/appointment-details/appointment-details.ts index ac35aec..7a55865 100644 --- a/app/booking-ui/src/app/doctor/components/appointment-details/appointment-details.ts +++ b/app/booking-ui/src/app/doctor/components/appointment-details/appointment-details.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { AppointmentDto } from '@core/models/appointmnet.models'; +import { AppointmentResponse } from '@core/models/appointmnet.models'; @Component({ selector: 'app-appointment-details', @@ -10,6 +10,6 @@ import { AppointmentDto } from '@core/models/appointmnet.models'; styleUrl: './appointment-details.scss', }) export class AppointmentDetailsComponent { - @Input({ required: true }) appointment!: AppointmentDto; + @Input({ required: true }) appointment!: AppointmentResponse; @Output() closeDetails = new EventEmitter(); } diff --git a/app/booking-ui/src/app/doctor/components/appointments-table/appointments-table.ts b/app/booking-ui/src/app/doctor/components/appointments-table/appointments-table.ts index ff1a17f..a84e7f5 100644 --- a/app/booking-ui/src/app/doctor/components/appointments-table/appointments-table.ts +++ b/app/booking-ui/src/app/doctor/components/appointments-table/appointments-table.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; import { Component, EventEmitter, Input, Output, signal } from '@angular/core'; -import { AppointmentDto, AppointmentStatus } from '@core/models/appointmnet.models'; +import { AppointmentResponse, AppointmentStatus } from '@core/models/appointmnet.models'; @Component({ selector: 'app-appointments-table', @@ -9,7 +9,7 @@ import { AppointmentDto, AppointmentStatus } from '@core/models/appointmnet.mode styleUrl: './appointments-table.scss', }) export class AppointmentsTable { - @Input({ required: true }) appointments: AppointmentDto[] = []; + @Input({ required: true }) appointments: AppointmentResponse[] = []; @Input() isLoading = false; @Output() action = new EventEmitter<{ type: string; id: string }>(); diff --git a/app/booking-ui/src/app/doctor/components/calendar-grid/calendar-grid.html b/app/booking-ui/src/app/doctor/components/calendar-grid/calendar-grid.html index 9f26812..a8ece63 100644 --- a/app/booking-ui/src/app/doctor/components/calendar-grid/calendar-grid.html +++ b/app/booking-ui/src/app/doctor/components/calendar-grid/calendar-grid.html @@ -37,7 +37,7 @@ @if (appt.patientName) { {{ appt.patientName }} } @else { - #{{ appt.customerId | slice: 0 : 5 }} + #{{ appt.patientId | slice: 0 : 5 }} } diff --git a/app/booking-ui/src/app/doctor/components/calendar-grid/calendar-grid.ts b/app/booking-ui/src/app/doctor/components/calendar-grid/calendar-grid.ts index 75f0d7a..033a8d5 100644 --- a/app/booking-ui/src/app/doctor/components/calendar-grid/calendar-grid.ts +++ b/app/booking-ui/src/app/doctor/components/calendar-grid/calendar-grid.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { CommonModule } from '@angular/common'; import { CalendarDay } from '../../models/calendar.models'; // Наша новая модель -import { AppointmentDto } from '@core/models/appointmnet.models'; +import { AppointmentResponse } from '@core/models/appointmnet.models'; @Component({ selector: 'app-calendar-grid', @@ -15,7 +15,7 @@ export class CalendarGridComponent { @Input() loading = false; @Input() selectedId: string | undefined; - @Output() selectAppointment = new EventEmitter(); + @Output() selectAppointment = new EventEmitter(); weekDays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; diff --git a/app/booking-ui/src/app/doctor/layout/doctor-layout/doctor-layout.ts b/app/booking-ui/src/app/doctor/layout/doctor-layout/doctor-layout.ts index c74e62a..3f631a8 100644 --- a/app/booking-ui/src/app/doctor/layout/doctor-layout/doctor-layout.ts +++ b/app/booking-ui/src/app/doctor/layout/doctor-layout/doctor-layout.ts @@ -1,5 +1,5 @@ import { CommonModule } from '@angular/common'; -import { DoctorDetailsDto } from '@core/models/doctor.model'; +import { DoctorResponse } from '@core/models/doctor.model'; import { AuthService, DoctorService } from '@core/services/index'; import { Component, inject, OnInit, signal } from '@angular/core'; import { Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; @@ -17,7 +17,7 @@ export class DoctorLayout implements OnInit { private router = inject(Router); currentUser = this.authService.currentUser; - doctorProfile = signal(null); + doctorProfile = signal(null); isProfileLoading = signal(true); isInitializing = signal(true); @@ -31,50 +31,33 @@ export class DoctorLayout implements OnInit { } this.doctorService.resolveDoctorId(user.id).subscribe({ - next: (doctorId) => { + next: (doctorId: string | null) => { if (doctorId) { this.loadProfile(doctorId); } else { console.error('This user is not a doctor!'); + this.isProfileLoading.set(false); } this.isInitializing.set(false); }, - error: () => this.isInitializing.set(false), + error: (err: string) => { + console.error(err); + this.isInitializing.set(false); + this.isProfileLoading.set(false); + }, }); } - initDoctorData() { - const user = this.currentUser(); - - if (user && user.id) { - console.log('Auth UserId (Token):', user.id); - - this.doctorService.resolveDoctorId(user.id).subscribe({ - next: (realDoctorId) => { - if (realDoctorId) { - console.log('Real DoctorId (DB PK):', realDoctorId); - - this.loadProfile(realDoctorId); - } else { - console.error('Doctor entity not found for this User'); - this.isProfileLoading.set(false); - } - }, - error: (err) => { - console.error('Error resolving doctor ID', err); - this.isProfileLoading.set(false); - }, - }); - } - } - loadProfile(doctorId: string) { - this.doctorService.getDoctorProfile(doctorId).subscribe({ - next: (profile) => { + this.doctorService.getDoctorById(doctorId).subscribe({ + next: (profile: DoctorResponse) => { this.doctorProfile.set(profile); this.isProfileLoading.set(false); }, - error: (err) => console.error(err), + error: (err: string) => { + console.error('Failed to load doctor profile', err); + this.isProfileLoading.set(false); + }, }); } diff --git a/app/booking-ui/src/app/doctor/models/calendar.models.ts b/app/booking-ui/src/app/doctor/models/calendar.models.ts index 8d715d6..234cc0a 100644 --- a/app/booking-ui/src/app/doctor/models/calendar.models.ts +++ b/app/booking-ui/src/app/doctor/models/calendar.models.ts @@ -1,7 +1,7 @@ -import { AppointmentDto } from '@core/models/appointmnet.models'; +import { AppointmentResponse } from '@core/models/appointmnet.models'; export interface CalendarDay { date: Date; isCurrentMonth: boolean; - appointments: AppointmentDto[]; + appointments: AppointmentResponse[]; } diff --git a/app/booking-ui/src/app/doctor/pages/dashboard/dashboard.ts b/app/booking-ui/src/app/doctor/pages/dashboard/dashboard.ts index 0b8876e..6177dee 100644 --- a/app/booking-ui/src/app/doctor/pages/dashboard/dashboard.ts +++ b/app/booking-ui/src/app/doctor/pages/dashboard/dashboard.ts @@ -1,7 +1,11 @@ import { CommonModule } from '@angular/common'; import { Component, computed, effect, inject, signal, ViewChild } from '@angular/core'; import { AppointmentService, DoctorService } from '@core/services/index'; -import { AppointmentDto, AppointmentStatus } from '@core/models/appointmnet.models'; +import { + AppointmentResponse, + AppointmentStatus, + CompleteAppointmentRequest, +} from '@core/models/appointmnet.models'; import { StatsCards } from '../../components/stats-cards/stats-cards'; import { AppointmentsTable } from '../../components/appointments-table/appointments-table'; import { AppointmentCompletionModal } from '../../components/appointment-completion-modal/appointment-completion-modal'; @@ -24,7 +28,7 @@ export class Dashboard { isCompleteModalOpen = signal(false); selectedAppointmentForCompletion = signal<{ id: string; name: string } | null>(null); - appointments = signal([]); + appointments = signal([]); selectedPeriod = signal('Day'); stats = signal(null); isLoadingStats = signal(false); @@ -177,7 +181,15 @@ export class Dashboard { const selected = this.selectedAppointmentForCompletion(); if (!selected) return; - this.appointmentService.completeAppointment(selected.id, notes).subscribe({ + const completionRequest: CompleteAppointmentRequest = { + appointmentId: selected.id, + diagnosis: 'Consultation Complete', + medicalNotes: notes, + treatmentPlan: '', + prescribedMedications: '', + }; + + this.appointmentService.completeAppointment(selected.id, completionRequest).subscribe({ next: () => { this.appointments.update((list) => list.map((a) => @@ -212,7 +224,7 @@ export class Dashboard { }); } - private calculateStats(data: AppointmentDto[]) { + private calculateStats(data: AppointmentResponse[]) { const today = new Date().toLocaleDateString('en-CA'); const todayCount = data.filter((a) => a.startTime.startsWith(today)).length; diff --git a/app/booking-ui/src/app/doctor/pages/doctor-calendar/doctor-calendar.ts b/app/booking-ui/src/app/doctor/pages/doctor-calendar/doctor-calendar.ts index 1489396..4d3a5c5 100644 --- a/app/booking-ui/src/app/doctor/pages/doctor-calendar/doctor-calendar.ts +++ b/app/booking-ui/src/app/doctor/pages/doctor-calendar/doctor-calendar.ts @@ -1,12 +1,12 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectorRef, Component, inject, OnInit, signal } from '@angular/core'; import { finalize } from 'rxjs'; -import { AppointmentDto } from '@core/models/appointmnet.models'; -import { AppointmentService, DoctorService } from '@core/services'; +import { AppointmentService, AuthService, DoctorService } from '@core/services'; import { CalendarDay } from '../../models/calendar.models'; import { AppointmentDetailsComponent } from '../../components/appointment-details/appointment-details'; import { CalendarHeaderComponent } from '../../components/calendar-header.component/calendar-header.component'; import { CalendarGridComponent } from '../../components/calendar-grid/calendar-grid'; +import { AppointmentResponse } from '@core/models/appointmnet.models'; @Component({ selector: 'app-doctor-calendar', @@ -23,9 +23,10 @@ import { CalendarGridComponent } from '../../components/calendar-grid/calendar-g export class DoctorCalendarComponent implements OnInit { private appointmentService = inject(AppointmentService); private doctorService = inject(DoctorService); + private authService = inject(AuthService); private cdr = inject(ChangeDetectorRef); - selectedAppointment = signal(null); + selectedAppointment = signal(null); viewDate: Date = new Date(); calendarDays: CalendarDay[] = []; loading = false; @@ -64,9 +65,12 @@ export class DoctorCalendarComponent implements OnInit { if (this.calendarDays.length === 0) this.generateCalendar(); + if (!this.calendarDays.length) return; + const startIso = this.calendarDays[0].date.toISOString(); const endIso = this.calendarDays[this.calendarDays.length - 1].date.toISOString(); - const doctorId = this.doctorService.currentDoctorId(); + + const doctorId = this.authService.currentUser()?.id; if (!doctorId) { this.loading = false; @@ -74,7 +78,7 @@ export class DoctorCalendarComponent implements OnInit { } this.appointmentService - .getAppointmentList(doctorId, startIso, endIso) + .getDoctorSchedule(startIso, endIso) .pipe( finalize(() => { this.loading = false; @@ -82,16 +86,16 @@ export class DoctorCalendarComponent implements OnInit { }), ) .subscribe({ - next: (data) => { + next: (data: AppointmentResponse[]) => { this.mapAppointmentsToDays(data); }, - error: (err) => { + error: (err: string) => { console.error('ERROR:', err); }, }); } - private mapAppointmentsToDays(appointments: AppointmentDto[]) { + private mapAppointmentsToDays(appointments: AppointmentResponse[]) { if (!appointments || !Array.isArray(appointments)) return; this.calendarDays.forEach((day) => (day.appointments = [])); @@ -123,7 +127,7 @@ export class DoctorCalendarComponent implements OnInit { this.loadAppointments(); } - onAppointmentSelect(appt: AppointmentDto) { + onAppointmentSelect(appt: AppointmentResponse) { if (this.selectedAppointment()?.id === appt.id) { this.selectedAppointment.set(null); } else { diff --git a/app/booking-ui/src/app/pages/doctor-details/components/doctor-booking/doctor-booking.html b/app/booking-ui/src/app/pages/doctor-details/components/doctor-booking/doctor-booking.html index 6ea1661..692c9ed 100644 --- a/app/booking-ui/src/app/pages/doctor-details/components/doctor-booking/doctor-booking.html +++ b/app/booking-ui/src/app/pages/doctor-details/components/doctor-booking/doctor-booking.html @@ -2,11 +2,12 @@

Book Appointment

- + @@ -14,16 +15,19 @@

Book Appointment

Available Slots

- @if (isLoadingSlots) { -
Loading slots...
+ @if (isLoadingSlots()) { +
+
+ Finding slots... +
} @else {
- @for (slot of slots; track slot.start) { + @for (slot of availableSlots(); track slot.start) { @@ -34,8 +38,16 @@

Available Slots

}
- + + @if (!authService.currentUser()) { + + }
diff --git a/app/booking-ui/src/app/pages/doctor-details/components/doctor-booking/doctor-booking.ts b/app/booking-ui/src/app/pages/doctor-details/components/doctor-booking/doctor-booking.ts index 8d45b86..e4138f7 100644 --- a/app/booking-ui/src/app/pages/doctor-details/components/doctor-booking/doctor-booking.ts +++ b/app/booking-ui/src/app/pages/doctor-details/components/doctor-booking/doctor-booking.ts @@ -1,7 +1,10 @@ -import { Component, EventEmitter, Input, Output, signal } from '@angular/core'; +import { Component, effect, inject, Input, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; -import { TimeSlot } from '@core/models/appointmnet.models'; +import { CreateAppointmentRequest } from '@core/models/appointmnet.models'; +import { DoctorResponse, TimeSlot } from '@core/models/doctor.model'; +import { AppointmentService, AuthService, DoctorService } from '@core/services'; +import { Router } from '@angular/router'; @Component({ selector: 'app-doctor-booking', @@ -11,31 +14,86 @@ import { TimeSlot } from '@core/models/appointmnet.models'; styleUrl: './doctor-booking.scss', }) export class DoctorBooking { - @Input({ required: true }) slots: TimeSlot[] = []; - @Input() selectedDate = ''; - @Input() isLoadingSlots = false; - @Input() isBooking = false; + private doctorService = inject(DoctorService); + private appointmentService = inject(AppointmentService); + public authService = inject(AuthService); + private router = inject(Router); - @Output() dateChange = new EventEmitter(); - @Output() confirmBooking = new EventEmitter(); + @Input({ required: true }) doctor!: DoctorResponse; + selectedDate = signal(new Date().toISOString().split('T')[0]); + minDate = new Date().toISOString().split('T')[0]; + + availableSlots = signal([]); selectedSlot = signal(null); + isLoadingSlots = signal(false); + isBooking = signal(false); + + constructor() { + effect(() => { + this.loadSlots(this.selectedDate()); + }); + } + onDateChange(newDate: string) { + this.selectedDate.set(newDate); this.selectedSlot.set(null); - this.dateChange.emit(newDate); } - onSelectSlot(slot: TimeSlot) { - if (slot.isAvailable) { - this.selectedSlot.set(slot); - } + loadSlots(date: string) { + this.isLoadingSlots.set(true); + const utcDate = `${date}T00:00:00Z`; + this.doctorService.getAvailableSlots(this.doctor.id, utcDate).subscribe({ + next: (slots) => { + this.availableSlots.set(slots); + this.isLoadingSlots.set(false); + }, + error: () => { + this.availableSlots.set([]); + this.isLoadingSlots.set(false); + }, + }); } - onBook() { + bookAppointment() { const slot = this.selectedSlot(); - if (slot) { - this.confirmBooking.emit(slot); + if (!slot) return; + + if (!this.authService.currentUser()) { + this.router.navigate(['/auth/login']); + return; } + + this.isBooking.set(true); + + const formatToUtc = (dateStr: string): string => { + if (!dateStr) return ''; + if (dateStr.includes('Z') || dateStr.includes('+')) { + return new Date(dateStr).toISOString(); + } + return new Date(dateStr.endsWith('Z') ? dateStr : `${dateStr}Z`).toISOString(); + }; + + const request: CreateAppointmentRequest = { + doctorId: this.doctor.id, + startTime: formatToUtc(slot.start), + endTime: formatToUtc(slot.end), + }; + + console.log('Sending to Backend:', request); + + this.appointmentService.createAppointment(request).subscribe({ + next: () => { + alert('Appointment booked successfully!'); + this.isBooking.set(false); + this.selectedSlot.set(null); + this.loadSlots(this.selectedDate()); + }, + error: () => { + this.isBooking.set(false); + alert('Booking failed. Check Network tab for details.'); + }, + }); } } diff --git a/app/booking-ui/src/app/pages/doctor-details/components/doctor-info/doctor-info.scss b/app/booking-ui/src/app/pages/doctor-details/components/doctor-info/doctor-info.scss index d54e4a4..d65a1a8 100644 --- a/app/booking-ui/src/app/pages/doctor-details/components/doctor-info/doctor-info.scss +++ b/app/booking-ui/src/app/pages/doctor-details/components/doctor-info/doctor-info.scss @@ -69,6 +69,10 @@ font-size: 1rem; } +.about-section.card { + margin: 0; +} + @media (max-width: 992px) { .profile-header { flex-direction: column; diff --git a/app/booking-ui/src/app/pages/doctor-details/components/doctor-info/doctor-info.ts b/app/booking-ui/src/app/pages/doctor-details/components/doctor-info/doctor-info.ts index 03974d4..526e3a6 100644 --- a/app/booking-ui/src/app/pages/doctor-details/components/doctor-info/doctor-info.ts +++ b/app/booking-ui/src/app/pages/doctor-details/components/doctor-info/doctor-info.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { DoctorDetailsDto } from '@core/models/doctor.model'; +import { DoctorResponse } from '@core/models/doctor.model'; @Component({ selector: 'app-doctor-info', @@ -9,5 +9,5 @@ import { DoctorDetailsDto } from '@core/models/doctor.model'; styleUrl: './doctor-info.scss', }) export class DoctorInfo { - @Input() doctor!: DoctorDetailsDto; + @Input({ required: true }) doctor!: DoctorResponse; } diff --git a/app/booking-ui/src/app/pages/doctor-details/components/doctor-reviews/doctor-reviews.html b/app/booking-ui/src/app/pages/doctor-details/components/doctor-reviews/doctor-reviews.html index 8dadc91..22cdeac 100644 --- a/app/booking-ui/src/app/pages/doctor-details/components/doctor-reviews/doctor-reviews.html +++ b/app/booking-ui/src/app/pages/doctor-details/components/doctor-reviews/doctor-reviews.html @@ -1,19 +1,19 @@
-

Patient Reviews ({{ reviews.length }})

+

Patient Reviews ({{ reviews().length }})

- @if (user) { + @if (authService.currentUser(); as user) {

Leave a Review

-
+
@for (star of stars; track star) {
} @else { @@ -44,34 +45,38 @@

Leave a Review


- @for (review of reviews; track review.id) { -
-
- Avatar -
- {{ review.patientName }} - {{ review.createdAt | date: 'mediumDate' }} -
-
- @for (star of stars; track star) { - - {{ star <= review.rating ? 'star' : 'star_border' }} - - } + @if (isLoading()) { +
Загрузка отзывов...
+ } @else { + @for (review of reviews(); track review.id) { +
+
+ Avatar +
+ {{ review.patientName }} + {{ review.createdAt | date: 'mediumDate' }} +
+
+ @for (star of stars; track star) { + + {{ star <= review.rating ? 'star' : 'star_border' }} + + } +
+

{{ review.text }}

-

{{ review.text }}

-
- } @empty { -

No reviews yet. Be the first!

+ } @empty { +

No reviews yet. Be the first!

+ } }
diff --git a/app/booking-ui/src/app/pages/doctor-details/components/doctor-reviews/doctor-reviews.ts b/app/booking-ui/src/app/pages/doctor-details/components/doctor-reviews/doctor-reviews.ts index df003c5..5c08782 100644 --- a/app/booking-ui/src/app/pages/doctor-details/components/doctor-reviews/doctor-reviews.ts +++ b/app/booking-ui/src/app/pages/doctor-details/components/doctor-reviews/doctor-reviews.ts @@ -1,9 +1,10 @@ import { CommonModule } from '@angular/common'; -import { Component, EventEmitter, Input, Output, signal } from '@angular/core'; +import { Component, inject, Input, signal, SimpleChanges, OnChanges } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { RouterLink } from '@angular/router'; -import { CurrentUser } from '@core/models/auth.model'; -import { ReviewDto } from '@core/models/doctor.model'; +import { CreateReviewRequest, ReviewDto } from '@core/models/doctor.model'; +import { AuthService } from '@core/services'; +import { ReviewService } from '@core/services/reviews/reviews.service'; @Component({ selector: 'app-doctor-reviews', @@ -11,32 +12,78 @@ import { ReviewDto } from '@core/models/doctor.model'; templateUrl: './doctor-reviews.html', styleUrl: './doctor-reviews.scss', }) -export class DoctorReviews { - @Input({ required: true }) reviews: ReviewDto[] = []; - @Input() user: CurrentUser | null = null; - @Input() isSubmitting = false; +export class DoctorReviews implements OnChanges { + private reviewService = inject(ReviewService); + public authService = inject(AuthService); - @Output() submitReview = new EventEmitter<{ rating: number; text: string }>(); + @Input({ required: true }) doctorId!: string; + + reviews = signal([]); + isLoading = signal(false); + isSubmitting = signal(false); userRating = signal(0); hoverRating = signal(0); comment = signal(''); - stars = [1, 2, 3, 4, 5]; + ngOnChanges(changes: SimpleChanges) { + if (changes['doctorId'] && this.doctorId) { + this.loadReviews(); + } + } + + loadReviews() { + this.isLoading.set(true); + this.reviewService.getDoctorReviews(this.doctorId).subscribe({ + next: (data) => { + this.reviews.set(data); + this.isLoading.set(false); + }, + error: (err) => { + console.error('Error loading reviews', err); + this.isLoading.set(false); + }, + }); + } + setRating(star: number) { this.userRating.set(star); } + setHover(star: number) { + this.hoverRating.set(star); + } + + clearHover() { + this.hoverRating.set(0); + } + sendReview() { const rating = this.userRating(); const text = this.comment(); - if (rating > 0) { - this.submitReview.emit({ rating, text }); + if (rating > 0 && text.trim()) { + this.isSubmitting.set(true); + + const request: CreateReviewRequest = { + doctorId: this.doctorId, + rating: rating, + text: text, + }; - this.userRating.set(0); - this.comment.set(''); + this.reviewService.createReview(request).subscribe({ + next: () => { + this.userRating.set(0); + this.comment.set(''); + this.isSubmitting.set(false); + this.loadReviews(); + }, + error: () => { + alert('Failed to post review'); + this.isSubmitting.set(false); + }, + }); } } } diff --git a/app/booking-ui/src/app/pages/doctor-details/doctor-details.html b/app/booking-ui/src/app/pages/doctor-details/doctor-details.html index 3b66b1e..f8e8763 100644 --- a/app/booking-ui/src/app/pages/doctor-details/doctor-details.html +++ b/app/booking-ui/src/app/pages/doctor-details/doctor-details.html @@ -5,27 +5,13 @@

Loading doctor profile...

} @else if (doctor(); as doc) { -
-
+
+
- - -
- -
} @else { diff --git a/app/booking-ui/src/app/pages/doctor-details/doctor-details.scss b/app/booking-ui/src/app/pages/doctor-details/doctor-details.scss index 6f6c8f6..caabad2 100644 --- a/app/booking-ui/src/app/pages/doctor-details/doctor-details.scss +++ b/app/booking-ui/src/app/pages/doctor-details/doctor-details.scss @@ -26,8 +26,25 @@ h2 { .content-grid { display: grid; grid-template-columns: 2fr 1fr; + grid-template-areas: + 'info sidebar' + 'reviews sidebar'; gap: 40px; align-items: start; + + app-doctor-info { + grid-area: info; + } + + .sidebar { + grid-area: sidebar; + position: sticky; + top: 24px; + } + + app-doctor-reviews { + grid-area: reviews; + } } .loading-state, @@ -54,10 +71,14 @@ h2 { @media (max-width: 992px) { .content-grid { grid-template-columns: 1fr; + grid-template-areas: + 'info' + 'sidebar' + 'reviews'; gap: 32px; } - .sidebar .booking-card.sticky { + .sidebar { position: static; } } diff --git a/app/booking-ui/src/app/pages/doctor-details/doctor-details.ts b/app/booking-ui/src/app/pages/doctor-details/doctor-details.ts index 727a82a..63a7c7b 100644 --- a/app/booking-ui/src/app/pages/doctor-details/doctor-details.ts +++ b/app/booking-ui/src/app/pages/doctor-details/doctor-details.ts @@ -1,17 +1,12 @@ import { FormsModule } from '@angular/forms'; import { CommonModule } from '@angular/common'; -import { DoctorDetailsDto, ReviewDto } from '@core/models/doctor.model'; -import { Component, OnInit, effect, inject, signal } from '@angular/core'; -import { ActivatedRoute, Router, RouterLink } from '@angular/router'; -import { - CreateAppointmentRequest, - CreateReviewRequest, - TimeSlot, -} from '@core/models/appointmnet.models'; -import { DoctorService, AppointmentService, AuthService } from '@core/services/index'; +import { Component, OnInit, inject, signal } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; +import { DoctorService } from '@core/services/index'; import { DoctorInfo } from './components/doctor-info/doctor-info'; import { DoctorReviews } from './components/doctor-reviews/doctor-reviews'; import { DoctorBooking } from './components/doctor-booking/doctor-booking'; +import { DoctorResponse } from '@core/models/doctor.model'; @Component({ selector: 'app-doctor-details', @@ -22,161 +17,28 @@ import { DoctorBooking } from './components/doctor-booking/doctor-booking'; }) export class DoctorDetails implements OnInit { private route = inject(ActivatedRoute); - private router = inject(Router); private doctorService = inject(DoctorService); - private appointmentService = inject(AppointmentService); - private authService = inject(AuthService); - - currentUser = this.authService.currentUser; - userRating = signal(0); - hoverRating = signal(0); - reviewText = signal(''); - isSubmittingReview = signal(false); - - reviews = signal([]); - isLoadingReviews = signal(false); + doctor = signal(null); isLoading = signal(true); - isLoadingSlots = signal(false); - isBooking = signal(false); - - selectedDate = signal(new Date().toISOString().split('T')[0]); - selectedSlot = signal(null); - - doctor = signal(null); - availableSlots = signal([]); - - constructor() { - effect(() => { - const doc = this.doctor(); - const date = this.selectedDate(); - - if (doc && date) { - this.loadSlots(doc.id, date); - } - }); - } ngOnInit() { const id = this.route.snapshot.paramMap.get('id'); - if (id) this.loadDoctor(id); - } - - setHoverRating(stars: number) { - this.hoverRating.set(stars); - } - - clearHoverRating() { - this.hoverRating.set(0); - } - - submitReview(event: { rating: number; text: string }) { - const doc = this.doctor(); - if (!doc) return; - - this.isSubmittingReview.set(true); - - const request: CreateReviewRequest = { - doctorId: doc.id, - rating: event.rating, - text: event.text, - }; - - this.doctorService.addReview(request).subscribe({ - next: () => { - alert('Thank you for your review!'); - this.isSubmittingReview.set(false); - this.loadReviews(doc.id); - }, - error: (err) => { - this.isSubmittingReview.set(false); - console.error(err); - }, - }); - } - - loadSlots(doctorId: string, date: string) { - console.log('1. Запуск loadSlots для:', date); - this.isLoadingSlots.set(true); - this.selectedSlot.set(null); - - this.doctorService.getDoctorSlots(doctorId, date).subscribe({ - next: (slots) => { - console.log('2. Ответ сервера (Слоты):', slots); - this.availableSlots.set(slots); - this.isLoadingSlots.set(false); - }, - error: (err) => { - console.error('Error loading slots', err); - this.availableSlots.set([]); - this.isLoadingSlots.set(false); - }, - }); - } - - onDateChange(event: Event) { - const input = event.target as HTMLInputElement; - this.selectedDate.set(input.value); + if (id) { + this.loadDoctor(id); + } } - loadDoctor(id: string) { - this.doctorService.getDoctorProfile(id).subscribe({ + private loadDoctor(id: string) { + this.isLoading.set(true); + this.doctorService.getDoctorById(id).subscribe({ next: (data) => { this.doctor.set(data); this.isLoading.set(false); - this.loadReviews(data.id); - }, - error: () => this.isLoading.set(false), - }); - } - - loadReviews(doctorId: string) { - this.isLoadingReviews.set(true); - this.doctorService.getDoctorReviews(doctorId).subscribe({ - next: (data) => { - this.reviews.set(data); - this.isLoadingReviews.set(false); }, error: (err) => { - console.error('Ошибка загрузки отзывов', err); - this.isLoadingReviews.set(false); - }, - }); - } - - selectSlot(slot: TimeSlot) { - if (slot.isAvailable) { - this.selectedSlot.set(slot); - } - } - - bookAppointment(slot: TimeSlot) { - const doc = this.doctor(); - if (!doc || !slot) return; - - if (!this.authService.currentUser()) { - alert('Please login'); - this.router.navigate(['/auth/login']); - return; - } - - this.isBooking.set(true); - - const request: CreateAppointmentRequest = { - doctorId: doc.id, - startTime: slot.start, - endTime: slot.end, - }; - - this.appointmentService.createAppointment(request).subscribe({ - next: () => { - alert('Success!'); - this.isBooking.set(false); - this.loadSlots(doc.id, this.selectedDate()); - }, - error: (err) => { - alert('Error: ' + err.message); - this.isBooking.set(false); + console.error('Error loading doctor', err); + this.isLoading.set(false); }, }); } diff --git a/app/booking-ui/src/app/pages/home/home.ts b/app/booking-ui/src/app/pages/home/home.ts index 1e7a382..c2d9f78 100644 --- a/app/booking-ui/src/app/pages/home/home.ts +++ b/app/booking-ui/src/app/pages/home/home.ts @@ -2,7 +2,7 @@ import { Component, OnInit, inject, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { DoctorCard, DoctorCardDto } from '@shared/components/doctor-card/doctor-card'; import { DoctorService } from '@core/services/doctor/doctor.service'; -import { DoctorDto } from '@core/models/doctor.model'; +import { DoctorResponse } from '@core/models/doctor.model'; @Component({ selector: 'app-home', @@ -23,7 +23,7 @@ export class Home implements OnInit { loadDoctors() { this.doctorService.getDoctors().subscribe({ - next: (data: DoctorDto[]) => { + next: (data: DoctorResponse[]) => { const mappedDoctors: DoctorCardDto[] = data.slice(0, 4).map((dto) => ({ id: dto.id, name: `Dr. ${dto.name} ${dto.lastname}`,