using System.Collections.ObjectModel;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MySqlConnector;
using UtopiaCanteenSystem.Models;
using UtopiaCanteenSystem.Services;
namespace UtopiaCanteenSystem.ViewModels;
///
/// Admin CRUD for meal schedules in production (hrms.meal_schedule). No SQLite. Load all on open; optional Site filter (local).
///
public partial class MealSchedulesViewModel : ObservableObject
{
private readonly IMealScheduleService _mealScheduleService;
private readonly INavigationService _navigation;
[ObservableProperty]
private ObservableCollection _schedules = new();
/// Full list from production; Schedules is filtered by SelectedSiteFilter.
private List _allSchedules = new();
[ObservableProperty]
private ObservableCollection _siteFilterChoices = new() { "All" };
[ObservableProperty]
private string _selectedSiteFilter = "All";
[ObservableProperty]
private MealSchedule? _selectedSchedule;
[ObservableProperty]
private string _locationSiteId = string.Empty;
[ObservableProperty]
private int _mealSessionIndex; // 0=Breakfast, 1=Lunch, 2=Tea, 3=Dinner
[ObservableProperty]
private string _startTime = "06:00:00";
[ObservableProperty]
private string _endTime = "09:00:00";
[ObservableProperty]
private string _message = string.Empty;
[ObservableProperty]
private bool _isError;
[ObservableProperty]
private bool _isLoading;
public static readonly string[] MealSessionNames = { "Breakfast", "Lunch", "Tea", "Dinner" };
public MealSchedulesViewModel(IMealScheduleService mealScheduleService, INavigationService navigation)
{
_mealScheduleService = mealScheduleService;
_navigation = navigation;
_ = LoadSchedulesAsync();
}
partial void OnSelectedSiteFilterChanged(string value)
{
ApplyFilter();
}
partial void OnSelectedScheduleChanged(MealSchedule? value)
{
if (value == null) return;
LocationSiteId = value.LocationSiteId ?? string.Empty;
MealSessionIndex = value.MealSession;
StartTime = value.StartTime ?? "00:00:00";
EndTime = value.EndTime ?? "23:59:59";
}
private void ApplyFilter()
{
if (string.IsNullOrEmpty(SelectedSiteFilter) || SelectedSiteFilter == "All")
{
Schedules = new ObservableCollection(_allSchedules);
return;
}
var filtered = _allSchedules.Where(s => string.Equals(s.LocationSiteId?.Trim(), SelectedSiteFilter.Trim(), StringComparison.OrdinalIgnoreCase)).ToList();
Schedules = new ObservableCollection(filtered);
}
private async Task LoadSchedulesAsync()
{
IsLoading = true;
Message = string.Empty;
IsError = false;
try
{
var list = await _mealScheduleService.GetAllSchedulesAsync().ConfigureAwait(true);
_allSchedules = list.ToList();
var siteIds = _allSchedules.Select(s => s.LocationSiteId?.Trim() ?? "").Where(s => !string.IsNullOrEmpty(s)).Distinct().OrderBy(s => s, StringComparer.Ordinal).ToList();
SiteFilterChoices = new ObservableCollection(new[] { "All" }.Concat(siteIds));
SelectedSiteFilter = "All";
ApplyFilter();
if (_allSchedules.Count == 0)
Message = "No schedules in production. Add one below (HRMS MySQL must be configured).";
}
catch (Exception ex)
{
IsError = true;
var msg = ex is MySqlException mysql
? $"{mysql.Message}{Environment.NewLine}{Environment.NewLine}{mysql.StackTrace}"
: $"{ex.Message}{Environment.NewLine}{Environment.NewLine}{ex.StackTrace}";
Message = "Could not load schedules from production.";
MessageBox.Show(msg, "Meal Schedules – Load Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
IsLoading = false;
}
}
[RelayCommand]
private void AddNew()
{
SelectedSchedule = null;
LocationSiteId = "02";
MealSessionIndex = 0;
StartTime = "06:00:00";
EndTime = "09:00:00";
Message = string.Empty;
}
[RelayCommand]
private async Task Save()
{
Message = string.Empty;
IsError = false;
if (!TimeSpan.TryParse(StartTime?.Trim(), out var start) || !TimeSpan.TryParse(EndTime?.Trim(), out var end))
{
Message = "Start time and end time must be in HH:mm:ss format.";
IsError = true;
return;
}
if (start >= end)
{
Message = "Start time must be before end time.";
IsError = true;
return;
}
var siteId = (LocationSiteId ?? string.Empty).Trim();
if (string.IsNullOrEmpty(siteId))
{
Message = "Location site ID is required.";
IsError = true;
return;
}
try
{
if (SelectedSchedule != null)
{
var dto = new MealSchedule
{
Id = SelectedSchedule.Id,
LocationSiteId = siteId,
MealSession = MealSessionIndex,
StartTime = StartTime.Trim(),
EndTime = EndTime.Trim()
};
await _mealScheduleService.UpdateAsync(dto).ConfigureAwait(true);
Message = "Schedule updated in production.";
}
else
{
var dto = new MealSchedule
{
LocationSiteId = siteId,
MealSession = MealSessionIndex,
StartTime = StartTime.Trim(),
EndTime = EndTime.Trim()
};
await _mealScheduleService.CreateAsync(dto).ConfigureAwait(true);
Message = "Schedule added to production.";
}
await LoadSchedulesAsync().ConfigureAwait(true);
}
catch (Exception ex)
{
IsError = true;
var msg = ex is MySqlException mysql
? $"{mysql.Message}{Environment.NewLine}{Environment.NewLine}{mysql.StackTrace}"
: $"{ex.Message}{Environment.NewLine}{Environment.NewLine}{ex.StackTrace}";
Message = "Failed to save to production.";
MessageBox.Show(msg, "Meal Schedules – Save Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
[RelayCommand]
private async Task Delete()
{
if (SelectedSchedule == null)
{
Message = "Select a schedule to delete.";
IsError = true;
return;
}
Message = string.Empty;
IsError = false;
try
{
await _mealScheduleService.DeleteAsync(SelectedSchedule.Id).ConfigureAwait(true);
Message = "Schedule deleted from production.";
SelectedSchedule = null;
await LoadSchedulesAsync().ConfigureAwait(true);
}
catch (Exception ex)
{
IsError = true;
var msg = ex is MySqlException mysql
? $"{mysql.Message}{Environment.NewLine}{Environment.NewLine}{mysql.StackTrace}"
: $"{ex.Message}{Environment.NewLine}{Environment.NewLine}{ex.StackTrace}";
Message = "Failed to delete.";
MessageBox.Show(msg, "Meal Schedules – Delete Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
[RelayCommand]
private void Back()
{
_navigation.NavigateToSettings();
}
}