Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/booking-ui/src/app/admin/pages/doctors/doctors.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ <h1>Doctors Management</h1>
<img [src]="doc.imageUrl" class="avatar-img" alt="avatar" />
} @else {
<div class="avatar-placeholder">
{{ (doc.fullName || doc.name || '?').charAt(0).toUpperCase() }}
{{ (doc.name || '?').charAt(0).toUpperCase() }}
</div>
}

<div class="details">
<span class="name">
{{ doc.fullName || doc.name + ' ' + doc.lastname }}
{{ doc.name + ' ' + doc.lastname }}
</span>
<span class="email-hint">{{ doc.specialtyName || 'No Specialty' }}</span>
</div>
Expand Down
11 changes: 6 additions & 5 deletions app/booking-ui/src/app/admin/pages/doctors/doctors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -17,17 +17,18 @@ export class DoctorsComponent implements OnInit {
private specialtyService = inject(SpecialtyService);
private fb = inject(FormBuilder);

doctors = signal<DoctorDto[]>([]);
doctors = signal<DoctorResponse[]>([]);
specialties = signal<SpecialtyDto[]>([]);

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)]],
Expand Down Expand Up @@ -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,
Expand Down
57 changes: 42 additions & 15 deletions app/booking-ui/src/app/core/models/appointmnet.models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
69 changes: 41 additions & 28 deletions app/booking-ui/src/app/core/models/doctor.model.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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;
}
6 changes: 3 additions & 3 deletions app/booking-ui/src/app/core/models/patient.models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
13 changes: 13 additions & 0 deletions app/booking-ui/src/app/core/models/review.model.ts
Original file line number Diff line number Diff line change
@@ -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;
}
5 changes: 5 additions & 0 deletions app/booking-ui/src/app/core/models/specialty.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ export interface SpecialtyDto {
id: string;
name: string;
}

export interface UpdateSpecialtyRequest {
id: string;
name: string;
}
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -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<AppointmentDto[]>(`${this.apiUrl}/doctor-schedule`, { params });
createAppointment(request: CreateAppointmentRequest): Observable<void> {
return this.http.post<void>(this.apiUrl, request);
}

getPatientHistory() {
return this.http.get<AppointmentDto[]>(`${this.apiUrl}/patient-history`);
getAppointmentById(id: string): Observable<AppointmentResponse> {
return this.http.get<AppointmentResponse>(`${this.apiUrl}/${id}`);
}

createAppointment(request: CreateAppointmentRequest) {
return this.http.post<void>(this.apiUrl, request);
cancelAppointment(id: string): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
}

completeAppointment(id: string, medicalNotes: string) {
return this.http.post<void>(`${this.apiUrl}/${id}/complete`, JSON.stringify(medicalNotes), {
headers: { 'Content-Type': 'application/json' },
});
// --- DOCTORS METHODS ---

getDoctorSchedule(start?: string, end?: string): Observable<AppointmentResponse[]> {
let params = new HttpParams();
if (start) params = params.set('start', start);
if (end) params = params.set('end', end);

return this.http.get<AppointmentResponse[]>(`${this.apiUrl}/doctor-schedule`, { params });
}

confirmAppointment(id: string) {
confirmAppointment(id: string): Observable<void> {
return this.http.post<void>(`${this.apiUrl}/${id}/confirm`, {});
}

cancelAppointment(id: string) {
return this.http.delete<void>(`${this.apiUrl}/${id}`);
rescheduleAppointment(id: string, request: RescheduleRequest): Observable<void> {
return this.http.put<void>(`${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<AppointmentDto[]>(`${this.apiUrl}`, { params });
completeAppointment(id: string, data: CompleteAppointmentRequest): Observable<void> {
return this.http.post<void>(`${this.apiUrl}/${id}/complete`, data);
}

// --- PATIENTS METHODS ---

getPatientHistory(): Observable<AppointmentResponse[]> {
return this.http.get<AppointmentResponse[]>(`${this.apiUrl}/patient-history`);
}

// --- Attachments ---

uploadAttachment(appointmentId: string, file: File, type: AttachmentType): Observable<void> {
const formData = new FormData();
formData.append('file', file); // Имя поля должно совпадать с IFormFile в контроллере
formData.append('type', type.toString());

return this.http.post<void>(`${this.apiUrl}/${appointmentId}/attachments`, formData);
}

// DELETE:
deleteAttachment(appointmentId: string, attachmentId: string): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}/${appointmentId}/attachments/${attachmentId}`);
}

// GET: Report
downloadReport(appointmentId: string): Observable<Blob> {
return this.http.get(`${this.apiUrl}/${appointmentId}/report`, {
responseType: 'blob',
});
}
}
Loading
Loading