Bläddra i källkod

Added MyTasks page, but haven't implemented many functions.

Kenric Nugteren 1 vecka sedan
förälder
incheckning
f1a2ddf711

+ 2 - 2
PRS.Avalonia/PRS.Avalonia/HomePage/HomePageViewModel.cs

@@ -51,9 +51,9 @@ public partial class HomePageViewModel : ViewModelBase
         Modules.Add<ViewMobilePurchaseOrdersModule, PurchaseOrdersViewModel>("Orders", "", Images.shoppingcart, isVisible: false);
         Modules.Add<ViewMobileDocumentScannerModule, DocumentScannerViewModel>("Doc Scanner", "", Images.camera, isVisible: false);
         Modules.Add<ViewMobileSiteModule, SiteViewModel>("Site Module", "", Images.construction);
-        Modules.Add<ViewMobileMyTasksModule, MyTasksViewModel>("My Tasks", "", Images.task, isVisible: false, configure: model =>
+        Modules.Add<ViewMobileMyTasksModule, MyTasksViewModel>("My Tasks", "", Images.task, configure: model =>
         {
-            // model.Model = Repositories;
+            model.Model = Repositories.MyTasks;
         });
         Modules.Add<ViewMobileWarehousingModule, WarehouseModuleViewModel>("Warehouse", "", Images.warehouse, isVisible: true);
         Modules.Add("Update App", "", Images.version, UpdateApp, isVisible: false);

+ 1 - 5
PRS.Avalonia/PRS.Avalonia/Modules/DigitalForms/FormsViewModel.cs

@@ -31,11 +31,7 @@ public partial class FormsViewModel : ModuleViewModel
 
     public FormsViewModel()
     {
-        Kanbans = new KanbanModel(DataAccess,
-            () => new Filter<Kanban>(x => x.ID).InQuery(new Filter<KanbanSubscriber>(x => x.Employee.ID).IsEqualTo(Repositories.Me.ID), x => x.Kanban.ID)
-                .And(new Filter<Kanban>(x => x.Status).IsNotEqualTo(KanbanStatus.Complete)
-                    .Or(x => x.Completed).IsGreaterThanOrEqualTo(DateTime.Today.AddDays(-7))),
-            () => DefaultCacheFileName<KanbanShell>());
+        Kanbans = Repositories.MyTasks;
 
         Forms = new KanbanFormModel(DataAccess,
             () => new Filter<KanbanForm>(x => x.Parent.EmployeeLink.ID).IsEqualTo(Repositories.Me.ID)

+ 106 - 1
PRS.Avalonia/PRS.Avalonia/Modules/MyTasks/MyTasksView.axaml

@@ -2,6 +2,111 @@
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+			 xmlns:listView="using:PRS.Avalonia.Components.ListView"
+			 xmlns:local="using:PRS.Avalonia.Modules"
+			 xmlns:classes="using:Comal.Classes"
+			 xmlns:prs="using:PRS.Avalonia"
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
-             x:Class="PRS.Avalonia.MyTasksView">
+             x:Class="PRS.Avalonia.Modules.MyTasksView"
+			 x:DataType="local:MyTasksViewModel">
+	<Grid>
+		<Grid.RowDefinitions>
+			<RowDefinition Height="*"/>
+			<RowDefinition Height="Auto"/>
+		</Grid.RowDefinitions>
+		<Grid.ColumnDefinitions>
+			<ColumnDefinition Width="*"/>
+			<ColumnDefinition Width="Auto"/>
+		</Grid.ColumnDefinitions>
+		<listView:PrsListView Grid.Row="0" Grid.ColumnSpan="2"
+							  Repository="{Binding Model}"
+							  FilterShell="PrsListView_FilterShell"
+							  FiltersVisible="True">
+			<listView:PrsListView.ItemTemplate>
+				<DataTemplate DataType="prs:IKanbanShell">
+					<Button Classes="Standard" Background="{Binding .,Converter={x:Static local:KanbanBackgroundColorConverter.Instance}}"
+							Command="{Binding $parent[local:MyTasksView].((local:MyTasksViewModel)DataContext).KanbanClickedCommand}"
+							CommandParameter="{Binding .}"
+							Padding="0"
+							HorizontalContentAlignment="Stretch">
+						<Grid>
+							<Grid.RowDefinitions>
+								<RowDefinition Height="Auto"/>
+								<RowDefinition Height="Auto"/>
+								<RowDefinition Height="Auto"/>
+							</Grid.RowDefinitions>
+							<Grid.ColumnDefinitions>
+								<ColumnDefinition Width="Auto"/>
+								<ColumnDefinition Width="Auto"/>
+								<ColumnDefinition Width="*"/>
+								<ColumnDefinition Width="Auto"/>
+								<ColumnDefinition Width="Auto"/>
+							</Grid.ColumnDefinitions>
+							
+							<CheckBox Grid.Row="0" Grid.Column="0"
+									  Height="15"
+									  Classes="small"
+									  MinHeight="0"
+									  Padding="0"
+									  IsChecked="{Binding IsSelected}"/>
+							<Label Grid.Row="0" Grid.Column="1"
+								   Content="{Binding Number}"
+								   FontSize="{StaticResource PrsFontSizeSmall}"
+								   FontWeight="{StaticResource PrsFontWeightBold}"
+								   VerticalAlignment="Stretch"
+								   VerticalContentAlignment="Center"
+								   Foreground="{Binding .,Converter={x:Static local:KanbanForegroundColorConverter.Instance}}"/>
+							<Label Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="2"
+								   Content="{Binding Title}"
+								   FontSize="{StaticResource PrsFontSizeSmall}"
+								   FontWeight="{StaticResource PrsFontWeightBold}"
+								   VerticalAlignment="Stretch"
+								   VerticalContentAlignment="Center"
+								   Foreground="{Binding .,Converter={x:Static local:KanbanForegroundColorConverter.Instance}}"/>
+							<Image Grid.Row="0" Grid.Column="4" Width="20" Height="20"
+								   VerticalAlignment="Center"
+								   HorizontalAlignment="Right"
+								   Source="{SvgImage /Images/paperclip.svg}"/>
+							
+							<Label Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="5"
+								   Content="{Binding Summary}"
+								   FontSize="{StaticResource PrsFontSizeExtraSmall}"
+								   VerticalAlignment="Stretch"
+								   VerticalContentAlignment="Center"
+								   Foreground="{Binding .,Converter={x:Static local:KanbanForegroundColorConverter.Instance}}"/>
+							
+							<Label Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3"
+								   Content="{Binding EmployeeName}"
+								   FontSize="{StaticResource PrsFontSizeExtraSmall}"
+								   FontStyle="{StaticResource PrsFontStylItalic}"
+								   VerticalAlignment="Stretch"
+								   VerticalContentAlignment="Center"
+								   Foreground="{Binding .,Converter={x:Static local:KanbanForegroundColorConverter.Instance}}"/>
+							
+							<Label Grid.Row="2" Grid.Column="3" Grid.ColumnSpan="2"
+								   Content="{Binding DueDate,StringFormat='{}{0:dd MMM yy}'}"
+								   FontSize="{StaticResource PrsFontSizeExtraSmall}"
+								   FontStyle="{StaticResource PrsFontStylItalic}"
+								   VerticalAlignment="Stretch"
+								   VerticalContentAlignment="Center"
+								   Foreground="{Binding .,Converter={x:Static local:KanbanForegroundColorConverter.Instance}}"/>
+						</Grid>
+					</Button>
+				</DataTemplate>
+			</listView:PrsListView.ItemTemplate>
+		</listView:PrsListView>
+		<TabStrip Name="_tabList" Classes="Standard"
+				  Grid.Row="1" Grid.Column="0"
+				  SelectionChanged="TabStrip_SelectionChanged"
+				  Margin="{StaticResource PrsControlSpacing}">
+			<TabStripItem Content="New" Tag="{x:Static classes:KanbanStatus.Open}"/>
+			<TabStripItem Content="Current" Tag="{x:Static classes:KanbanStatus.InProgress}"/>
+			<TabStripItem Content="Waiting" Tag="{x:Static classes:KanbanStatus.Waiting}"/>
+			<TabStripItem Content="Done" Tag="{x:Static classes:KanbanStatus.Complete}"/>
+		</TabStrip>
+		<Button Classes="Standard" Name="SelectionMenuButton" Grid.Row="1" Grid.Column="1"
+				Background="Silver"
+				Command="{Binding SelectionMenuCommand}"
+				CommandParameter="{Binding $self}"/>
+	</Grid>
 </UserControl>

+ 107 - 1
PRS.Avalonia/PRS.Avalonia/Modules/MyTasks/MyTasksView.axaml.cs

@@ -1,13 +1,119 @@
 using Avalonia;
 using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
 using Avalonia.Markup.Xaml;
+using Avalonia.Media;
+using Comal.Classes;
+using InABox.Avalonia;
+using InABox.Avalonia.Converters;
+using InABox.Core;
+using System;
+using System.Collections.Generic;
 
-namespace PRS.Avalonia;
+namespace PRS.Avalonia.Modules;
+
+public abstract class KanbanColorConverter : AbstractConverter<IKanbanShell, IBrush>
+{
+    private enum ColorClass
+    {
+        Overdue, 
+        NearlyDue,
+        NotYetDue,
+        Completed,
+        Manager,
+        Observer
+    }
+
+    protected enum ColorType
+    {
+        Background,
+        Foreground
+    }
+
+    protected abstract ColorType Type { get; }
+    
+    private static readonly Dictionary<ColorClass, Func<ColorType, Color>> _colors = 
+        new Dictionary<ColorClass, Func<ColorType, Color>>
+        {
+            { ColorClass.Overdue, (type) => type == ColorType.Background ? Colors.Coral : Colors.Black },
+            { ColorClass.NearlyDue, (type) => type == ColorType.Background ? Colors.LightYellow : Colors.Black },
+            { ColorClass.NotYetDue, (type) => type == ColorType.Background ? Colors.LightGreen : Colors.Black },
+            { ColorClass.Completed, (type) => type == ColorType.Background ? Colors.DimGray : Colors.LightGray },
+            { ColorClass.Manager, (type) => type == ColorType.Background ? Colors.LightGray : Colors.Black },
+            { ColorClass.Observer, (type) => type == ColorType.Background ? Colors.Orchid : Colors.Black },
+        };
+    
+    private Color GetColorByDate(IKanbanShell shell, ColorType type)
+    {
+        return shell.Completed.IsEmpty()
+            ? shell.DueDate > DateTime.Today.AddDays(7)
+                ? _colors[ColorClass.NotYetDue](type)
+                : shell.DueDate > DateTime.Today
+                    ? _colors[ColorClass.NearlyDue](type)
+                    : _colors[ColorClass.Overdue](type)
+            : _colors[ColorClass.Completed](type);
+    }
+
+    protected override IBrush? Convert(IKanbanShell? value, object? parameter = null)
+    {
+        if (value is IKanbanSubscriberShell subscriber)
+        {
+            return new SolidColorBrush(subscriber.IsEmployee
+                ? GetColorByDate(subscriber, Type)
+                : subscriber.IsManager
+                    ? _colors[ColorClass.Manager](Type)
+                    : _colors[ColorClass.Observer](Type));
+        }
+
+        if (value is IKanbanShell kanban)
+            return new SolidColorBrush(GetColorByDate(kanban, Type));
+        
+        return new SolidColorBrush(Colors.Transparent);
+    }
+}
+
+public class KanbanBackgroundColorConverter : KanbanColorConverter
+{
+    public static KanbanBackgroundColorConverter Instance { get; } = new();
+
+    protected override ColorType Type => ColorType.Background;
+}
+
+public class KanbanForegroundColorConverter : KanbanColorConverter
+{
+    public static KanbanForegroundColorConverter Instance { get; } = new();
+
+    protected override ColorType Type => ColorType.Foreground;
+}
 
 public partial class MyTasksView : UserControl
 {
+    private MyTasksViewModel ViewModel = null!;
+
     public MyTasksView()
     {
         InitializeComponent();
     }
+
+    protected override void OnDataContextChanged(EventArgs e)
+    {
+        base.OnDataContextChanged(e);
+        
+        ViewModel = (DataContext as MyTasksViewModel)!;
+    }
+
+    private bool PrsListView_FilterShell(IShell shell)
+    {
+        return ViewModel.FilterShell(shell);
+    }
+
+    private void TabStrip_SelectionChanged(object sender, SelectionChangedEventArgs e)
+    {
+        if (_tabList is null) return;
+
+        if(_tabList.SelectedItem is TabStripItem tab && tab.Tag is KanbanStatus status)
+        {
+            ViewModel.TabChangedCommand?.Execute(status);
+        }
+    }
 }

+ 100 - 1
PRS.Avalonia/PRS.Avalonia/Modules/MyTasks/MyTasksViewModel.cs

@@ -1,5 +1,15 @@
+using Avalonia.Controls;
+using Avalonia.Media;
+using Comal.Classes;
 using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
 using InABox.Avalonia;
+using InABox.Avalonia.Components;
+using InABox.Avalonia.Dialogs;
+using InABox.Core;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
 
 namespace PRS.Avalonia.Modules;
 
@@ -11,5 +21,94 @@ public partial class MyTasksViewModel : ModuleViewModel
     private string _tasksTitle = "My Tasks";
 
     [ObservableProperty]
-    private ICoreRepository _model;
+    private ICoreRepository _model = null!; // Set by configuration
+
+    [ObservableProperty]
+    private bool _selectionMenuVisible;
+
+    [ObservableProperty]
+    private KanbanStatus _status;
+
+    public MyTasksViewModel()
+    {
+        PrimaryMenu.Add(new(Images.plus, AddTask));
+    }
+
+    public bool FilterShell(IShell shell)
+    {
+        if (shell is not IKanbanShell kanban) return false;
+        return kanban.Status == Status;
+    }
+
+    [RelayCommand]
+    private void TabChanged(KanbanStatus status)
+    {
+        Status = status;
+        Model?.SelectNone();
+        SelectionMenuVisible = false;
+    }
+
+    private async Task<bool> AddTask()
+    {
+        await MessageDialog.ShowMessage("Unimplemented");
+        return true;
+    }
+
+    [RelayCommand]
+    private async Task KanbanClicked(IKanbanShell shell)
+    {
+        await MessageDialog.ShowMessage("Unimplemented");
+    }
+
+    protected override async Task<TimeSpan> OnRefresh()
+    {
+        await Model.RefreshAsync(false);
+        return TimeSpan.Zero;
+    }
+
+    [RelayCommand]
+    private void SelectionMenu(Control? control)
+    {
+        var anySelected = Model?.SelectedItems.OfType<IShell>().Any() == true;
+
+        var menu = new ContextMenu();
+
+        if (anySelected)
+        {
+            AvaloniaMenuItem.LoadMenuItems([
+                new CoreMenuItem<IImage>("Reassign Task", null, ReassignTask),
+                new CoreMenuItem<IImage>("Change Status", null, ChangeStatus),
+                new CoreMenuSeparator()
+                ], menu.Items);
+        }
+        AvaloniaMenuItem.LoadMenuItems([
+            new CoreMenuItem<IImage>("Select All", null, SelectAll),
+            new CoreMenuItem<IImage>("Select None", null, SelectNone),
+            ], menu.Items);
+        menu.Open(control);
+    }
+
+    private async Task<bool> ChangeStatus()
+    {
+        await MessageDialog.ShowMessage("Unimplemented");
+        return true;
+    }
+
+    private async Task<bool> ReassignTask()
+    {
+        await MessageDialog.ShowMessage("Unimplemented");
+        return true;
+    }
+
+    private Task<bool> SelectNone()
+    {
+        Model?.SelectNone();
+        return Task.FromResult(true);
+    }
+
+    private Task<bool> SelectAll()
+    {
+        Model?.SelectAll();
+        return Task.FromResult(true);
+    }
 }

+ 5 - 0
PRS.Avalonia/PRS.Avalonia/Repositories/Kanban/BaseKanbanShell.cs

@@ -161,4 +161,9 @@ public abstract class BaseKanbanShell<TModel> : Shell<TModel, Kanban>, IKanbanSh
             .Map(nameof(EquipmentID), x => x.Equipment.ID)
             .Map(nameof(EquipmentDescription), x => x.Equipment.Description);
     }
+
+    public override string[] TextSearchValues()
+    {
+        return [Summary, Number.ToString(), Title];
+    }
 }

+ 10 - 0
PRS.Avalonia/PRS.Avalonia/RepositoryLayer.cs

@@ -37,6 +37,14 @@ public partial class RepositoryLayer : ObservableObject
 
         MyQualifications = new EmployeeQualificationModel(Data,
             () => new Filter<EmployeeQualification>(x => x.Employee.UserLink.ID).IsEqualTo(ClientFactory.UserGuid));
+
+        MyTasks = new KanbanModel(Data,
+            () => new Filter<Kanban>(x => x.ID)
+                .InQuery(new Filter<KanbanSubscriber>(x => x.Employee.ID).IsEqualTo(Me.ID), x => x.Kanban.ID)
+                .And(
+                    new Filter<Kanban>(x => x.Status).IsNotEqualTo(KanbanStatus.Complete)
+                        .Or(x => x.Completed).IsGreaterThanOrEqualTo(DateTime.Today.AddDays(-7))),
+            () => DefaultCacheFileName<KanbanShell>());
         
         Jobs = new JobModel(Data,
             () => JobFilter(),
@@ -59,6 +67,8 @@ public partial class RepositoryLayer : ObservableObject
 
     public EmployeeAlertModel MyAlerts { get; private set; }
 
+    public KanbanModel MyTasks { get; private set; }
+
     public NotificationModel MyNotifications { get; private set; }
 
     public EmployeeQualificationModel MyQualifications { get; private set; }