Перейти к основному содержанию

Машины и резервы

MaquinasScreen​

MaquinasScreen делегирует MaquinasViewModel, который управляет состоянием списка компьютеров, диалоговыми окнами действий и операциями CRUD через MaquinaRepository и ReservaRepository через ServiceLocator.

Состояние модели представления​

class MaquinasViewModel : ViewModel() {
var listaMaquinas by mutableStateOf<List<Maquina>>(emptyList())
var isLoading by mutableStateOf(false)
var showingDialogAdd by mutableStateOf(false)
var maquinaParaMantenimiento by mutableStateOf<Int?>(null)
var maquinaParaReservar by mutableStateOf<Int?>(null)
var fechaSeleccionada by mutableStateOf("")
var horaSeleccionada by mutableStateOf("")
var reservasDeLaMaquina by mutableStateOf<List<Reserva>>(emptyList())
var reservasDelUsuario by mutableStateOf<List<Reserva>>(emptyList())
var reservaExitosa by mutableStateOf<Reserva?>(null)
var errorMessage by mutableStateOf<String?>(null)
var nuevaFoto by mutableStateOf<String?>(null)
// ...
}

Хотя isLoading — это true, на экране вместо фактического содержимого отображается SkeletonList.

Стандартный пользовательский режим​

Пользователь видит полный список доступных машин. Нажав на исправный автомат, вы сможете начать процесс бронирования.

Процесс бронирования:

  1. Для выбора даты отображается DatePickerDialog (Android View).
  2. AppTimePickerDialog (Составление материала 3) отображается для выбора времени начала. Приложение автоматически рассчитывает время окончания, добавляя 30 минут.
  3. Проверка максимальной продолжительности: Параметр onValidate в AppTimePickerDialog проверяет, не превышает ли временной интервал 60 минут. При его превышении выводится сообщение об ошибке и резервирование не подтверждается.
  4. ViewModel параллельно загружает reservasDeLaMaquina и reservasDelUsuario из репозиториев.
  5. Перед подтверждением проверяются два условия:
  • Что машина еще не зарезервирована в этот временной интервал.
  • Что у пользователя еще нет другого резервирования в том же слоте.
  1. Если конфликта нет, ReservaRepository.insert(...) вызывается с estado = true.
  2. После успешной вставки автоматически планируется напоминание с помощью NotificationHelper.rescheduleNextReservationNotification(context, userId).
  3. На несколько секунд отобразится Snackbar с возможностью отменить вновь созданное резервирование. Если пользователь нажимает «Отменить», бронирование удаляется, а запланированное уведомление отменяется.

Ограничение из-за статуса технического обслуживания:

Если на машине установлен operativa = false, отображается значок «Техническое обслуживание», а кнопка резервирования неактивна. На карточке показаны даты начала и окончания периода обслуживания, если он доступен.

Изображения машин​

На карточках отображается фотография машины, если доступно поле foto (Base64). Если изображения нет, отображается заполнитель. Администраторы могут прикрепить изображение при создании нового компьютера или при его редактировании, выбрав его из галереи устройств с помощью ActivityResultContracts.GetContent().

Режим администратора​

Администратору доступны дополнительные функции с каждой карты машины:

Регистрация машины: FAB (FloatingActionButton) открывает диалоговое окно для ввода имени, типа, описания и изображения новой машины. Делегаты MaquinasViewModel.addMachine(...) → MaquinaRepository.insert(...).

Машинное удаление. Кнопка удаления на каждой карте вызывает MaquinasViewModel.deleteMachine(id) → MaquinaRepository.delete(id).

Активация обслуживания: Кнопка с гаечным ключом открывает форму с четырьмя полями (дата начала, время начала, дата окончания и время окончания). После подтверждения ViewModel выполняет следующую последовательность:

  1. MaquinaRepository.activateMaintenance(id, desde, hasta): наберите operativa = false и сохраните диапазон.
  2. Проверьте все активные резервирования для этой машины.
  3. Для каждого резервирования проверьте, не перекрывается ли диапазон [fecha + hora_inicio, fecha + hora_fin] с диапазоном обслуживания.
  4. Перекрывающиеся бронирования автоматически отменяются с помощью ReservaRepository.delete(reserva.id).

Обнаружение перекрытия использует стандартное условие: resInicio < calHasta && resFin > calDesde.

Повторная активация машины: Вызовите MaquinasViewModel.deactivateMaintenance(id) → MaquinaRepository.deactivateMaintenance(id), при этом вводится operativa = true и очищаются поля обслуживания.


Экран резервов​

Перечислите все бронирования с системой фильтрации по критериям:

ФильтрПоведение
ПредстоящиеПоказывает бронирования, время окончания которых еще не наступило
СегодняПоказывает бронирования с датой, равной текущей дате
ПрошлоеПоказывает бронирования, время окончания которых уже прошло
ВсеПоказать все бронирования без фильтра по дате
ДатаОткройте DatePickerDialog для фильтрации по определенной дате

Логика сортировки между «следующим» и «прошлым» основана на полном времени окончания (fecha + hora_fin), а не только на дате. Благодаря этому прошлое бронирование сегодня корректно отображается в фильтре «Прошлое».

Резервирования загружаются из ReservaRepository со встроенным синтаксисом отношений: *, usuarios(*), maquinas(*). Это позволяет отображать имя пользователя и компьютера напрямую без дополнительных запросов.

Представление администратора. Администратор видит резервирования всех пользователей. В каждой строке помимо имени компьютера отображается имя пользователя. Вы также можете отменить любое активное бронирование.

Просмотр пользователя: Пользователь видит только свои бронирования и может отменить активное бронирование.

Бронирования сортируются по возрастанию даты, а затем по времени начала, поэтому ближайшие из них отображаются первыми. Во время загрузки данных отображается SkeletonList.

Экспорт в календарь устройства​

На каждой карточке бронирования имеется кнопка календаря (Icons.Default.CalendarMonth). При нажатии на нее отображается диалоговое окно подтверждения, и после принятия к CalendarContract.Events.CONTENT_URI запускается Intent с действием Intent.ACTION_INSERT. Намерение включает в себя название машины, описание со временем, а также время начала и окончания в миллисекундах. Устройство открывает приложение календаря пользователя для завершения вставки событий.

Отмена и уведомления​

При отмене бронирования с экрана:

  1. Резервирование Supabase с ReservaRepository.delete(id) удалено.
  2. Напоминание, запрограммированное с помощью NotificationHelper.cancelScheduledNotification(context, reservaId), будет отменено.
  3. Напоминание о следующем резервировании в будущем переносится на NotificationHelper.rescheduleNextReservationNotification(context, userId).