浏览代码

PRS MOBILE - DataGrid improvements, added JobFormsGrid, KanbanGrid and ViewCellGrid

Nick-PRSDigital@bitbucket.org 3 年之前
父节点
当前提交
911ccd49fc

+ 2 - 2
prs.mobile/comal.timesheets/DataGrid/Host Page/DataGridHost.xaml

@@ -14,8 +14,8 @@
             <Label Grid.Column="1" VerticalOptions="Center" x:Name="titleLbl"
                    HorizontalOptions="Center" HorizontalTextAlignment="Center" TextColor="White" FontSize="Medium" FontAttributes="Bold"/>
             <Button Grid.Column="2" HorizontalOptions="End" VerticalOptions="Center" TextColor="White" BackgroundColor="Transparent" Margin="0" Padding="0"
-                    IsVisible="false" x:Name="saveBtn"
-                Text="Accept" Clicked="SaveBtn_Clicked"/>
+                    IsVisible="false" x:Name="actionBtn"
+                Text="Accept" Clicked="ActionBtn_Clicked"/>
         </Grid>
     </NavigationPage.TitleView>
     <ContentPage.Content>

+ 19 - 3
prs.mobile/comal.timesheets/DataGrid/Host Page/DataGridHost.xaml.cs

@@ -1,4 +1,5 @@
-using System;
+using Org.BouncyCastle.Asn1.BC;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -29,16 +30,26 @@ namespace comal.timesheets
             Device.BeginInvokeOnMainThread(() =>
             {
                 titleLbl.Text = title + " List";
-                saveBtn.IsVisible = savetype == DataGridSaveType.None ? false : true;
+                actionBtn.IsVisible = savetype == DataGridSaveType.None ? false : true;
             });
         }
 
+        public void DisableActionBtn()
+        { 
+            actionBtn.IsVisible = false;
+        }
+
+        public void SetActionButton(string text)
+        {
+            actionBtn.Text = text;
+        }
+
         private void CancelBtn_Clicked(object sender, EventArgs e)
         {
             Navigation.PopAsync();
         }
 
-        private void SaveBtn_Clicked(object sender, EventArgs e)
+        private void ActionBtn_Clicked(object sender, EventArgs e)
         {
             OnSaved?.Invoke(CreateSelectedList());
         }
@@ -53,5 +64,10 @@ namespace comal.timesheets
             }
             return newlist;
         }
+
+        public void RefreshGrid()
+        { 
+            
+        }
     }
 }

+ 47 - 13
prs.mobile/comal.timesheets/DataGrid/MobileDataGrid.xaml.cs

@@ -6,6 +6,7 @@ using Syncfusion.XForms.PopupLayout;
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Collections.Specialized;
 using System.IO;
 using System.Linq;
 using System.Reflection;
@@ -22,7 +23,7 @@ namespace comal.timesheets
     }
 
     public delegate void DataGridOptionsSet(string title, DataGridSaveType savetype = DataGridSaveType.None);
-    public delegate void DataGridItemSelected(DataGridViewModelItem item);
+    public delegate object DataGridItemSelected(DataGridViewModelItem item);
     [XamlCompilation(XamlCompilationOptions.Compile)]
     public partial class MobileDataGrid : ContentView
     {
@@ -48,7 +49,7 @@ namespace comal.timesheets
             popupLayout = new SfPopupLayout();
             popupLayout.PopupView.WidthRequest = 600;
             popupLayout.PopupView.HeightRequest = 600;
-            popupLayout.PopupView.HeaderTitle = "Image";
+            popupLayout.PopupView.HeaderTitle = "Detail";
             popupLayout.PopupView.AcceptButtonText = "Close";
         }
 
@@ -67,10 +68,12 @@ namespace comal.timesheets
         }
 
         /// <summary>
-        /// Never pass CurrentItems into this function - create an intermediate list first
+        /// Never pass CurrentItems into this function 
+        /// - Create an intermediate list first
+        /// - CurrentItems gets cleared in order to be reset properly
         /// </summary>
         /// <param name="items"></param>
-        private void Refresh(List<DataGridViewModelItem> items)
+        public void Refresh(List<DataGridViewModelItem> items, bool RefreshFromExternal = false)
         {
             itemsListView.ItemsSource = items;
             countLbl.Text = items.Count + " Records";
@@ -104,6 +107,8 @@ namespace comal.timesheets
             SelectListViewTemplate(item);
         }
 
+        //only way to reliably get the desired layout/correct bindings dynamically - viewcells are predefined in DataGrid/View/Templates folder.
+        //Mobiles will only ever want 4 columns maximum, so the predefined templates are a small tradeoff
         private void SelectListViewTemplate(DataGridViewModelItem item)
         {
             var template = new DataTemplate();
@@ -112,12 +117,12 @@ namespace comal.timesheets
                 switch (item.Data.Count)
                 {
                     case 2:
-                        template = new DataTemplate(() => 
+                        template = new DataTemplate(() =>
                         {
                             var cell = new ViewCell2Columns();
                             cell.OnCellTapped += Row_Tapped;
                             return cell;
-                        });                    
+                        });
                         break;
                     case 3:
                         template = new DataTemplate(() =>
@@ -158,7 +163,7 @@ namespace comal.timesheets
                             cell.OnImageTapped += Image_Tapped;
                             return cell;
                         });
-                        break;                   
+                        break;
                 }
             }
             itemsListView.ItemTemplate = template;
@@ -232,12 +237,25 @@ namespace comal.timesheets
         #endregion
 
         #region Events
+        /// <summary>
+        /// Depending on the selection mode of the supplied entity grid, tap will do different things
+        /// - Does not apply to images
+        /// - Multi select mode adds to selection (or removes it if already selected)
+        /// - Single select mode adds to selection (or removes it if already selected), removes any other selected items
+        /// - None does not select or deselect
+        /// 
+        /// - NOTE that Item Selected event can return an object - e.g. a view to be displayed in a popup (since grids don't have access to popups)
+        /// - This allows different selection reactions to be defined at the grid level, or even different reactions for the same grid depending on where it is called from
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e"></param>
         private void Row_Tapped(object sender, EventArgs e)
         {
             var item = itemsListView.SelectedItem as DataGridViewModelItem;
             if (item == null)
                 return;
-            if (SaveType != DataGridSaveType.None)
+
+            if (SaveType == DataGridSaveType.Single || SaveType == DataGridSaveType.Multiple)
             {
                 switch (SaveType)
                 {
@@ -255,9 +273,23 @@ namespace comal.timesheets
                     list.Add(i);
                 Refresh(list);
             }
-            else if (SaveType == DataGridSaveType.None)
+
+            var obj = OnItemSelected?.Invoke(item);
+            ProcessReturnObj(obj, item);
+        }
+
+        private void ProcessReturnObj(object obj, DataGridViewModelItem item)
+        {
+            if (obj == null) 
+                return;
+
+            if (obj is View)
             {
-                OnItemSelected?.Invoke(item);
+                popupLayout.PopupView.ContentTemplate = new DataTemplate(() =>
+                {
+                    return obj;
+                });
+                Device.BeginInvokeOnMainThread(() => { popupLayout.Show(); });
             }
         }
 
@@ -337,7 +369,6 @@ namespace comal.timesheets
             }
         }
 
-
         private void Filters_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
         {
             if (bSearching)
@@ -533,7 +564,7 @@ namespace comal.timesheets
                             || GetStringValue(property, item).Contains(value.ToLower())
                             || GetStringValue(property, item).Contains(SearchUtils.LowerCaseFirst(value))
                             || GetStringValue(property, item).Contains(SearchUtils.UpperCaseFirst(value))
-                            || GetStringValue(property, item).Contains(SearchUtils.UpperCaseSecond(value))
+                            || GetStringValue(property, item).Contains(SearchUtils.UpperCaseSecond(value))                          
                             )
                         {
                             if (!intermediatelist.Contains(item))
@@ -657,7 +688,9 @@ namespace comal.timesheets
         public bool ImageColVisible { get; set; }
         public bool Col3IsVisible { get; set; }
 
-        public DataGridViewModelItem(Guid id, List<Tuple<string, string>> data, Image image = null, Guid imageid = new Guid())
+        public string ExtraDetail { get; set; }
+
+        public DataGridViewModelItem(Guid id, List<Tuple<string, string>> data, Image image = null, Guid imageid = new Guid(), string extradetail = "")
         {
             ID = id;
             ImageID = imageid;
@@ -676,6 +709,7 @@ namespace comal.timesheets
             ColWidth3 = data.Count > 3 ? new GridLength(1, GridUnitType.Star) : new GridLength(0, GridUnitType.Auto);
             Col3IsVisible = data.Count > 3 ? true : false;
             IsSelected = false;
+            ExtraDetail = extradetail;
         }
     }
 }

+ 2 - 2
prs.mobile/comal.timesheets/DataGrid/Views/Templates/ViewCell2Columns.xaml

@@ -9,8 +9,8 @@
                 <ColumnDefinition Width="{Binding ColWidth1}"/>
             </Grid.ColumnDefinitions>
 
-            <Label Grid.Column="0" Text="{Binding Col0}" Margin="5, 0, 5, 0" FontSize="16"/>
-            <Label Grid.Column="1" Text="{Binding Col1}" Margin="5, 0, 5, 0" FontSize="16"/>
+            <Label Grid.Column="0" Text="{Binding Col0}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
+            <Label Grid.Column="1" Text="{Binding Col1}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
 
         </Grid>
   </ViewCell.View>

+ 2 - 2
prs.mobile/comal.timesheets/DataGrid/Views/Templates/ViewCell2ColumnsWithImage.xaml

@@ -10,8 +10,8 @@
                 <ColumnDefinition Width="{Binding ImgColWidth}"/>
             </Grid.ColumnDefinitions>
 
-            <Label Grid.Column="0" Text="{Binding Col0}" Margin="5, 0, 5, 0" FontSize="16"/>
-            <Label Grid.Column="1" Text="{Binding Col1}" Margin="5, 0, 5, 0" FontSize="16"/>
+            <Label Grid.Column="0" Text="{Binding Col0}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
+            <Label Grid.Column="1" Text="{Binding Col1}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
 
             <Grid Grid.Column="2" HorizontalOptions="Center" VerticalOptions="Center" RowSpacing="0" ColumnSpacing="0" Padding="0" Margin="0">
                 <Grid.RowDefinitions>

+ 3 - 3
prs.mobile/comal.timesheets/DataGrid/Views/Templates/ViewCell3Columns.xaml

@@ -9,9 +9,9 @@
                 <ColumnDefinition Width="{Binding ColWidth1}"/>
                 <ColumnDefinition Width="{Binding ColWidth2}"/>
             </Grid.ColumnDefinitions>
-            <Label Grid.Column="0" Text="{Binding Col0}" Margin="5, 0, 5, 0" FontSize="16"/>
-            <Label Grid.Column="1" Text="{Binding Col1}" Margin="5, 0, 5, 0" FontSize="16"/>
-            <Label Grid.Column="2" Text="{Binding Col2}" Margin="5, 0, 5, 0" FontSize="16"/>
+            <Label Grid.Column="0" Text="{Binding Col0}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
+            <Label Grid.Column="1" Text="{Binding Col1}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
+            <Label Grid.Column="2" Text="{Binding Col2}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
         </Grid>
     </ViewCell.View>
 </ViewCell>

+ 3 - 3
prs.mobile/comal.timesheets/DataGrid/Views/Templates/ViewCell3ColumnsWithImage.xaml

@@ -11,9 +11,9 @@
                 <ColumnDefinition Width="{Binding ImgColWidth}"/>
             </Grid.ColumnDefinitions>
 
-            <Label Grid.Column="0" Text="{Binding Col0}" Margin="5, 0, 5, 0" FontSize="16"/>
-            <Label Grid.Column="1" Text="{Binding Col1}" Margin="5, 0, 5, 0" FontSize="16"/>
-            <Label Grid.Column="2" Text="{Binding Col2}" Margin="5, 0, 5, 0" FontSize="16"/>
+            <Label Grid.Column="0" Text="{Binding Col0}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
+            <Label Grid.Column="1" Text="{Binding Col1}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
+            <Label Grid.Column="2" Text="{Binding Col2}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
 
             <Grid Grid.Column="3" HorizontalOptions="Center" VerticalOptions="Center" RowSpacing="0" ColumnSpacing="0" Padding="0" Margin="0">
                 <Grid.RowDefinitions>

+ 4 - 4
prs.mobile/comal.timesheets/DataGrid/Views/Templates/ViewCell4Columns.xaml

@@ -10,10 +10,10 @@
                 <ColumnDefinition Width="{Binding ColWidth2}"/>
                 <ColumnDefinition Width="{Binding ColWidth3}"/>
             </Grid.ColumnDefinitions>
-            <Label Grid.Column="0" Text="{Binding Col0}" Margin="5, 0, 5, 0" FontSize="16"/>
-            <Label Grid.Column="1" Text="{Binding Col1}" Margin="5, 0, 5, 0" FontSize="16"/>
-            <Label Grid.Column="2" Text="{Binding Col2}" Margin="5, 0, 5, 0" FontSize="16"/>
-            <Label Grid.Column="3" Text="{Binding Col3}" Margin="5, 0, 5, 0" FontSize="16"/>
+            <Label Grid.Column="0" Text="{Binding Col0}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
+            <Label Grid.Column="1" Text="{Binding Col1}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
+            <Label Grid.Column="2" Text="{Binding Col2}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
+            <Label Grid.Column="3" Text="{Binding Col3}" Margin="5, 0, 5, 0" FontSize="16" MaxLines="8"/>
         </Grid>
     </ViewCell.View>
 </ViewCell>

+ 19 - 8
prs.mobile/comal.timesheets/DigitalForms/DigitalFormHost.xaml.cs

@@ -24,15 +24,15 @@ namespace comal.timesheets
             Changed = changed;
         }
     }
-    
+
     public delegate void DigitalFormsHostClosingDelegate(object sender, DigitalFormsHostClosingArgs args);
-    
+
     [XamlCompilation(XamlCompilationOptions.Compile)]
     public partial class DigitalFormHost : ContentPage
     {
 
         public event DigitalFormsHostClosingDelegate OnClosing;
-        
+
         public IDigitalFormHostModel Model { get; private set; }
         QAFormViewer viewer;
         bool readOnly = false;
@@ -49,9 +49,19 @@ namespace comal.timesheets
                 Model.SetPropertyValues(viewer);
             };
 
-            Model.OnDigitalFormHostModelSaved += () =>
+            Model.OnDigitalFormHostModelSaved += async (responseRequest) =>
             {
                 DisplayAlert("Success", "Form completed " + Model.DigitalFormDataModel.Instance.Form.Description, "OK");
+                if (responseRequest == DigitalFormHostResponseRequest.CloseKanban)
+                {
+                    string chosenOption = await DisplayActionSheet("Input Required", "All forms for this task are complete. Complete this task as well?", null, "Yes", "No");
+                    switch (chosenOption)
+                    {
+                        case "Yes":
+                            return DigitalFormHostUserResponse.Yes;
+                    }
+                }
+                return DigitalFormHostUserResponse.No;
             };
 
             saveBtn.IsEnabled = !Model.ReadOnly;
@@ -67,7 +77,7 @@ namespace comal.timesheets
         private void ExitBtn_Clicked(object sender, EventArgs e)
         {
             RetainedResults.IsFormRetained = false;
-            OnClosing?.Invoke(this,new DigitalFormsHostClosingArgs(false));
+            OnClosing?.Invoke(this, new DigitalFormsHostClosingArgs(false));
             Navigation.PopAsync();
         }
 
@@ -150,7 +160,7 @@ namespace comal.timesheets
                     chosenOption = await DisplayActionSheet("Select Option", "Cancel", null, "Save Progress", "Complete Form");
                 else
                     chosenOption = await DisplayActionSheet("Select Option", "Cancel", null, "Save Progress", "Complete Form");
-                
+
                 if (!string.IsNullOrEmpty(chosenOption))
                 {
                     if (!chosenOption.Equals("Cancel"))
@@ -208,7 +218,7 @@ namespace comal.timesheets
                                 });
                             }
                         }
-                        OnClosing?.Invoke(this,new DigitalFormsHostClosingArgs(true));
+                        OnClosing?.Invoke(this, new DigitalFormsHostClosingArgs(true));
                         Navigation.PopAsync();
                     }
                 }
@@ -224,7 +234,8 @@ namespace comal.timesheets
             try
             {
                 RetainedResults.IsFormRetained = false;
-                RetainedResults.Results.Clear();
+                if (RetainedResults.Results != null)
+                    RetainedResults.Results.Clear();
             }
             catch
             {

+ 69 - 0
prs.mobile/comal.timesheets/JobFormsGrid.cs

@@ -0,0 +1,69 @@
+using Comal.Classes;
+using InABox.Clients;
+using InABox.Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Material.Forms.UI.Dialogs;
+
+namespace comal.timesheets
+{
+    public class JobFormsGrid : MobileDataGrid
+    {
+        Guid JobID = Guid.Empty;
+        public JobFormsGrid(Guid jobid, DataGridSaveType savetype) 
+        {
+            JobID = jobid;
+            OnItemSelected += JobFormsGrid_OnItemSelected;
+            LoadItems(savetype);
+        }
+
+        private object JobFormsGrid_OnItemSelected(DataGridViewModelItem item)
+        {
+            return null;
+        }
+
+        private void LoadItems(DataGridSaveType savetype)
+        {
+            Task.Run(async () =>
+            {
+                using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Loading"))
+                {
+                    CoreTable table = new Client<JobForm>().Query(
+                        new Filter<JobForm>(x => x.Parent.ID).IsEqualTo(JobID),
+                    new Columns<JobForm>(
+                        x => x.ID,
+                        x => x.FormCompleted,
+                        x => x.Form.Description,
+                        x => x.FormCompletedBy.UserID,
+                        x => x.FormStarted, 
+                        x => x.Form.ID
+                        )
+                    );
+                    if (!table.Rows.Any())
+                        return;
+
+                    List<DataGridViewModelItem> shells = new List<DataGridViewModelItem>();
+                    foreach (CoreRow row in table.Rows)
+                    {
+                        List<Tuple<string, string>> tuples = new List<Tuple<string, string>>
+                        {
+                            new Tuple<string, string>("Name", row.Get<JobForm, string>(x => x.Form.Description)),
+                            new Tuple<string, string>("Started", row.Get<JobForm, DateTime>(x => x.FormStarted).ToString("dd MMM yy")),
+                            new Tuple<string, string>("Completed", row.Get<JobForm, DateTime>(x => x.FormCompleted) == DateTime.MinValue? " " : row.Get<JobForm, DateTime>(x => x.FormCompleted).ToString("dd MMM yy")),
+                            new Tuple<string, string>("User", row.Get<JobForm, string>(x => x.FormCompletedBy.UserID))
+                        };
+                        shells.Add(new DataGridViewModelItem
+                                        (
+                                            id: row.Get<JobForm, Guid>(x => x.ID),
+                                            data: tuples
+                                        ));
+                    }
+                    Setup(shells, typeof(JobForm), savetype);
+                }
+            });
+        }
+    }
+}

+ 138 - 0
prs.mobile/comal.timesheets/KanbanGrid.cs

@@ -0,0 +1,138 @@
+using Comal.Classes;
+using InABox.Clients;
+using InABox.Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xamarin.Forms;
+using XF.Material.Forms.UI.Dialogs;
+
+namespace comal.timesheets
+{
+    public delegate object KanbanGridItemSelected(DataGridViewModelItem item);
+    public class KanbanGrid : MobileDataGrid
+    {
+        public event KanbanGridItemSelected OnKanbanGridItemSelected;
+        Filter<Kanban> Filter;
+        public KanbanGrid(Filter<Kanban> filter, DataGridSaveType savetype = DataGridSaveType.None)
+        {
+            Filter = filter;
+            savetype = Security.IsAllowed<CanChangeOthersTasks>()? DataGridSaveType.Multiple : DataGridSaveType.None;
+            Load(filter, savetype);
+            OnItemSelected += KanbanGrid_OnItemSelected;
+        }
+
+        private object KanbanGrid_OnItemSelected(DataGridViewModelItem item)
+        {
+            return OnKanbanGridItemSelected.Invoke(item);
+        }
+
+        private List<DataGridViewModelItem> LoadItems(Filter<Kanban> filter)
+        {
+            var columns = new Columns<Kanban>(
+                        x => x.ID,
+                        x => x.Number,
+                        x => x.Title,
+                        x => x.Summary,
+                        x => x.Notes,
+                        x => x.EmployeeLink.Name,
+                        x => x.ManagerLink.Name,
+                        x => x.Category,
+                        x => x.DueDate,
+                        x => x.Created,
+                        x => x.StartDate,
+                        x => x.Type.Description
+                       );
+
+            if (new Client<CompanyInformation>().Query(new Filter<CompanyInformation>(x => x.CompanyName).IsEqualTo("Com-Al Windows")
+                .Or(x => x.CompanyName).IsEqualTo("PRS Software")).Rows.Any())
+                columns.Add("IssueNumber");
+
+            CoreTable table = new Client<Kanban>().Query(
+                    filter,
+                    columns
+                    );
+
+            List<DataGridViewModelItem> shells = new List<DataGridViewModelItem>();
+            foreach (CoreRow row in table.Rows)
+            {
+                List<Tuple<string, string>> tuples = new List<Tuple<string, string>>
+                        {
+                            new Tuple<string, string>("Title", "(No. " + row.Get<Kanban, int>(x => x.Number).ToString() + ") " + row.Get<Kanban, string>(x => x.Title)),
+                            new Tuple<string, string>("Summary", row.Get<Kanban, string>(x => x.Summary)),
+                            new Tuple<string, string>("Type", row.Get<Kanban,string>(x => x.Type.Description)),
+                            new Tuple<string, string>("Emp", row.Get<Kanban,string>(x => x.EmployeeLink.Name)),
+                        };
+                shells.Add(new DataGridViewModelItem
+                                (
+                                    id: row.Get<Kanban, Guid>(x => x.ID),
+                                    data: tuples,
+                                    extradetail: GenerateDetail(row)
+                                ));
+            }
+            return shells;
+        }
+
+        private string GenerateDetail(CoreRow row)
+        {
+            string detail = "";
+
+            if (!string.IsNullOrWhiteSpace(row.Get<Kanban, string>(x => x.Title)))
+                detail = "TITLE: " + row.Get<Kanban, string>(x => x.Title) + System.Environment.NewLine + System.Environment.NewLine;
+
+            if (!string.IsNullOrWhiteSpace(row.Get<Kanban, string>(x => x.Summary)))
+                detail = detail + "SUMMARY: " + row.Get<Kanban, string>(x => x.Summary) + System.Environment.NewLine + System.Environment.NewLine;
+
+            if (row.Get<Kanban, string[]>(x => x.Notes).Any())
+            {
+                detail = detail + "NOTES: ";
+                foreach (var note in row.Get<Kanban, string[]>(x => x.Notes))
+                    detail = detail + note + System.Environment.NewLine;
+            }
+
+            if (!string.IsNullOrWhiteSpace(row.Get<Kanban, string>(x => x.ManagerLink.Name)))
+                detail = detail + "MANAGER: " + row.Get<Kanban, string>(x => x.ManagerLink.Name) + System.Environment.NewLine + System.Environment.NewLine;
+
+            if (!string.IsNullOrWhiteSpace(row.Get<Kanban, string>(x => x.Category)))
+                detail = detail + "CATEGORY: " + row.Get<Kanban, string>(x => x.Category) + System.Environment.NewLine + System.Environment.NewLine;
+
+            if (!string.IsNullOrWhiteSpace(row.Get<string>("IssueNumber")))
+                detail = detail + "ISSUE NUMBER: " + row.Get<string>("IssueNumber") + System.Environment.NewLine + System.Environment.NewLine;
+
+            if (row.Get<Kanban, DateTime>(x => x.Created) != DateTime.MinValue)
+                detail = detail + "CREATED: " + row.Get<Kanban, DateTime>(x => x.Created).ToString("dd MMM yy") + System.Environment.NewLine + System.Environment.NewLine;
+
+            if (row.Get<Kanban, DateTime>(x => x.StartDate) != DateTime.MinValue)
+                detail = detail + "STARTED: " + row.Get<Kanban, DateTime>(x => x.StartDate).ToString("dd MMM yy") + System.Environment.NewLine + System.Environment.NewLine;
+
+            if (row.Get<Kanban, DateTime>(x => x.DueDate) != DateTime.MinValue)
+                detail = detail + "DUE: " + row.Get<Kanban, DateTime>(x => x.DueDate).ToString("dd MMM yy") + System.Environment.NewLine + System.Environment.NewLine;
+
+            return detail;
+        }
+
+        private void Load(Filter<Kanban> filter, DataGridSaveType savetype)
+        {
+            Task.Run(async () =>
+            {
+                using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Loading"))
+                {
+                    Setup(LoadItems(Filter), typeof(Kanban), savetype);
+                }
+            });
+        }
+
+        public async void RefreshGrid()
+        {
+            using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Loading"))
+            {
+                var list = LoadItems(Filter);
+                Items.Clear();
+                Items.AddRange(list);
+                Refresh(Items, true);
+            }
+        }
+    }
+}

+ 10 - 0
prs.mobile/comal.timesheets/ViewCellGrid.cs

@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace comal.timesheets
+{
+    class ViewCellGrid
+    {
+    }
+}

+ 3 - 0
prs.mobile/comal.timesheets/comal.timesheets.projitems

@@ -237,6 +237,8 @@
       <DependentUpon>MultiSelectPage.xaml</DependentUpon>
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="$(MSBuildThisFileDirectory)JobFormsGrid.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)KanbanGrid.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Site\JobDocFilterItem.xaml.cs">
       <DependentUpon>JobDocFilterItem.xaml</DependentUpon>
       <SubType>Code</SubType>
@@ -355,6 +357,7 @@
     <Compile Include="$(MSBuildThisFileDirectory)DataGrid\Views\Templates\ViewCell4Columns.xaml.cs">
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="$(MSBuildThisFileDirectory)ViewCellGrid.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Warehousing\Receivals\Receivals.xaml.cs">
       <DependentUpon>Receivals.xaml</DependentUpon>
       <SubType>Code</SubType>