diff --git a/HealthCareABApi/HealthCareABApi.csproj b/HealthCareABApi/HealthCareABApi.csproj index 48d7652..5770088 100644 --- a/HealthCareABApi/HealthCareABApi.csproj +++ b/HealthCareABApi/HealthCareABApi.csproj @@ -9,12 +9,14 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/HealthCareABApi/Models/SmtpSettings.cs b/HealthCareABApi/Models/SmtpSettings.cs new file mode 100644 index 0000000..1581a4a --- /dev/null +++ b/HealthCareABApi/Models/SmtpSettings.cs @@ -0,0 +1,12 @@ +namespace HealthCareABApi.Models +{ + public class SmtpSettings + { + public string Server { get; set; } + public int Port { get; set; } + public string Username { get; set; } + public string Password { get; set; } + public string FromName { get; set; } + public string FromEmail { get; set; } + } +} diff --git a/HealthCareABApi/Program.cs b/HealthCareABApi/Program.cs index 295fd8f..5941fbd 100644 --- a/HealthCareABApi/Program.cs +++ b/HealthCareABApi/Program.cs @@ -8,6 +8,8 @@ using Microsoft.EntityFrameworkCore; using HealthCareABApi.Repositories.Interfaces; using HealthCareABApi.BackgroundJobs; +using HealthCareABApi.Models; +using Microsoft.Extensions.Configuration; var builder = WebApplication.CreateBuilder(args); @@ -15,6 +17,8 @@ builder.Services.AddDbContext(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); +builder.Services.Configure(builder.Configuration.GetSection("SmtpSettings")); + // Register repositories builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -26,6 +30,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); // Register background jobs builder.Services.AddHostedService(); diff --git a/HealthCareABApi/Repositories/Implementations/AppointmentRepository.cs b/HealthCareABApi/Repositories/Implementations/AppointmentRepository.cs index dc10d19..af1d0bf 100644 --- a/HealthCareABApi/Repositories/Implementations/AppointmentRepository.cs +++ b/HealthCareABApi/Repositories/Implementations/AppointmentRepository.cs @@ -1,25 +1,28 @@ -using System; -using HealthCareABApi.Models; -using HealthCareABApi.Repositories.Data; -using Microsoft.EntityFrameworkCore; - -namespace HealthCareABApi.Repositories.Implementations -{ - public class AppointmentRepository : IAppointmentRepository - { - private readonly HealthCareDbContext _Dbcontext; - - public AppointmentRepository(HealthCareDbContext context) - { - _Dbcontext = context; - } - - public async Task> GetAllAsync() - { - return await _Dbcontext.Appointment - .Include(a => a.Patient) - .Include(a => a.Caregiver) - .ToListAsync(); +using System; +using HealthCareABApi.Models; +using HealthCareABApi.Repositories.Data; +using HealthCareABApi.Repositories.Interfaces; +using Microsoft.EntityFrameworkCore; + +namespace HealthCareABApi.Repositories.Implementations +{ + public class AppointmentRepository : IAppointmentRepository + { + private readonly HealthCareDbContext _Dbcontext; + private readonly IEmailService _emailService; + + public AppointmentRepository(HealthCareDbContext context, IEmailService emailService) + { + _Dbcontext = context; + _emailService = emailService; + } + + public async Task> GetAllAsync() + { + return await _Dbcontext.Appointment + .Include(a => a.Patient) + .Include(a => a.Caregiver) + .ToListAsync(); } public async Task GetByIdAsync(int id) @@ -39,76 +42,136 @@ public async Task GetByPatientAndTimeAsync(int patientId, DateTime a.PatientId == patientId && EF.Functions.DateDiffSecond(a.DateTime, localTime) == 0 && // Ignorerar millisekunder i databasen och kollar tiden på sekundnivå a.Status == AppointmentStatus.Scheduled); - } - + } + public async Task> GetCompletedByUserIdAsync(int userId) - { - return await _Dbcontext.Appointment - .Include(x => x.Caregiver) - .Include(x => x.Patient) + { + return await _Dbcontext.Appointment + .Include(x => x.Caregiver) + .Include(x => x.Patient) .Where(x => x.PatientId == userId && x.Status == AppointmentStatus.Completed) - .ToListAsync(); - } + .ToListAsync(); + } + + public async Task CreateAsync(Appointment appointment) + { + if (appointment == null) + { + throw new ArgumentNullException(nameof(appointment), "Appointment is null and will blow up the system in 3........2........1........."); + } + + try + { + var availability = await _Dbcontext.Availability + .FirstOrDefaultAsync(a => + a.Caregiver.Id == appointment.CaregiverId && + a.StartTime <= appointment.DateTime && + a.EndTime > appointment.DateTime && + !a.IsBooked); + + var patient = await _Dbcontext.User.Where(x => x.Id == appointment.PatientId).FirstOrDefaultAsync(); + var careGiver = await _Dbcontext.User.Where(x => x.Id == appointment.CaregiverId).FirstOrDefaultAsync(); + + if (availability == null) + { + throw new InvalidOperationException("No available slot for this time."); + } + + availability.IsBooked = true; + availability.Appointment = appointment; + + await _Dbcontext.Appointment.AddAsync(appointment); + + await _Dbcontext.SaveChangesAsync(); + //await _Dbcontext.Appointment.AddAsync(appointment); + //await _Dbcontext.SaveChangesAsync(); + + var body = $@" + Hello {appointment.Patient.Firstname}, + + Your appointment has been successfully scheduled: + + Patient Name: {patient.Firstname} {patient.Lastname} + Appointment Time: {appointment.DateTime.ToString("f")} + Caregiver: {careGiver.Firstname} {careGiver.Lastname} + + If you have any questions, please contact us at support@healthCareAbExample.com + + The Healthcare AB Team + "; + + + await _emailService.SendEmailAsync(patient.Email, "Appointment Booked", body, $"{patient.Firstname} {patient.Lastname}"); + + } + catch (DbUpdateException ex) + { + throw new InvalidOperationException("Database error while creating appointment", ex); + } + catch (Exception ex) + { + throw new InvalidOperationException("Error creating new appointment", ex); + } + } + + public async Task UpdateAsync(int id, Appointment appointment) + { + var exist = await _Dbcontext.Appointment.Where(a => a.Id == id).Include(x => x.Patient).Include(x => x.Caregiver).FirstOrDefaultAsync(); + + if (exist == null) + { + return false; + } + + + _Dbcontext.Appointment.Entry(exist).CurrentValues.SetValues(appointment); + await _Dbcontext.SaveChangesAsync(); + + var body = $@" + Hello {appointment.Patient.Firstname}, - public async Task CreateAsync(Appointment appointment) - { - if (appointment == null) - { - throw new ArgumentNullException(nameof(appointment), "Appointment is null and will blow up the system in 3........2........1........."); - } + Your appointment has been successfully Updated: - try - { - var availability = await _Dbcontext.Availability - .FirstOrDefaultAsync(a => - a.Caregiver.Id == appointment.CaregiverId && - a.StartTime <= appointment.DateTime && - a.EndTime > appointment.DateTime && - !a.IsBooked); + Patient Name: {appointment.Patient.Firstname} {appointment.Patient.Lastname} + Appointment Time: {appointment.DateTime.ToString("f")} + Caregiver: {appointment.Caregiver.Firstname} {appointment.Caregiver.Lastname} - if (availability == null) - { - throw new InvalidOperationException("No available slot for this time."); - } + If you have any questions, please contact us at support@healthCareAbExample.com - availability.IsBooked = true; - availability.Appointment = appointment; + The Healthcare AB Team + "; - await _Dbcontext.Appointment.AddAsync(appointment); - await _Dbcontext.SaveChangesAsync(); - } - catch (DbUpdateException ex) - { - throw new InvalidOperationException("Database error while creating appointment", ex); - } - catch (Exception ex) + await _emailService.SendEmailAsync(appointment.Patient.Email, "Appointment Updated", body, $"{appointment.Patient.Firstname} {appointment.Patient.Lastname}"); + + return true; + } + + public async Task DeleteAsync(int id) + { + using (var transaction = await _Dbcontext.Database.BeginTransactionAsync()) { - throw new InvalidOperationException("Error creating new appointment", ex); - } - } - public async Task UpdateAsync(int id, Appointment appointment) - { - var exist = await _Dbcontext.Appointment.Where(a => a.Id == id).FirstOrDefaultAsync(); + try + { + var appointment = await _Dbcontext.Appointment.Where(x => x.Id == id).Include(x => x.Patient).Include(x => x.Caregiver).FirstOrDefaultAsync(); - if (exist == null) - { - return false; - } + var body = $@" + Hello {appointment.Patient.Firstname}, - _Dbcontext.Appointment.Entry(exist).CurrentValues.SetValues(appointment); - await _Dbcontext.SaveChangesAsync(); - return true; - } + Your appointment have been deleted: - public async Task DeleteAsync(int id) - { - using (var transaction = await _Dbcontext.Database.BeginTransactionAsync()) - { + Patient Name: {appointment.Patient.Firstname} {appointment.Patient.Lastname} + Appointment Time: {appointment.DateTime.ToString("f")} + Caregiver: {appointment.Caregiver.Firstname} {appointment.Caregiver.Lastname} - try - { + If you have any questions, please contact us at support@healthCareAbExample.com + + The Healthcare AB Team + "; + + await _emailService.SendEmailAsync(appointment.Patient.Email, "Appointment Deleted", body, $"{appointment.Patient.Firstname} {appointment.Patient.Lastname}"); + var relatedAvailability = await _Dbcontext.Availability .Where(a => a.AppointmentId == id) .FirstOrDefaultAsync(); @@ -161,4 +224,4 @@ public async Task> GetScheduledAppointmentsAsync(int us } } } - + diff --git a/HealthCareABApi/Repositories/Interfaces/IEmailService.cs b/HealthCareABApi/Repositories/Interfaces/IEmailService.cs new file mode 100644 index 0000000..b39c6aa --- /dev/null +++ b/HealthCareABApi/Repositories/Interfaces/IEmailService.cs @@ -0,0 +1,7 @@ +namespace HealthCareABApi.Repositories.Interfaces +{ + public interface IEmailService + { + Task SendEmailAsync(string toEmail, string subject, string body, string name); + } +} diff --git a/HealthCareABApi/Services/AppointmentService.cs b/HealthCareABApi/Services/AppointmentService.cs index c203eed..9d371cf 100644 --- a/HealthCareABApi/Services/AppointmentService.cs +++ b/HealthCareABApi/Services/AppointmentService.cs @@ -14,7 +14,6 @@ public AppointmentService(IAppointmentRepository appointmentRepository, IAvailab { _appointmentRepository = appointmentRepository; _availabilityRepository = availabilityRepository; - } public async Task CreateAsync(CreateAppointmentDTO dto) diff --git a/HealthCareABApi/Services/AvailabilityService.cs b/HealthCareABApi/Services/AvailabilityService.cs index b3201ad..d618d7b 100644 --- a/HealthCareABApi/Services/AvailabilityService.cs +++ b/HealthCareABApi/Services/AvailabilityService.cs @@ -11,11 +11,13 @@ namespace HealthCareABApi.Services public class AvailabilityService : IAvailabilityService { private readonly IAvailabilityRepository _availabilityRepository; + private readonly IAppointmentRepository _appointmentRepository; private readonly HealthCareDbContext _Dbcontext; - public AvailabilityService(IAvailabilityRepository availabilityRepository, HealthCareDbContext context) + public AvailabilityService(IAvailabilityRepository availabilityRepository, HealthCareDbContext context, IAppointmentRepository appointmentRepository) { _availabilityRepository = availabilityRepository; + _appointmentRepository = appointmentRepository; _Dbcontext = context; } @@ -175,7 +177,7 @@ public async Task DeleteAsync(int id) if (availability.AppointmentId.HasValue) { - _Dbcontext.Appointment.Remove(availability.Appointment); + await _appointmentRepository.DeleteAsync(availability.AppointmentId.Value); } _Dbcontext.Availability.Remove(availability); diff --git a/HealthCareABApi/Services/EmailService.cs b/HealthCareABApi/Services/EmailService.cs new file mode 100644 index 0000000..df9451e --- /dev/null +++ b/HealthCareABApi/Services/EmailService.cs @@ -0,0 +1,46 @@ +using MimeKit; +using MailKit.Security; +using Microsoft.Extensions.Options; +using HealthCareABApi.Repositories.Interfaces; +using MailKit.Net.Smtp; +using HealthCareABApi.Models; +namespace HealthCareABApi.Services +{ + public class EmailService : IEmailService + { + private readonly SmtpSettings _smtpSettings; + + public EmailService(IOptions smtpSettings) + { + _smtpSettings = smtpSettings.Value; + } + + public async Task SendEmailAsync(string toEmail, string subject, string body, string name) + { + var message = new MimeMessage(); + message.From.Add(new MailboxAddress(_smtpSettings.FromName, _smtpSettings.FromEmail)); + message.To.Add(new MailboxAddress(name, toEmail)); + message.Subject = subject; + + var bodyBuilder = new BodyBuilder + { + HtmlBody = body + }; + message.Body = bodyBuilder.ToMessageBody(); + try + { + using (var client = new SmtpClient()) + { + await client.ConnectAsync(_smtpSettings.Server, _smtpSettings.Port, SecureSocketOptions.StartTls); + await client.AuthenticateAsync(_smtpSettings.Username, _smtpSettings.Password); + await client.SendAsync(message); + await client.DisconnectAsync(true); + } + } + catch (Exception ex) + { + throw new Exception($"Failed to send email: {ex.Message}", ex); + } + } + } +} diff --git a/HealthCareABApi/appsettings.json b/HealthCareABApi/appsettings.json index 262b9f0..7b37f94 100644 --- a/HealthCareABApi/appsettings.json +++ b/HealthCareABApi/appsettings.json @@ -14,5 +14,13 @@ "Microsoft.AspNetCore": "Warning" } }, + "SmtpSettings": { + "Server": "smtp.gmail.com", + "Port": 587, + "Username": "axel.calas.dev@gmail.com", + "Password": "ubiq xspz pxns ogmw", + "FromName": "HealthCare AB", + "FromEmail": "noReply@healthcare.com" + }, "AllowedHosts": "*" }