@page "/playouts/{Id:int}/alternate-schedules" @using System.Globalization @using System.Text @using ErsatzTV.Application.Channels @using ErsatzTV.Application.Playouts @using ErsatzTV.Application.ProgramSchedules @using ErsatzTV.Core.Scheduling @implements IDisposable @inject NavigationManager NavigationManager @inject ILogger Logger @inject ISnackbar Snackbar @inject IMediator Mediator
Save Alternate Schedules Add Alternate Schedule
@_channelName - Content In priority order from top to bottom. The bottom entry will *always* match all days and all months, as a catch-all. Schedule Days of the Week Days of the Month Months @context.ProgramSchedule.Name @ToDaysOfWeekString(context.DaysOfWeek) @ToDaysOfMonthString(context.DaysOfMonth) @ToMonthsOfYearString(context.MonthsOfYear)
@if (_selectedItem is not null) { Edit Content
Schedule
@foreach (ProgramScheduleViewModel schedule in _schedules) { @schedule.Name }
Limit to Date Range
Start Day
@foreach (int month in Enumerable.Range(1, 12)) { @_dtf.GetMonthName(month) }
@foreach (int day in Enumerable.Range(1, 31)) { @day.ToString() }
@foreach (int year in Enumerable.Range(DateTime.Today.Year, 50)) { @year.ToString() }
End Day
@foreach (int month in Enumerable.Range(1, 12)) { @_dtf.GetMonthName(month) }
@foreach (int day in Enumerable.Range(1, 31)) { @day.ToString() }
@foreach (int year in Enumerable.Range(DateTime.Today.Year, 50)) { @year.ToString() }
Days of the Week
@_dtf.GetDayName(DayOfWeek.Monday) @_dtf.GetDayName(DayOfWeek.Tuesday) @_dtf.GetDayName(DayOfWeek.Wednesday) @_dtf.GetDayName(DayOfWeek.Thursday) @_dtf.GetDayName(DayOfWeek.Friday) @_dtf.GetDayName(DayOfWeek.Saturday) @_dtf.GetDayName(DayOfWeek.Sunday)
Weekdays Weekends
Days of the Month
@foreach (int day in Enumerable.Range(1, 31)) { @day }
Months
@foreach (int month in Enumerable.Range(1, 12)) { @_dtf.GetMonthName(month) }
}
@code { private CancellationTokenSource _cts; private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentCulture.DateTimeFormat; [Parameter] public int Id { get; set; } private string _channelName; private List _items = []; private List _schedules = []; private PlayoutAlternateScheduleEditViewModel _selectedItem; public void Dispose() { _cts?.Cancel(); _cts?.Dispose(); } protected override async Task OnParametersSetAsync() { _cts?.Cancel(); _cts?.Dispose(); _cts = new CancellationTokenSource(); var token = _cts.Token; try { _schedules = await Mediator.Send(new GetAllProgramSchedules(), token) .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList()); _channelName = (await Mediator.Send(new GetChannelNameByPlayoutId(Id), token)).IfNone(string.Empty); List results = await Mediator.Send(new GetPlayoutAlternateSchedules(Id), token); _items = results.Map(ProjectToEditViewModel).ToList(); if (_items.Count == 1) { _selectedItem = _items.Head(); } } catch (OperationCanceledException) { // do nothing } } private PlayoutAlternateScheduleEditViewModel ProjectToEditViewModel(PlayoutAlternateScheduleViewModel item) => new() { Id = item.Id, Index = item.Index, ProgramSchedule = _schedules.Find(vm => vm.Id == item.ProgramScheduleId), DaysOfWeek = item.DaysOfWeek.OrderBy(x => ((int)x + 6) % 7).ToList(), DaysOfMonth = item.DaysOfMonth.ToList(), MonthsOfYear = item.MonthsOfYear.ToList(), LimitToDateRange = item.LimitToDateRange, StartMonth = item.StartMonth, StartDay = item.StartDay, StartYear = item.StartYear, EndMonth = item.EndMonth, EndDay = item.EndDay, EndYear = item.EndYear }; private void SelectWeekdays() { _selectedItem.DaysOfWeek.Clear(); _selectedItem.DaysOfWeek.AddRange( [ DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday ]); } private void SelectWeekends() { _selectedItem.DaysOfWeek.Clear(); _selectedItem.DaysOfWeek.AddRange( [ DayOfWeek.Saturday, DayOfWeek.Sunday ]); } private void AddAlternateSchedule() { var item = new PlayoutAlternateScheduleEditViewModel { Index = _items.Map(i => i.Index).DefaultIfEmpty().Max() + 1, ProgramSchedule = _schedules.Head(), DaysOfWeek = AlternateScheduleSelector.AllDaysOfWeek(), DaysOfMonth = AlternateScheduleSelector.AllDaysOfMonth(), MonthsOfYear = AlternateScheduleSelector.AllMonthsOfYear(), LimitToDateRange = false, StartMonth = 1, StartDay = 1, StartYear = null, EndMonth = 1, EndDay = 1, EndYear = null }; _items.Add(item); _selectedItem = item; } private void RemoveAlternateSchedule(PlayoutAlternateScheduleEditViewModel item) { _selectedItem = null; _items.Remove(item); } private void MoveItemUp(PlayoutAlternateScheduleEditViewModel item) { // swap with lower index PlayoutAlternateScheduleEditViewModel toSwap = _items.OrderByDescending(x => x.Index).First(x => x.Index < item.Index); (toSwap.Index, item.Index) = (item.Index, toSwap.Index); } private void MoveItemDown(PlayoutAlternateScheduleEditViewModel item) { // swap with higher index PlayoutAlternateScheduleEditViewModel toSwap = _items.OrderBy(x => x.Index).First(x => x.Index > item.Index); (toSwap.Index, item.Index) = (item.Index, toSwap.Index); } private async Task SaveChanges() { var items = _items.Map(item => new ReplacePlayoutAlternateSchedule( item.Id, item.Index, item.ProgramSchedule.Id, item.DaysOfWeek, item.DaysOfMonth, item.MonthsOfYear, item.LimitToDateRange, item.StartMonth, item.StartDay, item.StartYear, item.EndMonth, item.EndDay, item.EndYear)).ToList(); Seq errorMessages = await Mediator.Send(new ReplacePlayoutAlternateScheduleItems(Id, items), _cts.Token) .Map(e => e.LeftToSeq()); errorMessages.HeadOrNone().Match( error => { Snackbar.Add($"Unexpected error saving alternate schedules: {error.Value}", Severity.Error); Logger.LogError("Unexpected error saving alternate schedules: {Error}", error.Value); }, () => NavigationManager.NavigateTo("playouts")); } private string ToDaysOfWeekString(List daysOfWeek) { if (daysOfWeek.Count is 0 or 7) { return "*any*"; } daysOfWeek.Sort(); return string.Join(", ", daysOfWeek.Map(_dtf.GetAbbreviatedDayName)); } private string ToDaysOfMonthString(List daysOfMonth) { if (daysOfMonth.Count is 0 or 31) { return "*any*"; } return ToRangeString(daysOfMonth); } private string ToMonthsOfYearString(List monthsOfYear) { if (monthsOfYear.Count is 0 or 12) { return "*any*"; } monthsOfYear.Sort(); return string.Join(", ", monthsOfYear.Map(_dtf.GetAbbreviatedMonthName)); } private static string ToRangeString(List list) { list = list.Distinct().ToList(); list.Sort(); var result = new StringBuilder(); for (var i = 0; i < list.Count; i++) { int temp = list[i]; //add a number result.Append(list[i]); //skip number(s) between a range while (i < list.Count - 1 && list[i + 1] == list[i] + 1) { i++; } //add the range if (temp != list[i]) { result.Append("-").Append(list[i]); } //add comma if (i != list.Count - 1) { result.Append(", "); } } return result.ToString(); } private string SelectedRowClassFunc(PlayoutAlternateScheduleEditViewModel element, int rowNumber) => _selectedItem != null && _selectedItem == element ? "selected" : string.Empty; private void OnSelectedDaysChanged(IEnumerable selectedDays) { _selectedItem.DaysOfWeek.Clear(); _selectedItem.DaysOfWeek.AddRange(selectedDays); } private void OnSelectedDaysChanged(IEnumerable selectedDays) { _selectedItem.DaysOfMonth.Clear(); _selectedItem.DaysOfMonth.AddRange(selectedDays); } private void OnSelectedMonthsChanged(IEnumerable selectedMonths) { _selectedItem.MonthsOfYear.Clear(); _selectedItem.MonthsOfYear.AddRange(selectedMonths); } }