484 lines
19 KiB
C#
484 lines
19 KiB
C#
using LSFE.MobApp.Contracts;
|
|
using LSFE.MobApp.Entities.Doctor;
|
|
using LSFE.MobApp.Entities.Planning;
|
|
using LSFE.MobApp.Helpers;
|
|
using LSFE.MobApp.Models;
|
|
using LSFE.MobApp.Pages.CoveragePlan;
|
|
using LSFE.MobApp.Services;
|
|
using LSFE.MobApp.Services.Coverage;
|
|
using LSFE.MobApp.ViewModels.Common;
|
|
using LSFE.MobApp.Views.CoveragePlan;
|
|
using Microsoft.Extensions.Configuration;
|
|
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Windows.Input;
|
|
|
|
namespace LSFE.MobApp.ViewModels.Coverage
|
|
{
|
|
public class MarketProductVM : BaseDoctorVM
|
|
{
|
|
#region Service and Helper
|
|
private readonly IFileDownloadService _fileDownloadService;
|
|
#endregion
|
|
|
|
#region Repository
|
|
private readonly Repository<ProductTransaction> _productTransRepository;
|
|
private readonly Repository<InventoryTransaction> _inventoryTransRepository;
|
|
private readonly Repository<MRRawPlan> _mrRawPlanRepository;
|
|
#endregion
|
|
|
|
public ObservableCollection<ProductMaterial> Materials { get; set; }
|
|
public ObservableCollection<SamplingItem> Samplings { get; set; }
|
|
private readonly Doctor _doctor;
|
|
public MarketProductVM(Doctor doctor, Repository<Doctor> doctorRepository,
|
|
IFileDownloadService fileDownloadService,
|
|
Repository<ProductTransaction> productTransRepository,
|
|
Repository<InventoryTransaction> inventoryTransRepository,
|
|
Repository<MRRawPlan> mrRawPlanRepository,
|
|
|
|
ISyncService syncService, IConfiguration configuration, AddressService addressService)
|
|
: base(doctorRepository, syncService, addressService, configuration)
|
|
{
|
|
_doctor = doctor;
|
|
_fileDownloadService = fileDownloadService;
|
|
_productTransRepository = productTransRepository;
|
|
_inventoryTransRepository = inventoryTransRepository;
|
|
_mrRawPlanRepository = mrRawPlanRepository;
|
|
|
|
Materials = new ObservableCollection<ProductMaterial>();
|
|
Samplings = new ObservableCollection<SamplingItem>();
|
|
|
|
CloseCommand = new Command(async () => await Close());
|
|
CopyEmailCommand = new Command(async () => await CopyEmail());
|
|
CallPhoneCommand = new Command(async () => await CallPhone());
|
|
DownloadMaterialCommand = new Command<ProductMaterial>(async (material) => await DownloadMaterial(material));
|
|
OpenMaterialCommand = new Command<ProductMaterial>(async (material) => await OpenMaterial(material));
|
|
DownloadAllCommand = new Command(async () => await DownloadAllMaterials());
|
|
CheckAllMaterialsCommand = new Command(CheckAllMaterials);
|
|
CheckAllSamplingCommand = new Command(CheckAllSampling);
|
|
DoctorSignInCommand = new Command(async () => await ProcessDoctorSignIn());
|
|
|
|
_ = LoadMaterials();
|
|
_ = LoadSamplings();
|
|
}
|
|
#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));
|
|
}
|
|
}
|
|
}
|
|
private bool _isLoading;
|
|
public bool IsLoading
|
|
{
|
|
get => _isLoading;
|
|
set
|
|
{
|
|
_isLoading = value;
|
|
OnPropertyChanged();
|
|
}
|
|
}
|
|
#region Dr 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
|
|
|
|
#region Styles
|
|
public static string GetIconEmoji(int iconEmojiId)
|
|
{
|
|
return iconEmojiId switch
|
|
{
|
|
1 => "💊",
|
|
2 => "🍎",
|
|
3 => "🍌",
|
|
4 => "🍓",
|
|
5 => "🍌",
|
|
6 => "💉",
|
|
_ => "🧪"
|
|
};
|
|
}
|
|
#endregion
|
|
|
|
#region Command
|
|
public ICommand CloseCommand { get; }
|
|
public ICommand CopyEmailCommand { get; }
|
|
public ICommand CallPhoneCommand { get; }
|
|
public ICommand DownloadMaterialCommand { get; }
|
|
public ICommand OpenMaterialCommand { get; }
|
|
public ICommand DownloadAllCommand { get; }
|
|
public ICommand CheckAllMaterialsCommand { get; }
|
|
public ICommand CheckAllSamplingCommand { get; }
|
|
public ICommand DoctorSignInCommand { get; }
|
|
#endregion
|
|
|
|
#region Method
|
|
private async Task ProcessDoctorSignIn()
|
|
{
|
|
try
|
|
{
|
|
IsLoading = true;
|
|
|
|
if (_doctor.Status == 2)
|
|
{
|
|
await UtilityVM.ShowAlert(
|
|
"Warning:",
|
|
$"Failed to process the signature. The visitation for Dr. {_doctor.FirstName} {_doctor.LastName} has already been completed."
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Get all checked materials
|
|
var checkedMaterials = Materials.Where(m => m.IsChecked).ToList();
|
|
|
|
// Get all checked sampling items
|
|
var checkedSamplings = Samplings.Where(s => s.IsChecked).ToList();
|
|
|
|
if (!checkedMaterials.Any() && !checkedSamplings.Any())
|
|
{
|
|
await Application.Current.MainPage.DisplayAlert(
|
|
"No Selection",
|
|
"Please select at least one material or sampling item.",
|
|
"OK");
|
|
return;
|
|
}
|
|
|
|
// Build summary message
|
|
var vm = new ConfirmSelectionVM(
|
|
FullName,
|
|
Specialization,
|
|
checkedMaterials,
|
|
checkedSamplings,
|
|
async () =>
|
|
{
|
|
var signatureVM = new DoctorSignatureVM(
|
|
_doctor,
|
|
_addressService,
|
|
_syncService,
|
|
_mrRawPlanRepository,
|
|
_doctorRepository,
|
|
_configuration,
|
|
checkedMaterials,
|
|
checkedSamplings,
|
|
OnSignatureCompleted
|
|
);
|
|
var signaturePage = new DoctorSignaturePage(signatureVM);
|
|
await Application.Current.MainPage.Navigation.PushAsync(signaturePage);
|
|
});
|
|
|
|
await Application.Current.MainPage.Navigation.PushModalAsync(
|
|
new ConfirmSelectionModal(vm)
|
|
);
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await Application.Current.MainPage.DisplayAlert("Error", $"Failed to process: {ex.Message}", "OK");
|
|
}
|
|
finally
|
|
{
|
|
IsLoading = false;
|
|
}
|
|
}
|
|
private async Task LoadSamplings()
|
|
{
|
|
try
|
|
{
|
|
Samplings.Clear();
|
|
|
|
var sampleData = await _inventoryTransRepository.GetAllAsync();
|
|
var today = DateTime.Today;
|
|
var filtered = sampleData.Where(i => i.DoctorId == _doctor.DoctorId &&
|
|
i.AppointmentDate >= today &&
|
|
i.AppointmentDate < today.AddDays(1));
|
|
|
|
foreach (var item in filtered)
|
|
{
|
|
Samplings.Add(new SamplingItem
|
|
{
|
|
InventoryTransId = item.InventoryTransId,
|
|
Description = item.Description,
|
|
Qty = item.QtyIn,
|
|
IconEmoji = GetIconEmoji(item.IconEmojiId),
|
|
IsChecked = false
|
|
});
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Failed to load samplings: {ex.Message}");
|
|
}
|
|
}
|
|
private async Task DownloadMaterial(ProductMaterial material)
|
|
{
|
|
if (material == null || material.IsDownloading) return;
|
|
|
|
try
|
|
{
|
|
material.IsDownloading = true;
|
|
|
|
await _fileDownloadService.DownloadFileAsync(material.FileUrl, material.ProductLink, CancellationToken.None);
|
|
|
|
material.IsDownloaded = true;
|
|
material.IsDownloading = false;
|
|
|
|
await Application.Current.MainPage.DisplayAlert("Success", $"{material.ProductName} downloaded successfully!", "OK");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
material.IsDownloading = false;
|
|
await Application.Current.MainPage.DisplayAlert("Error", $"Failed to download: {ex.Message}", "OK");
|
|
}
|
|
}
|
|
private async Task OpenMaterial(ProductMaterial material)
|
|
{
|
|
try
|
|
{
|
|
if (!material.IsDownloaded)
|
|
{
|
|
var download = await Application.Current.MainPage.DisplayAlert(
|
|
"Not Downloaded",
|
|
"This material needs to be downloaded first. Download now?",
|
|
"Yes", "No");
|
|
|
|
if (download)
|
|
{
|
|
await DownloadMaterial(material);
|
|
}
|
|
return;
|
|
}
|
|
|
|
var localPath = _fileDownloadService.GetLocalFilePath(material.ProductLink);
|
|
|
|
if (File.Exists(localPath))
|
|
{
|
|
await Launcher.OpenAsync(new OpenFileRequest
|
|
{
|
|
File = new ReadOnlyFile(localPath)
|
|
});
|
|
}
|
|
else
|
|
{
|
|
await Application.Current.MainPage.DisplayAlert("Error", "File not found. Please re-download.", "OK");
|
|
material.IsDownloaded = false;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await Application.Current.MainPage.DisplayAlert("Error", $"Failed to open file: {ex.Message}", "OK");
|
|
}
|
|
}
|
|
private async Task DownloadMaterials(ProductMaterial material)
|
|
{
|
|
if (material == null || material.IsDownloading) return;
|
|
|
|
try
|
|
{
|
|
material.IsDownloading = true;
|
|
|
|
await _fileDownloadService.DownloadFileAsync(material.FileUrl, material.ProductLink, CancellationToken.None);
|
|
|
|
material.IsDownloaded = true;
|
|
material.IsDownloading = false;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
material.IsDownloading = false;
|
|
await Application.Current.MainPage.DisplayAlert("Error", $"Failed to download: {ex.Message}", "OK");
|
|
}
|
|
}
|
|
private async Task DownloadAllMaterials()
|
|
{
|
|
try
|
|
{
|
|
await LoadMaterials();
|
|
|
|
var allTransactions = await _productTransRepository.GetAllAsync();
|
|
|
|
foreach (var product in allTransactions)
|
|
{
|
|
var endpoint = _configuration["ApiSettings:Endpoints:GetProductFiles"];
|
|
var fileUrl = $"{endpoint}{Uri.EscapeDataString(product.ProductLink)}";
|
|
|
|
var material = new ProductMaterial
|
|
{
|
|
ProductTransId = product.ProductTransId,
|
|
ProductId = product.ProductId,
|
|
ProductName = product.ProductName,
|
|
ProductLink = product.ProductLink,
|
|
FileUrl = fileUrl,
|
|
FileExtension = Path.GetExtension(product.ProductLink)
|
|
};
|
|
|
|
if (!await _fileDownloadService.IsFileDownloadedAsync(product.ProductLink))
|
|
{
|
|
await DownloadMaterials(material);
|
|
}
|
|
}
|
|
|
|
await Application.Current.MainPage.DisplayAlert("Success", "All materials downloaded!", "OK");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await Application.Current.MainPage.DisplayAlert("Error", $"Failed to download all: {ex.Message}", "OK");
|
|
}
|
|
}
|
|
private async Task LoadMaterials()
|
|
{
|
|
IsLoading = true;
|
|
|
|
try
|
|
{
|
|
await TrySyncAsync();
|
|
|
|
Materials.Clear();
|
|
|
|
// Broadcast to ALL ViewModels - ONE LINE!
|
|
DoctorStatusHelper.BroadcastStatusUpdate(_doctor.DoctorId);
|
|
|
|
var transactions = await _productTransRepository.GetAllAsync();
|
|
|
|
var displayList = transactions
|
|
.Where(p => p.DoctorId == _doctor.DoctorId && p.MRRawPlanId < _doctor.MRRawPlanId)
|
|
.ToList();
|
|
|
|
// Load materials in parallel
|
|
var materialTasks = displayList.Select(async product =>
|
|
{
|
|
var fileExtension = Path.GetExtension(product.ProductLink);
|
|
var endpoint = _configuration["ApiSettings:Endpoints:GetProductFiles"];
|
|
var fileUrl = $"{endpoint}{Uri.EscapeDataString(product.ProductLink)}";
|
|
var isDownloaded = await _fileDownloadService.IsFileDownloadedAsync(product.ProductLink);
|
|
|
|
return new ProductMaterial
|
|
{
|
|
ProductTransId = product.ProductTransId,
|
|
ProductId = product.ProductId,
|
|
ProductName = product.ProductName,
|
|
ProductLink = product.ProductLink,
|
|
FileUrl = fileUrl,
|
|
FileExtension = fileExtension,
|
|
IsDownloaded = isDownloaded,
|
|
IsChecked = false
|
|
};
|
|
});
|
|
|
|
var materials = await Task.WhenAll(materialTasks);
|
|
foreach (var m in materials)
|
|
Materials.Add(m);
|
|
}
|
|
finally
|
|
{
|
|
IsLoading = false;
|
|
}
|
|
}
|
|
private async Task TrySyncAsync()
|
|
{
|
|
try
|
|
{
|
|
await _syncService.SyncPullProductsAsync();
|
|
await _syncService.SyncPullSamplesAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Sync failed: {ex.Message}");
|
|
}
|
|
}
|
|
private void CheckAllMaterials()
|
|
{
|
|
bool allChecked = Materials.All(m => m.IsChecked);
|
|
foreach (var material in Materials)
|
|
{
|
|
material.IsChecked = !allChecked;
|
|
}
|
|
}
|
|
private void CheckAllSampling()
|
|
{
|
|
bool allChecked = Samplings.All(s => s.IsChecked);
|
|
foreach (var sampling in Samplings)
|
|
{
|
|
sampling.IsChecked = !allChecked;
|
|
}
|
|
}
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
private void OnSignatureCompleted(byte[] signatureBytes, string signatureFilePath)
|
|
{
|
|
// Handle the completed signature here
|
|
// You can save to database, upload to server, etc.
|
|
|
|
// Example: Save the transaction record
|
|
// var transaction = new DoctorTransaction
|
|
// {
|
|
// DoctorId = _doctor.DoctorId,
|
|
// SignatureFilePath = signatureFilePath,
|
|
// TransactionDate = DateTime.Now,
|
|
// Materials = checkedMaterials,
|
|
// Samplings = checkedSamplings
|
|
// };
|
|
// await _repository.SaveAsync(transaction);
|
|
}
|
|
#endregion
|
|
}
|
|
} |