@page "/playouts/{Id:int}/templates" @using System.Globalization @using System.Text @using ErsatzTV.Application.Channels @using ErsatzTV.Application.Scheduling @using ErsatzTV.Core.Scheduling @implements IDisposable @inject NavigationManager NavigationManager @inject ILogger Logger @inject ISnackbar Snackbar @inject IMediator Mediator
Save Templates Add Template Preview Calendar
@_channelName - Templates In priority order from top to bottom. Template Days of the Week Days of the Month Months @context.Template?.Name @ToDaysOfWeekString(context.DaysOfWeek) @ToDaysOfMonthString(context.DaysOfMonth) @ToMonthsOfYearString(context.MonthsOfYear)
@if (_selectedItem is not null) { Edit Template
Template Group
@foreach (TemplateGroupViewModel templateGroup in _templateGroups) { @templateGroup.Name }
Template
@foreach (TemplateViewModel template in _templates) { @template.Name }
Deco Template Group
@foreach (DecoTemplateGroupViewModel decoTemplateGroup in _decoTemplateGroups) { @decoTemplateGroup.Name }
Deco Template
@foreach (DecoTemplateViewModel decoTemplate in _decoTemplates) { @decoTemplate.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) }
} else { Preview }
@code { private CancellationTokenSource _cts; private readonly DateTimeFormatInfo _dtf = CultureInfo.CurrentCulture.DateTimeFormat; [Parameter] public int Id { get; set; } private readonly List _templateGroups = []; private readonly List _templates = []; private readonly List _decoTemplateGroups = []; private readonly List _decoTemplates = []; private string _channelName; private List _items = []; private TemplateGroupViewModel _selectedGroup; private DecoTemplateGroupViewModel _selectedDecoTemplateGroup; private PlayoutTemplateEditViewModel _selectedItem; private readonly List _previewItems = []; public void Dispose() { _cts?.Cancel(); _cts?.Dispose(); } protected override async Task OnParametersSetAsync() { _cts?.Cancel(); _cts?.Dispose(); _cts = new CancellationTokenSource(); var token = _cts.Token; try { _channelName = (await Mediator.Send(new GetChannelNameByPlayoutId(Id), token)).IfNone(string.Empty); _templateGroups.Clear(); _templateGroups.AddRange(await Mediator.Send(new GetAllTemplateGroups(), token)); _decoTemplateGroups.Clear(); _decoTemplateGroups.AddRange(await Mediator.Send(new GetAllDecoTemplateGroups(), token)); List results = await Mediator.Send(new GetPlayoutTemplates(Id), token); _items = results.Map(ProjectToEditViewModel).ToList(); if (_items.Count == 1) { await SelectedItemChanged(_items.Head()); } } catch (OperationCanceledException) { // do nothing } } private PlayoutTemplateEditViewModel ProjectToEditViewModel(PlayoutTemplateViewModel item) => new() { Id = item.Id, Template = item.Template, DecoTemplate = item.DecoTemplate, Index = item.Index, DaysOfWeek = item.DaysOfWeek.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 async Task UpdateTemplateGroupItems(TemplateGroupViewModel templateGroup) { _selectedGroup = templateGroup; _templates.Clear(); _templates.AddRange(await Mediator.Send(new GetTemplatesByTemplateGroupId(_selectedGroup.Id), _cts.Token)); } private async Task UpdateDecoTemplateGroupItems(DecoTemplateGroupViewModel decoTemplateGroup) { _selectedDecoTemplateGroup = decoTemplateGroup; _decoTemplates.Clear(); _decoTemplates.AddRange(await Mediator.Send(new GetDecoTemplatesByDecoTemplateGroupId(_selectedDecoTemplateGroup.Id), _cts.Token)); } private async Task SelectedItemChanged(PlayoutTemplateEditViewModel template) { _selectedItem = template; foreach (TemplateGroupViewModel group in Optional(_templateGroups.Find(group => group.Id == _selectedItem?.Template?.TemplateGroupId))) { await UpdateTemplateGroupItems(group); } foreach (DecoTemplateGroupViewModel group in Optional(_decoTemplateGroups.Find(group => group.Id == _selectedItem?.DecoTemplate?.DecoTemplateGroupId))) { await UpdateDecoTemplateGroupItems(group); } } 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 AddTemplate() { var item = new PlayoutTemplateEditViewModel { Index = _items.Map(i => i.Index).DefaultIfEmpty().Max() + 1, DaysOfWeek = AlternateScheduleSelector.AllDaysOfWeek(), DaysOfMonth = AlternateScheduleSelector.AllDaysOfMonth(), MonthsOfYear = AlternateScheduleSelector.AllMonthsOfYear() }; _items.Add(item); _selectedItem = item; } private void DeleteTemplate(PlayoutTemplateEditViewModel item) { _selectedItem = null; _items.Remove(item); } private void MoveItemUp(PlayoutTemplateEditViewModel item) { // swap with lower index PlayoutTemplateEditViewModel toSwap = _items.OrderByDescending(x => x.Index).First(x => x.Index < item.Index); (toSwap.Index, item.Index) = (item.Index, toSwap.Index); } private void MoveItemDown(PlayoutTemplateEditViewModel item) { // swap with higher index PlayoutTemplateEditViewModel toSwap = _items.OrderBy(x => x.Index).First(x => x.Index > item.Index); (toSwap.Index, item.Index) = (item.Index, toSwap.Index); } private async Task SaveChanges() { if (_items.Any(i => i.Template is null)) { Snackbar.Add("Unable to save; item has no template selected", Severity.Error); return; } var items = _items.Map(item => new ReplacePlayoutTemplate( item.Id, item.Index, item.Template.Id, item.DecoTemplate?.Id, item.DaysOfWeek, item.DaysOfMonth, item.MonthsOfYear, item.LimitToDateRange, item.StartMonth, item.StartDay, item.StartYear, item.EndMonth, item.EndDay, item.EndYear)).ToList(); Option maybeError = await Mediator.Send(new ReplacePlayoutTemplateItems(Id, items), _cts.Token); maybeError.Match( error => { Snackbar.Add($"Unexpected error saving playout templates: {error.Value}", Severity.Error); Logger.LogError("Unexpected error saving playout templates: {Error}", error.Value); }, () => NavigationManager.NavigateTo("playouts")); } private async Task PreviewCalendar() { _selectedItem = null; await InvokeAsync(StateHasChanged); } private async Task DateRangeChanged(DateRange dateRange) { await Task.CompletedTask; _previewItems.Clear(); var prioritized = _items.Filter(i => i.Template is not null).OrderBy(t => t.Index).ToList(); if (dateRange.Start.HasValue && dateRange.End.HasValue) { DateTime current = dateRange.Start.Value.Date; while (current <= dateRange.End.Value.Date) { foreach (PlayoutTemplateEditViewModel template in prioritized) { if (template.AppliesToDate(current)) { _previewItems.Add( new CalendarItem { AllDay = true, Start = current, Text = template.Template.Name }); break; } } current = current.AddDays(1); } } } 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(PlayoutTemplateEditViewModel 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); } }