using System.Collections.ObjectModel; using Microsoft.Extensions.Logging; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using GSS2.Core.Analysis.AmineContent.Database; using GSS2.UI.Core.ViewModels; using GSS2.UI.Core.Services; namespace GSS2.ViewModels.AmineContent; public partial class BrandCalibrationViewModel : ViewModelBase { /// /// Внутренняя модель элементов меню /// public partial class MenuItemViewModel : ViewModelBase { [ObservableProperty] public partial int Id { get; set; } [ObservableProperty] public partial string BrandName { get; set; } [ObservableProperty] public partial string MixtureName { get; set; } public MenuItemViewModel(int id, string brandName, string mixtureName) { Id = id; BrandName = brandName; MixtureName = mixtureName; } } /// /// Логгер /// private readonly ILogger _logger; /// /// Контекст базы данных /// private readonly Context _context; /// /// ViewLocator для получения других View по их моделям /// private readonly DependencyInjectionViewLocator _viewLocator; /// /// Сервис навигации между экранами /// private readonly NavigationService _navigation; /// /// Меню открыто /// [ObservableProperty] public partial bool MenuOpened { get; set; } = false; /// /// Элементы меню /// [ObservableProperty] public partial ObservableCollection MenuItems { get; set; } = new(); /// /// Текущий выбранный элемент меню. Равен -1 если элемент не выбран /// [ObservableProperty] public partial int MenuSelectedIndex { get; set; } = -1; /// /// выбранного элемента /// [ObservableProperty] public partial int SelectedId { get; set; } = 0; /// /// выбранного элемента /// [ObservableProperty] public partial string SelectedUuid { get; set; } = Guid.NewGuid().ToString(); /// /// выбранного элемента /// [ObservableProperty] public partial string SelectedBrandName { get; set; } = ""; /// /// выбранного элемента /// [ObservableProperty] public partial string SelectedMixtureName { get; set; } = ""; /// /// выбранного элемента /// [ObservableProperty] public partial double SelectedMixtureNormalRate { get; set; } = -1; /// /// Можно ли производить перемещение между экранами /// [ObservableProperty] public partial bool CanScroll { get; set; } = true; public BrandCalibrationViewModel( ILogger logger, Context context, DependencyInjectionViewLocator viewLocator, NavigationService navigation ) { // Устанавливаем readonly поля _logger = logger; _context = context; _viewLocator = viewLocator; _navigation = navigation; _logger.LogInformation("Инициализация"); // Получаем записи из базы данных MenuItems = new ObservableCollection(_context.BrandRecords.Select(r => new MenuItemViewModel(r.Id, r.BrandName, r.MixtureName))); if (MenuItems.Count() > 0) // Если записи есть, MenuSelectedIndex = 0; // Выбираем первую запись else // Если записей нет, MenuSelectedIndex = -1; // Устанавливаем, что запись не выбрана // Подписываемся на изменения свойств для обновления CanScroll PropertyChanged += (_, ea) => { if (ea.PropertyName == nameof(MenuOpened)) UpdateCanScroll(); }; _logger.LogInformation("Инициализировано"); } /// /// Вызывается при изменении /// partial void OnMenuSelectedIndexChanged(int value) { // Если значение выходит за верхнюю границу списка элементов меню, то логгируем как ошибку if (value >= MenuItems.Count()) _logger.LogError("Значение {}({}) больше количества элементов {}({})", nameof(MenuSelectedIndex), MenuSelectedIndex, nameof(MenuItems), MenuItems.Count()); // Если значение больше или равно 0, то значит выбран один из элементов меню if (value >= 0 && value < MenuItems.Count()) { _logger.LogTrace("Выбран элемент из списка: {}", value); // Находим запись в базе данных var selectedRecord = _context.BrandRecords.Find(MenuItems[value].Id); // Если запись найдена, то заполняем поля из базы данных if (selectedRecord is not null) { SelectedId = selectedRecord.Id; SelectedUuid = selectedRecord.Uuid; SelectedBrandName = selectedRecord.BrandName; SelectedMixtureName = selectedRecord.MixtureName; SelectedMixtureNormalRate = selectedRecord.MixtureNormalRate; return; } // Если значение не найдено в базе данных else { // Логгируем как ошибку _logger.LogError("Не удалось найти элемент с Id({}) в таблице {}", MenuItems[value].Id, nameof(_context.BrandRecords)); var nonExistingIndex = MenuSelectedIndex; // Сохраняем несуществующий индекс // Устанавливаем, что запись не выбрана, этот метод вызовется повторно и заполнит поля пустыми значениями MenuSelectedIndex = -1; // Удаляем несуществующий элемент из списка MenuItems.RemoveAt(nonExistingIndex); return; } } // Если элемент не выбран, заполняем поля пустыми значениями _logger.LogTrace("Выбран новый элемент"); SelectedId = 0; SelectedUuid = Guid.NewGuid().ToString(); SelectedBrandName = ""; SelectedMixtureName = ""; SelectedMixtureNormalRate = -1; } /// /// Обновить значение /// private void UpdateCanScroll() { CanScroll = !MenuOpened; } // Команды управления меню /// /// Открыть меню /// [RelayCommand] private void OpenMenu() => MenuOpened = true; /// /// Закрыть меню /// [RelayCommand] private void CloseMenu() => MenuOpened = false; /// /// Переключить видимость меню /// [RelayCommand] private void ToggleMenu() => MenuOpened = !MenuOpened; /// /// Создать новую запись /// [RelayCommand] private void CreateRecord() { _logger.LogInformation("Создание новой записи"); // Если уже создана новая запись, то MenuSelectedIndex может быть равен -1 и // при простом присвоении MenuSelectedIndex = -1 событие OnMenuSelectedIndexChanged не вызовется, // потому что событие срабатывает только при изменении значения, а значение было -1 и привилось ему -1, // поэтому если SelectedRecord != -1, то нужно явно вызвать метод OnMenuSelectedIndexChanged if (MenuSelectedIndex != -1) MenuSelectedIndex = -1; else OnMenuSelectedIndexChanged(-1); } /// /// Удалить выбранную запись из базы данных или очистить значения, если запись не выбрана /// [RelayCommand] private void DeleteRecord() { // TODO: Добавить определение и отображение количества затрагиваемых, записей которые будут повреждены при удалении этой записи try { _logger.LogTrace("Удаление записи: {}={}, {}={}, {}={}", nameof(BrandRecord.Id), SelectedId, nameof(BrandRecord.BrandName), SelectedBrandName, nameof(BrandRecord.MixtureName), SelectedMixtureName); // Если запись не выбрана, то просто создаём новую запись if (MenuSelectedIndex < 0) { _logger.LogTrace("Запись не выбрана, создаём новую"); CreateRecord(); return; } // Создаём модель для экрана подтверждения var confirmViewModel = new DeleteConfirmViewModel(_navigation) { Question = "Вы уверены, что хотите удалить запись?" }; // Создаём коллбек который вызовется при экрана подтверждения void OnConfirmCloses(object? sender, bool result) { // Если пользователь отменил удаление, то выходим из коллбека if (!result) { _logger.LogTrace("Пользователь отменил удаление"); return; } // Если запись не выбрана, то просто создаём новую запись if (MenuSelectedIndex < 0) { _logger.LogTrace("Запись не выбрана, создаём новую"); CreateRecord(); return; } // Сохраняем индекс удалённый записи и находим её в базе данных var deletedMenuIndex = MenuSelectedIndex; var deletedRecordId = SelectedId; var deletedRecord = _context.BrandRecords.Find(deletedRecordId); // Создаём новую запись CreateRecord(); // Удаляем запись из списка MenuItems.RemoveAt(deletedMenuIndex); // Если удалённая запись не найдена в базе данных, то логгируем как ошибку if (deletedRecord is null) { _logger.LogError("Не удалось найти элемент с Id({}) в таблице {}", deletedRecordId, nameof(_context.BrandRecords)); return; } // Удаляем запись из базы данных и сохраняем изменения в базе данных _context.BrandRecords.Remove(deletedRecord); _context.SaveChanges(); _logger.LogTrace("Запись удалена"); // Отписываемся от события закрытия экрана подтверждения confirmViewModel.Closes -= OnConfirmCloses; } // Создаём View экрана подтверждения var confirmView = _viewLocator.Build(confirmViewModel); // Если View не найден, то логгируем как ошибку if (confirmView is null) { _logger.LogError("Не удалось найти View для экрана подтверждения"); return; } _logger.LogTrace("Задаём вопрос"); // Подписываемся на событие закрытия экрана подтверждения confirmViewModel.Closes += OnConfirmCloses; // Преходим на экран подтверждения, при закрытии он сам вернёт экран обратно на текущий _navigation.NavigateTo(confirmView); } catch (Exception ex) { _logger.LogError(ex, "Исключение при удалении записи"); } } /// /// Сохранить текущие значения в новую запись или перезаписать значения в выдранной записи /// [RelayCommand] private void SaveRecord() { try { _logger.LogTrace("Сохранение записи: {}={}, {}={}, {}={}", nameof(BrandRecord.Id), SelectedId, nameof(BrandRecord.BrandName), SelectedBrandName, nameof(BrandRecord.MixtureName), SelectedMixtureName); // Если запись не выбрана if (MenuSelectedIndex < 0) { _logger.LogTrace("Создаём новую запись"); // Создаём новую запись var newRecord = new BrandRecord( uuid: SelectedUuid, brandName: SelectedBrandName, mixtureName: SelectedMixtureName, mixtureNormalRate: SelectedMixtureNormalRate ); // Добавляем запись в базу данных var attachedRecord = _context.BrandRecords.Add(newRecord); // Сохраняем изменения в базе данных _context.SaveChanges(); _logger.LogTrace("Запись сохранена"); // Добавляем новый элемент в меню MenuItems.Add(new MenuItemViewModel(attachedRecord.Entity.Id, attachedRecord.Entity.BrandName, attachedRecord.Entity.MixtureName)); // Выбираем последний элемент из меню MenuSelectedIndex = MenuItems.Count() - 1; } // Если запись выбрана else { // Находим запись в базе данных var attachedRecord = _context.BrandRecords.Find(SelectedId); // Если запись не найдена, то логгируем как ошибку if (attachedRecord is null) { _logger.LogError("Не удалось найти элемент с Id({}) в таблице {}", SelectedId, nameof(_context.BrandRecords)); return; } _logger.LogTrace("Записываем изменения в базу данных"); // Если в данных есть изменения, то записываем их if (attachedRecord.BrandName != SelectedBrandName) attachedRecord.BrandName = SelectedBrandName; if (attachedRecord.MixtureName != SelectedMixtureName) attachedRecord.MixtureName = SelectedMixtureName; if (attachedRecord.MixtureNormalRate != SelectedMixtureNormalRate) attachedRecord.MixtureNormalRate = SelectedMixtureNormalRate; // Сохраняем изменения в базе данных _context.SaveChanges(); _logger.LogTrace("Запись сохранена"); // Записываем изменения в меню _logger.LogTrace("Записываем изменения в меню"); MenuItems[MenuSelectedIndex].BrandName = SelectedBrandName; MenuItems[MenuSelectedIndex].MixtureName = SelectedMixtureName; } } catch (Exception ex) { _logger.LogError(ex, "Исключение при сохранении записи"); } } }