/
kochkatech
/
Queue
Обзор
Документация
Войти
/
kochkatech
/
Queue
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
src/Queue.Admin/ViewModels/TicketsViewModel.cs
287 строк
9 KB
kochkareal
init: Начальная структура проекта и панель администратора
24 июл 2026, 21:42
24 июл 2026, 21:42
cd5152a
Код
Авторство
О чём код?
using System; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.EntityFrameworkCore; using Queue.Core.Enums; using Queue.Core.Models; using Queue.Data; namespace Queue.Admin.ViewModels; // Строка грида: талон + подготовленные для отображения значения (имя услуги/окна, текст статуса). // Ticket — простая доменная модель без навигационных свойств, поэтому подстановки считаются здесь. public class TicketDisplayItem { public Ticket Ticket { get; init; } = null!; public string ServiceName { get; init; } = string.Empty; public string? WindowName { get; init; } public string StatusText { get; init; } = string.Empty; } public partial class TicketsViewModel : ObservableObject { private readonly QueueDbContextFactory _dbContextFactory; public ObservableCollection<TicketDisplayItem> Tickets { get; } = new(); public ObservableCollection<Service> Services { get; } = new(); public ObservableCollection<ServiceWindow> CallWindows { get; } = new(); [ObservableProperty] private Service? selectedNewTicketService; [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(CallCommand))] [NotifyCanExecuteChangedFor(nameof(StartServiceCommand))] [NotifyCanExecuteChangedFor(nameof(FinishCommand))] [NotifyCanExecuteChangedFor(nameof(MarkNoShowCommand))] [NotifyCanExecuteChangedFor(nameof(CancelCommand))] private TicketDisplayItem? selectedTicket; [ObservableProperty] private ServiceWindow? selectedCallWindow; public TicketsViewModel(QueueDbContextFactory dbContextFactory) { _dbContextFactory = dbContextFactory; _ = LoadAsync(); } [RelayCommand] private async Task LoadAsync() { using var db = _dbContextFactory.CreateDbContext(); var previousServiceId = SelectedNewTicketService?.Id; var activeServices = await db.Services.Where(s => s.IsActive).OrderBy(s => s.SortOrder).ToListAsync(); Services.Clear(); foreach (var service in activeServices) { Services.Add(service); } SelectedNewTicketService = previousServiceId.HasValue ? Services.FirstOrDefault(s => s.Id == previousServiceId.Value) ?? Services.FirstOrDefault() : Services.FirstOrDefault(); var allServices = await db.Services.ToListAsync(); var allWindows = await db.Windows.ToListAsync(); var serviceNames = allServices.ToDictionary(s => s.Id, s => s.Name); var windowNames = allWindows.ToDictionary(w => w.Id, w => w.Name); var today = DateTime.Today; var todaysTickets = await db.Tickets .Where(t => t.CreatedAt >= today) .OrderByDescending(t => t.CreatedAt) .ToListAsync(); Tickets.Clear(); foreach (var ticket in todaysTickets) { Tickets.Add(new TicketDisplayItem { Ticket = ticket, ServiceName = serviceNames.TryGetValue(ticket.ServiceId, out var sName) ? sName : "—", WindowName = ticket.WindowId.HasValue && windowNames.TryGetValue(ticket.WindowId.Value, out var wName) ? wName : null, StatusText = ToDisplay(ticket.Status) }); } } [RelayCommand] private async Task CreateTicketAsync() { if (SelectedNewTicketService is null) { return; } using var db = _dbContextFactory.CreateDbContext(); var todayCount = await db.Tickets.CountAsync(t => t.ServiceId == SelectedNewTicketService.Id && t.CreatedAt >= DateTime.Today); var number = $"{SelectedNewTicketService.Code}-{todayCount + 1:D3}"; var ticket = new Ticket { Number = number, ServiceId = SelectedNewTicketService.Id, Status = TicketStatus.Waiting, CreatedAt = DateTime.Now }; db.Tickets.Add(ticket); await db.SaveChangesAsync(); await LoadAsync(); } // Список окон в панели действий зависит от услуги выбранного талона, а не от формы создания. partial void OnSelectedTicketChanged(TicketDisplayItem? value) { _ = UpdateCallWindowsAsync(value?.Ticket.ServiceId); } private async Task UpdateCallWindowsAsync(int? serviceId) { CallWindows.Clear(); if (serviceId is null) { return; } using var db = _dbContextFactory.CreateDbContext(); var service = await db.Services.Include(s => s.Windows).FirstOrDefaultAsync(s => s.Id == serviceId); if (service is null) { return; } foreach (var window in service.Windows.Where(w => w.IsActive)) { CallWindows.Add(window); } SelectedCallWindow = CallWindows.FirstOrDefault(); } private bool CanCall() => SelectedTicket?.Ticket.Status == TicketStatus.Waiting; [RelayCommand(CanExecute = nameof(CanCall))] private async Task CallAsync() { if (SelectedTicket is null) { return; } using var db = _dbContextFactory.CreateDbContext(); var ticket = await db.Tickets.FindAsync(SelectedTicket.Ticket.Id); if (ticket is null) { return; } ticket.Status = TicketStatus.Called; ticket.CalledAt = DateTime.Now; ticket.WindowId = SelectedCallWindow?.Id; await db.SaveChangesAsync(); await LoadAsync(); } private bool CanStartService() => SelectedTicket?.Ticket.Status == TicketStatus.Called; [RelayCommand(CanExecute = nameof(CanStartService))] private async Task StartServiceAsync() { if (SelectedTicket is null) { return; } using var db = _dbContextFactory.CreateDbContext(); var ticket = await db.Tickets.FindAsync(SelectedTicket.Ticket.Id); if (ticket is null) { return; } ticket.Status = TicketStatus.InService; ticket.StartedAt = DateTime.Now; await db.SaveChangesAsync(); await LoadAsync(); } private bool CanFinish() => SelectedTicket?.Ticket.Status == TicketStatus.InService; [RelayCommand(CanExecute = nameof(CanFinish))] private async Task FinishAsync() { if (SelectedTicket is null) { return; } using var db = _dbContextFactory.CreateDbContext(); var ticket = await db.Tickets.FindAsync(SelectedTicket.Ticket.Id); if (ticket is null) { return; } ticket.Status = TicketStatus.Served; ticket.FinishedAt = DateTime.Now; await db.SaveChangesAsync(); await LoadAsync(); } private bool CanMarkNoShow() => SelectedTicket?.Ticket.Status == TicketStatus.Called; [RelayCommand(CanExecute = nameof(CanMarkNoShow))] private async Task MarkNoShowAsync() { if (SelectedTicket is null) { return; } using var db = _dbContextFactory.CreateDbContext(); var ticket = await db.Tickets.FindAsync(SelectedTicket.Ticket.Id); if (ticket is null) { return; } ticket.Status = TicketStatus.NoShow; ticket.FinishedAt = DateTime.Now; await db.SaveChangesAsync(); await LoadAsync(); } private bool CanCancel() => SelectedTicket is not null && (SelectedTicket.Ticket.Status == TicketStatus.Waiting || SelectedTicket.Ticket.Status == TicketStatus.Called || SelectedTicket.Ticket.Status == TicketStatus.InService); [RelayCommand(CanExecute = nameof(CanCancel))] private async Task CancelAsync() { if (SelectedTicket is null) { return; } using var db = _dbContextFactory.CreateDbContext(); var ticket = await db.Tickets.FindAsync(SelectedTicket.Ticket.Id); if (ticket is null) { return; } ticket.Status = TicketStatus.Cancelled; ticket.FinishedAt = DateTime.Now; await db.SaveChangesAsync(); await LoadAsync(); } private static string ToDisplay(TicketStatus status) => status switch { TicketStatus.Waiting => "Ожидает", TicketStatus.Called => "Вызван", TicketStatus.InService => "На обслуживании", TicketStatus.Served => "Обслужен", TicketStatus.NoShow => "Не явился", TicketStatus.Cancelled => "Отменён", _ => status.ToString() }; }