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(); } }