LSFE/LSFE.MobApp/ViewModels/Coverage/ActualCoverageVM.cs
2026-07-30 10:31:04 +08:00

239 lines
8.7 KiB
C#

using LSFE.MobApp.Contracts;
using LSFE.MobApp.Entities.Doctor;
using LSFE.MobApp.Entities.Planning;
using LSFE.MobApp.Helpers;
using LSFE.MobApp.Pages.CoveragePlan;
using LSFE.MobApp.Services;
using LSFE.MobApp.Services.Coverage;
using LSFE.MobApp.ViewModels.Common;
using Microsoft.Extensions.Configuration;
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace LSFE.MobApp.ViewModels.Coverage
{
public class ActualCoverageVM : BaseDoctorVM
{
private readonly Doctor _doctor;
private bool _isLoading;
private ObservableCollection<DoctorVM> _doctors;
private readonly Repository<MRRawPlan> _mrRawPlanRepository;
public ActualCoverageVM(
Doctor doctor,
Repository<Doctor> doctorRepository,
Repository<MRRawPlan> mrRawPlanrepository,
ISyncService syncService,
AddressService addressService,
IConfiguration configuration)
: base(doctorRepository, syncService, addressService, configuration)
{
_doctor = doctor;
_mrRawPlanRepository = mrRawPlanrepository;
CloseCommand = new Command(async () => await Close());
CopyEmailCommand = new Command(async () => await CopyEmail());
CallPhoneCommand = new Command(async () => await CallPhone());
StartCoverageCommand = new Command(async () => await StartCoverage());
}
#region Status Properties - Using Helper (DRY!)
public string StatusText => DoctorStatusHelper.GetStatusText(_doctor.Status);
public Color StatusBackgroundColor => DoctorStatusHelper.GetStatusBackgroundColor(_doctor.Status);
public Color StatusTextColor => DoctorStatusHelper.GetStatusTextColor(_doctor.Status);
#endregion
/// <summary>
/// This method is automatically called when status updates are broadcast
/// </summary>
protected override async void OnDoctorStatusUpdated(int doctorId)
{
// Only update if it's this doctor
if (_doctor.DoctorId == doctorId)
{
var updated = await RefreshDoctorFromRepositoryAsync(doctorId);
if (updated != null)
{
_doctor.Status = updated.Status;
// Notify UI to refresh
OnPropertyChanged(nameof(StatusText));
OnPropertyChanged(nameof(StatusBackgroundColor));
OnPropertyChanged(nameof(StatusTextColor));
}
}
}
public ObservableCollection<DoctorVM> Doctors
{
get => _doctors;
set
{
_doctors = value;
OnPropertyChanged();
}
}
#region Details information
public string FullName => $"Dr. {_doctor.FirstName} {_doctor.MiddleInitial} {_doctor.LastName}".Replace(" ", " ").Trim();
public string EmailAddress => _doctor.EmailAddress ?? "N/A";
public string PhoneNo => _doctor.PhoneNo ?? "N/A";
public string LicenseNo => _doctor.LicenseNo ?? "N/A";
public string Specialization => _doctor.SpecializationName ?? "General Practice";
public string InstitutionName => _doctor.InstitutionName ?? "No Institution Assigned";
public string Address => _doctor.Address ?? "No address provided";
public string MaxVisit => $"{_doctor.MaxVisit} doctors";
public string BirthDateFormatted => _doctor.BirthDate.ToString("MMMM dd, yyyy");
public string CreatedDateFormatted => _doctor.CreatedDate.ToString("MMMM dd, yyyy");
public bool HasInstitution => !string.IsNullOrEmpty(_doctor.InstitutionName);
public bool HasAddress => !string.IsNullOrEmpty(_doctor.Address);
#endregion
public bool IsLoading
{
get => _isLoading;
set
{
_isLoading = value;
OnPropertyChanged();
}
}
#region Commands
public ICommand StartCoverageCommand { get; }
public ICommand CloseCommand { get; }
public ICommand CopyEmailCommand { get; }
public ICommand CallPhoneCommand { get; }
#endregion
private async Task StartCoverage()
{
try
{
((Command)StartCoverageCommand).ChangeCanExecute();
if (_doctor.Status == 2)
{
await UtilityVM.ShowAlert(
"Warning:",
$"Failed to start coverage. The visitation for Dr. {_doctor.FirstName} {_doctor.LastName} has already been completed."
);
return;
}
var confirm = await Application.Current.MainPage.DisplayAlert(
"Start Coverage",
"Do you want to begin coverage now?",
"Yes, Start",
"Cancel");
if (!confirm)
{
return;
}
await EnsurePlanInitializedAsync();
}
catch (Exception ex)
{
await UtilityVM.ShowAlert("Error", $"Failed to open Market Product page: {ex.Message}");
}
finally
{
IsLoading = false;
((Command)StartCoverageCommand).ChangeCanExecute();
}
}
private async Task EnsurePlanInitializedAsync()
{
var today = DateTime.Today;
var tomorrow = today.AddDays(1);
var plan = await _mrRawPlanRepository.FirstOrDefaultAsync(
r => r.MRRawPlanId==_doctor.MRRawPlanId && r.DoctorId == _doctor.DoctorId);
var doctor = await _doctorRepository.FirstOrDefaultAsync(d => d.DoctorId == _doctor.DoctorId);
if (plan == null)
{
await Application.Current.MainPage.DisplayAlert("Error", "No plan found for today.", "OK");
return;
}
if (plan.StartTime == null)
{
plan.StartTime = DateTime.Now;
plan.ActualCallDate = today;
var result = await _mrRawPlanRepository.UpdateAsync(plan);
doctor.Status = 1;
await _doctorRepository.UpdateAsync(doctor);
// Broadcast to ALL ViewModels - ONE LINE!
DoctorStatusHelper.BroadcastStatusUpdate(_doctor.DoctorId);
if (result <= 0)
{
await Application.Current.MainPage.DisplayAlert(
"Failed",
"Actual coverage recording failed, please try again!",
"OK");
await Application.Current.MainPage.Navigation.PopAsync();
}
}
var viewVm = new MarketProductVM(
_doctor,
_doctorRepository,
ServiceHelper.GetService<IFileDownloadService>(),
ServiceHelper.GetService<Repository<ProductTransaction>>(),
ServiceHelper.GetService<Repository<InventoryTransaction>>(),
ServiceHelper.GetService<Repository<MRRawPlan>>(),
_syncService,
_configuration,
_addressService);
var viewPage = new MarketProductPage(viewVm);
IsLoading = true;
await Application.Current.MainPage.Navigation.PushAsync(viewPage);
}
private async Task Close()
{
await Application.Current.MainPage.Navigation.PopAsync();
}
private async Task CopyEmail()
{
if (!string.IsNullOrEmpty(_doctor.EmailAddress))
{
await Clipboard.SetTextAsync(_doctor.EmailAddress);
await Application.Current.MainPage.DisplayAlert("Copied", "Email address copied to clipboard", "OK");
}
}
private async Task CallPhone()
{
if (!string.IsNullOrEmpty(_doctor.PhoneNo))
{
try
{
if (PhoneDialer.Default.IsSupported)
{
PhoneDialer.Default.Open(_doctor.PhoneNo);
}
else
{
await Clipboard.SetTextAsync(_doctor.PhoneNo);
await Application.Current.MainPage.DisplayAlert("Phone Number", "Phone number copied to clipboard", "OK");
}
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Error", $"Unable to dial: {ex.Message}", "OK");
}
}
}
}
}