|
|
@@ -2,9 +2,11 @@
|
|
|
using Avalonia.Data.Converters;
|
|
|
using Avalonia.Media;
|
|
|
using Avalonia.Threading;
|
|
|
+using BruTile.Extensions;
|
|
|
using Comal.Classes;
|
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
using CommunityToolkit.Mvvm.Input;
|
|
|
+using DynamicData.Binding;
|
|
|
using InABox.Avalonia;
|
|
|
using InABox.Avalonia.Components;
|
|
|
using InABox.Avalonia.Components.DateSelector;
|
|
|
@@ -13,7 +15,10 @@ using InABox.Clients;
|
|
|
using InABox.Configuration;
|
|
|
using InABox.Core;
|
|
|
using PRS.Avalonia.Components;
|
|
|
+using ReactiveUI;
|
|
|
using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Data.Common;
|
|
|
using System.Linq;
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
@@ -24,6 +29,14 @@ public class AssignmentsModuleSettings : ILocalConfigurationSettings
|
|
|
public DateTime Date { get; set; }
|
|
|
|
|
|
public Guid[] Employees { get; set; }
|
|
|
+
|
|
|
+ public AssignmentsViewType ViewType { get; set; }
|
|
|
+}
|
|
|
+
|
|
|
+public enum AssignmentsViewType
|
|
|
+{
|
|
|
+ Day,
|
|
|
+ Month
|
|
|
}
|
|
|
|
|
|
public partial class AssignmentsViewModel : ModuleViewModel
|
|
|
@@ -57,6 +70,19 @@ public partial class AssignmentsViewModel : ModuleViewModel
|
|
|
[ObservableProperty]
|
|
|
private bool _showColumns;
|
|
|
|
|
|
+ [ObservableProperty]
|
|
|
+ private int _selectedTab;
|
|
|
+
|
|
|
+ [ObservableProperty]
|
|
|
+ private AssignmentsViewType _viewType;
|
|
|
+
|
|
|
+ [ObservableProperty]
|
|
|
+ private (DateTime Start, DateTime End) _dateRange;
|
|
|
+
|
|
|
+ public IEnumerable<AssignmentShell> MonthItems => ViewType == AssignmentsViewType.Month ? Model.Items : [];
|
|
|
+
|
|
|
+ public IEnumerable<AssignmentShell> DayItems => ViewType == AssignmentsViewType.Day ? Model.Items : [];
|
|
|
+
|
|
|
public AssignmentsViewModel()
|
|
|
{
|
|
|
Settings = LocalConfiguration.Load<AssignmentsModuleSettings>();
|
|
|
@@ -65,14 +91,30 @@ public partial class AssignmentsViewModel : ModuleViewModel
|
|
|
Settings.Employees = [Repositories.Me.ID];
|
|
|
}
|
|
|
_date = Settings.Date == DateTime.MinValue ? DateTime.Today : Settings.Date;
|
|
|
- UpdateTitle(_date.ToString("dd MMMM yyyy"));
|
|
|
+ ViewType = Settings.ViewType;
|
|
|
+ UpdateTitle();
|
|
|
|
|
|
Model = new AssignmentModel(DataAccess,
|
|
|
- () => Filter<Assignment>.Where(x => x.Date).IsEqualTo(Date)
|
|
|
+ () => GetDateFilter()
|
|
|
.And(x => x.EmployeeLink.ID).InList(EmployeeIDs));
|
|
|
EmployeeModel = Repositories.Employees();
|
|
|
ActivityModel = new ActivityModel(DataAccess,
|
|
|
() => Filter<EmployeeActivity>.Where(x => x.Employee.ID).InList(EmployeeIDs));
|
|
|
+
|
|
|
+ Model.WhenValueChanged(x => x.Items)
|
|
|
+ .Subscribe(x =>
|
|
|
+ {
|
|
|
+ OnPropertyChanged(nameof(DayItems));
|
|
|
+ OnPropertyChanged(nameof(MonthItems));
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private Filter<Assignment> GetDateFilter()
|
|
|
+ {
|
|
|
+ return ViewType == AssignmentsViewType.Day
|
|
|
+ ? Filter<Assignment>.Where(x => x.Date).IsEqualTo(Date)
|
|
|
+ : Filter<Assignment>.Where(x => x.Date).IsGreaterThanOrEqualTo(DateRange.Start)
|
|
|
+ .And(x => x.Date).IsLessThanOrEqualTo(DateRange.End);
|
|
|
}
|
|
|
|
|
|
protected override async Task<TimeSpan> OnRefresh()
|
|
|
@@ -85,6 +127,41 @@ public partial class AssignmentsViewModel : ModuleViewModel
|
|
|
return TimeSpan.Zero;
|
|
|
}
|
|
|
|
|
|
+ partial void OnDateRangeChanged((DateTime Start, DateTime End) value)
|
|
|
+ {
|
|
|
+ if(ViewType == AssignmentsViewType.Month)
|
|
|
+ {
|
|
|
+ Model.RefreshAsync(true).ErrorIfFail();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void UpdateTitle()
|
|
|
+ {
|
|
|
+ UpdateTitle(Date.ToString(ViewType == AssignmentsViewType.Day ? "dd MMMM yyyy" : "MMMM yyyy"));
|
|
|
+ }
|
|
|
+
|
|
|
+ private bool _holdRefresh = false;
|
|
|
+
|
|
|
+ partial void OnViewTypeChanged(AssignmentsViewType value)
|
|
|
+ {
|
|
|
+ SelectedTab = value == AssignmentsViewType.Day ? 0 : 1;
|
|
|
+ if(value != Settings.ViewType)
|
|
|
+ {
|
|
|
+ Settings.ViewType = value;
|
|
|
+ LocalConfiguration.Save(Settings);
|
|
|
+ }
|
|
|
+ UpdateTitle();
|
|
|
+ if(Model is not null && !_holdRefresh)
|
|
|
+ {
|
|
|
+ Model.RefreshAsync(true).ErrorIfFail();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ partial void OnSelectedTabChanged(int value)
|
|
|
+ {
|
|
|
+ ViewType = value == 0 ? AssignmentsViewType.Day : AssignmentsViewType.Month;
|
|
|
+ }
|
|
|
+
|
|
|
partial void OnEmployeeIDsChanged(Guid[] value)
|
|
|
{
|
|
|
Settings.Employees = value;
|
|
|
@@ -93,10 +170,13 @@ public partial class AssignmentsViewModel : ModuleViewModel
|
|
|
|
|
|
partial void OnDateChanged(DateTime value)
|
|
|
{
|
|
|
- UpdateTitle(value.ToString("dd MMMM yyyy"));
|
|
|
+ UpdateTitle();
|
|
|
Settings.Date = value;
|
|
|
LocalConfiguration.Save(Settings);
|
|
|
- Model.RefreshAsync(true).ErrorIfFail();
|
|
|
+ if(ViewType == AssignmentsViewType.Day && !_holdRefresh)
|
|
|
+ {
|
|
|
+ Model.RefreshAsync(true).ErrorIfFail();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
partial void OnEmployeesChanged(EmployeeShell[] value)
|
|
|
@@ -116,9 +196,9 @@ public partial class AssignmentsViewModel : ModuleViewModel
|
|
|
|
|
|
public void EmptyBlockHeld(TimeSpan start, TimeSpan end, Guid employeeID, CoreMenu<IImage> menu)
|
|
|
{
|
|
|
- menu.AddItem("New Assignment", () => NewAssignment(start, end, employeeID));
|
|
|
+ menu.AddItem("New Assignment", () => NewAssignment(Date, start, end, employeeID));
|
|
|
}
|
|
|
- public async Task<bool> NewAssignment(TimeSpan start, TimeSpan end, Guid employeeID)
|
|
|
+ public async Task<bool> NewAssignment(DateTime date, TimeSpan start, TimeSpan end, Guid employeeID)
|
|
|
{
|
|
|
var activity = (await SelectionViewModel.ExecutePopup<ActivityShell>(model =>
|
|
|
{
|
|
|
@@ -144,7 +224,7 @@ public partial class AssignmentsViewModel : ModuleViewModel
|
|
|
if (activity is null) return false;
|
|
|
|
|
|
var assignment = Model.CreateItem();
|
|
|
- assignment.Date = Date;
|
|
|
+ assignment.Date = date;
|
|
|
assignment.Title = "New Assignment";
|
|
|
|
|
|
assignment.BookedStart = start;
|
|
|
@@ -165,6 +245,98 @@ public partial class AssignmentsViewModel : ModuleViewModel
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+ private Dictionary<Guid, EmployeeRosterItem[]> _rosters = new();
|
|
|
+
|
|
|
+ public async Task<bool> NewAssignment(DateTime date)
|
|
|
+ {
|
|
|
+ EmployeeShell? employee;
|
|
|
+ if(Employees.Length == 0)
|
|
|
+ {
|
|
|
+ await MessageDialog.ShowMessage("No employees selected.");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ else if(Employees.Length == 1)
|
|
|
+ {
|
|
|
+ employee = Employees[0];
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ employee = (await SelectionViewModel.ExecutePopup<EmployeeShell>(model =>
|
|
|
+ {
|
|
|
+ model.Columns.BeginUpdate()
|
|
|
+ .Add(new AvaloniaDataGridTextColumn<EmployeeShell>
|
|
|
+ {
|
|
|
+ Column = x => x.Code,
|
|
|
+ Width = GridLength.Auto,
|
|
|
+ Alignment = TextAlignment.Center
|
|
|
+ })
|
|
|
+ .Add(new AvaloniaDataGridTextColumn<EmployeeShell>
|
|
|
+ {
|
|
|
+ Column = x => x.Name,
|
|
|
+ Width = GridLength.Star,
|
|
|
+ Alignment = TextAlignment.Left
|
|
|
+ })
|
|
|
+ .EndUpdate();
|
|
|
+ }, args =>
|
|
|
+ {
|
|
|
+ return Employees;
|
|
|
+ }))?.FirstOrDefault();
|
|
|
+ }
|
|
|
+ if (employee is null) return false;
|
|
|
+
|
|
|
+ var assignments = Model.Items.Where(x => x.Date == date).ToList();
|
|
|
+ var last = assignments.MaxBy(x => x.EndTime.TimeOfDay);
|
|
|
+ if(last is not null)
|
|
|
+ {
|
|
|
+ var start = last.EndTime.TimeOfDay;
|
|
|
+ var end = start + TimeSpan.FromHours(1);
|
|
|
+ if(end > TimeSpan.FromHours(24))
|
|
|
+ {
|
|
|
+ end = TimeSpan.FromHours(24);
|
|
|
+ }
|
|
|
+ return await NewAssignment(date, start, end, employee.ID);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if(!_rosters.TryGetValue(employee.ID, out var rosterItems))
|
|
|
+ {
|
|
|
+ rosterItems = Client.Query(
|
|
|
+ Filter<EmployeeRosterItem>.Where(x => x.Employee.ID).IsEqualTo(employee.ID),
|
|
|
+ Columns.None<EmployeeRosterItem>()
|
|
|
+ .Add(x => x.Start)
|
|
|
+ .Add(x => x.Finish)
|
|
|
+ .Add(x => x.Enabled),
|
|
|
+ new SortOrder<EmployeeRosterItem>(x => x.Day))
|
|
|
+ .ToArray<EmployeeRosterItem>();
|
|
|
+ _rosters[employee.ID] = rosterItems;
|
|
|
+ }
|
|
|
+
|
|
|
+ var roster = RosterUtils.GetRoster(rosterItems, employee.RosterStart, date)!;
|
|
|
+ if (!roster.Enabled)
|
|
|
+ {
|
|
|
+ return await NewAssignment(date, TimeSpan.FromHours(9), TimeSpan.FromHours(10), employee.ID);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return await NewAssignment(date, roster.Start, roster.Finish, employee.ID);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void EmptyMonthBlockClicked(DateTime date)
|
|
|
+ {
|
|
|
+ _holdRefresh = true;
|
|
|
+ ViewType = AssignmentsViewType.Day;
|
|
|
+ Date = date;
|
|
|
+ _holdRefresh = false;
|
|
|
+ Model.RefreshAsync(true).ErrorIfFail();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void EmptyMonthBlockHeld(DateTime date, CoreMenu<IImage> menu)
|
|
|
+ {
|
|
|
+ menu.AddItem("New Assignment", () => NewAssignment(date));
|
|
|
+ }
|
|
|
+
|
|
|
public async Task BlockClicked(AssignmentShell assignment)
|
|
|
{
|
|
|
await OpenAssignmentClick(assignment);
|
|
|
@@ -247,10 +419,28 @@ public partial class AssignmentsViewModel : ModuleViewModel
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ [RelayCommand]
|
|
|
+ private void Menu()
|
|
|
+ {
|
|
|
+ ViewType = ViewType switch
|
|
|
+ {
|
|
|
+ AssignmentsViewType.Day => AssignmentsViewType.Month,
|
|
|
+ AssignmentsViewType.Month => AssignmentsViewType.Day,
|
|
|
+ _ => AssignmentsViewType.Day
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
[RelayCommand]
|
|
|
private void PreviousDay()
|
|
|
{
|
|
|
- Date = Date.AddDays(-1);
|
|
|
+ if(ViewType == AssignmentsViewType.Day)
|
|
|
+ {
|
|
|
+ Date = Date.AddDays(-1);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Date = Date.AddMonths(-1);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
[RelayCommand]
|
|
|
@@ -269,6 +459,13 @@ public partial class AssignmentsViewModel : ModuleViewModel
|
|
|
[RelayCommand]
|
|
|
private void NextDay()
|
|
|
{
|
|
|
- Date = Date.AddDays(1);
|
|
|
+ if(ViewType == AssignmentsViewType.Day)
|
|
|
+ {
|
|
|
+ Date = Date.AddDays(1);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Date = Date.AddMonths(1);
|
|
|
+ }
|
|
|
}
|
|
|
}
|