AssignmentList.xaml.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. using System;
  2. using System.Collections.ObjectModel;
  3. using InABox.Clients;
  4. using InABox.Core;
  5. using Comal.Classes;
  6. using Xamarin.Forms;
  7. using System.Linq;
  8. using System.Threading.Tasks;
  9. using InABox.Configuration;
  10. using InABox.Mobile;
  11. using Syncfusion.SfSchedule.XForms;
  12. using XF.Material.Forms;
  13. using XF.Material.Forms.UI.Dialogs;
  14. using XF.Material.Forms.UI.Dialogs.Configurations;
  15. namespace PRS.Mobile
  16. {
  17. public enum AssignmentViewType
  18. {
  19. Day,
  20. TimeLine
  21. }
  22. public enum AssignmentLookupType
  23. {
  24. ActiveJobs,
  25. UnbookedJobs,
  26. Tasks
  27. }
  28. public class SelectedColorConverter : UtilityConverter<ILookupShell, Color>
  29. {
  30. public Color SelectedColor { get; set; }
  31. public Color UnselectedColor { get; set; }
  32. protected override Color Convert(ILookupShell value)
  33. {
  34. return (value.Parent as ICoreRepository)?.IsSelected(value) == true
  35. ? SelectedColor
  36. : UnselectedColor;
  37. }
  38. }
  39. public partial class AssignmentList
  40. {
  41. private AssignmentModel DataModel { get; set; }
  42. private Guid[] _employeeids;
  43. private readonly AssignmentModuleSettings _settings;
  44. private AssignmentViewType _view;
  45. private AssignmentLookupType _lookuptype;
  46. private String _teamname;
  47. private DateTime _currentdate;
  48. public AssignmentList()
  49. {
  50. InitializeComponent();
  51. DataModel = new AssignmentModel(App.Data, GetFilter);
  52. BindingContext = DataModel;
  53. _settings = new LocalConfiguration<AssignmentModuleSettings>().Load();
  54. _currentdate = _settings.Date.IsEmpty() ? DateTime.Today : _settings.Date;
  55. _view = _settings.View;
  56. _employeeids = _settings.Employees ?? new[] { App.Data.Me.ID };
  57. _lookuptype = _settings.LookupType;
  58. LookupType.Text = CoreUtils.Neatify(_lookuptype.ToString());
  59. _teamname = String.IsNullOrWhiteSpace(_settings.TeamName) ? App.Data.Me.Name : _settings.TeamName;
  60. ShowHideSources.IsVisible = Security.IsAllowed<CanViewMobileAssignmentsSidebar>();
  61. }
  62. protected override void OnAppearing()
  63. {
  64. base.OnAppearing();
  65. RefreshLookups();
  66. Reload();
  67. }
  68. private void RefreshLookups()
  69. {
  70. if (_lookuptype == AssignmentLookupType.Tasks)
  71. {
  72. App.Data.Kanbans.Refresh(false, () =>
  73. {
  74. Device.BeginInvokeOnMainThread(() =>
  75. {
  76. Lookups.ItemsSource = new ObservableCollection<KanbanShell>(App.Data.Kanbans);
  77. });
  78. });
  79. }
  80. else if (_lookuptype == AssignmentLookupType.ActiveJobs)
  81. {
  82. App.Data.Jobs.Refresh(false, () =>
  83. {
  84. Device.BeginInvokeOnMainThread(() =>
  85. {
  86. Lookups.ItemsSource = new ObservableCollection<JobShell>(App.Data.Jobs);
  87. });
  88. });
  89. }
  90. else if (_lookuptype == AssignmentLookupType.UnbookedJobs)
  91. {
  92. App.Data.Jobs.Refresh(false, () =>
  93. {
  94. Device.BeginInvokeOnMainThread(() =>
  95. {
  96. Lookups.ItemsSource = new ObservableCollection<JobShell>(App.Data.Jobs.Where(x=>x.OpenAssignments == 0));
  97. });
  98. });
  99. }
  100. }
  101. private void Reload()
  102. {
  103. DayView.DataSource = null;
  104. TimeLineView.DataSource = null;
  105. ScheduleType.Text = _teamname;
  106. if (_view == AssignmentViewType.Day)
  107. {
  108. TimelineFrame.IsVisible = false;
  109. DayFrame.IsVisible = true;
  110. DayView.DayViewSettings.DayLabelSettings.TimeFormat = "HH:mm";
  111. }
  112. else
  113. {
  114. DayFrame.IsVisible = false;
  115. TimelineFrame.IsVisible = true;
  116. TimeLineView.ResourceViewSettings.VisibleResourceCount = Math.Max(1, Math.Min(16, _employeeids.Length));
  117. TimeLineView.TimelineViewSettings.AppointmentHeight =
  118. (this.Height / TimeLineView.ResourceViewSettings.VisibleResourceCount) + 100;
  119. var resources = new ObservableCollection<object>();
  120. foreach (var empid in _employeeids)
  121. {
  122. var empname = App.Data.Employees.Items.FirstOrDefault(x => x.ID == empid)?.Name ?? empid.ToString();
  123. if (_employeeids.Length > 10)
  124. empname = String.Join("", empname.Split(' ').Select(x => x.Substring(0, 1)));
  125. else if (_employeeids.Length > 6)
  126. {
  127. var comps = empname.Split(' ').ToArray();
  128. empname = $"{comps.First()} {String.Join("", comps.Skip(1).Select(x => x.Substring(0, 1)))}";
  129. }
  130. resources.Add(
  131. new ScheduleResource()
  132. {
  133. Name = empname,
  134. Id = empid,
  135. Color = Color.Transparent,
  136. Image = ""
  137. }
  138. );
  139. }
  140. TimeLineView.ScheduleResources = resources;
  141. TimeLineView.ShowResourceView = true;
  142. }
  143. RefreshData();
  144. }
  145. private Filter<Assignment> GetFilter() =>
  146. new Filter<Assignment>(x => x.Date).IsEqualTo(_currentdate)
  147. .And(x => x.EmployeeLink.ID).InList(_employeeids);
  148. private void RefreshData()
  149. {
  150. DataModel.Refresh(true, () => Device.BeginInvokeOnMainThread(UpdateScreen));
  151. }
  152. private void UpdateScreen()
  153. {
  154. Title = $"{_currentdate:dd MMMM yyyy}";
  155. if (_view == AssignmentViewType.Day)
  156. {
  157. DayView.DataSource = new ObservableCollection<AssignmentShell>(DataModel.Items);
  158. DayView.MoveToDate = ShowRelevantTime();
  159. }
  160. else
  161. {
  162. TimeLineView.DataSource = new ObservableCollection<AssignmentShell>(DataModel.Items);
  163. TimeLineView.MoveToDate = ShowRelevantTime();
  164. }
  165. }
  166. private DateTime ShowRelevantTime()
  167. {
  168. return (DataModel.Items.Count > 0 ? DataModel.Items.First().StartTime : DateTime.Now).AddMinutes(-30);
  169. }
  170. private void Lookups_OnItemTapped(object sender, ItemTappedEventArgs e)
  171. {
  172. ICoreRepository model = _lookuptype == AssignmentLookupType.Tasks
  173. ? App.Data.Kanbans
  174. : App.Data.Jobs as ICoreRepository;
  175. model.SelectNone();
  176. model.SelectItem(e.Item);
  177. }
  178. private void LookupsType_Tapped(object sender, EventArgs e)
  179. {
  180. _lookuptype = _lookuptype == AssignmentLookupType.Tasks
  181. ? AssignmentLookupType.ActiveJobs
  182. : _lookuptype == AssignmentLookupType.ActiveJobs
  183. ? AssignmentLookupType.UnbookedJobs
  184. : AssignmentLookupType.Tasks;
  185. _settings.LookupType = _lookuptype;
  186. LookupType.Text = CoreUtils.Neatify(_lookuptype.ToString());
  187. new LocalConfiguration<AssignmentModuleSettings>().Save(_settings);
  188. RefreshLookups();
  189. }
  190. private void Schedule_OnCellTapped(object sender, CellTappedEventArgs e)
  191. {
  192. if (e.Appointment is AssignmentShell item)
  193. {
  194. if (item.DeliveryID != Guid.Empty)
  195. {
  196. ProgressVisible = true;
  197. DeliveryEdit editor = null;
  198. Task.Run(() => { editor = DeliveryEdit.Load(item.DeliveryID); }).Wait();
  199. ProgressVisible = false;
  200. if (editor != null)
  201. Navigation.PushAsync(editor);
  202. else
  203. DisplayAlert("Error", "Cannot Load Delivery!", "OK");
  204. }
  205. else
  206. {
  207. var editor = new AssignmentEdit(){ Item = item };
  208. Navigation.PushAsync(editor);
  209. }
  210. }
  211. }
  212. private async void Schedule_OnCellLongPressed(object sender, CellTappedEventArgs e)
  213. {
  214. if (e.Appointment == null)
  215. {
  216. if (Security.CanEdit<Assignment>())
  217. {
  218. CreateAssignment(
  219. e.Datetime,
  220. e.Resource as ScheduleResource
  221. );
  222. }
  223. }
  224. else if (Security.CanDelete<Assignment>()
  225. && e.Appointment is AssignmentShell assignment)
  226. {
  227. await DeleteAssignment(assignment.ID);
  228. }
  229. }
  230. private void CreateAssignment(DateTime date, ScheduleResource resource)
  231. {
  232. var shell = DataModel.AddItem();
  233. shell.Date = date.Date;
  234. shell.Title = "New Assignment";
  235. shell.BookedStart = new TimeSpan(date.TimeOfDay.Hours, 0, 0);
  236. shell.BookedFinish = date.TimeOfDay.Add(new TimeSpan(1, 0, 0));
  237. shell.BookedDuration = new TimeSpan(1, 0, 0);
  238. shell.EmployeeID = (resource is { } sr)
  239. ? (Guid)sr.Id
  240. : App.Data.Me.ID;
  241. var job = (_lookuptype == AssignmentLookupType.ActiveJobs) || (_lookuptype == AssignmentLookupType.UnbookedJobs)
  242. ? App.Data.Jobs.Items.FirstOrDefault(x => x.LookupSelected)
  243. : null;
  244. if (job != null)
  245. {
  246. shell.JobID = job.ID;
  247. shell.JobNumber = job.JobNumber;
  248. shell.JobName = job.Name;
  249. shell.Description = String.Join("\n",job.Notes);
  250. shell.Title = job.DisplayName;
  251. }
  252. var task = _lookuptype == AssignmentLookupType.Tasks
  253. ? App.Data.Kanbans.SelectedItems.FirstOrDefault()
  254. : null;
  255. if (task != null)
  256. {
  257. shell.TaskID = task.ID;
  258. shell.TaskNumber = task.Number;
  259. shell.TaskName = task.Description;
  260. shell.Title = task.Title;
  261. shell.Description = task.Description;
  262. App.Data.Kanbans.SelectNone();
  263. }
  264. var editor = new AssignmentEdit() { Item = shell };
  265. Navigation.PushAsync(editor);
  266. }
  267. private async Task DeleteAssignment(Guid id)
  268. {
  269. var confirm = await MaterialDialog.Instance.ConfirmAsync(
  270. "Are you sure you wish to delete this assignment?",
  271. "Confirm Deletion",
  272. "Yes, Delete",
  273. "Cancel",
  274. new MaterialAlertDialogConfiguration()
  275. {
  276. ButtonFontFamily = Material.FontFamily.Body2
  277. }
  278. );
  279. if (confirm == true)
  280. {
  281. using (await MaterialDialog.Instance.LoadingDialogAsync(message: "Deleting Assignment"))
  282. {
  283. var assignment = new Assignment() { ID = id };
  284. new Client<Assignment>().Delete(assignment, "Deleted on Mobile Device");
  285. }
  286. RefreshData();
  287. }
  288. }
  289. private void SelectDate_OnClicked(object sender, EventArgs e)
  290. {
  291. var selector = new MobileDateSelector();
  292. selector.Changed += (o, args) =>
  293. {
  294. if (_employeeids.Any())
  295. {
  296. _currentdate = args.Date;
  297. _settings.Date = args.Date;
  298. new LocalConfiguration<AssignmentModuleSettings>().Save(_settings);
  299. }
  300. DayView.MoveToDate = args.Date;
  301. TimeLineView.MoveToDate = args.Date;
  302. RefreshData();
  303. };
  304. ShowPopup(() => selector);
  305. }
  306. private void SelectEmployees_OnClicked(object sender, EventArgs e)
  307. {
  308. ShowPopup(() => CreateTeamSelection("Select Activity", (team) =>
  309. {
  310. if (team == null)
  311. {
  312. _view = AssignmentViewType.Day;
  313. _employeeids = new[] { App.Data.Me.ID };
  314. _teamname = App.Data.Me.Name;
  315. }
  316. else
  317. {
  318. _view = AssignmentViewType.TimeLine;
  319. _employeeids = App.Data.EmployeeTeams.Where(x => x.TeamID == team.ID)
  320. .Select(x => x.ID).Distinct().ToArray();
  321. _teamname = team.Name;
  322. }
  323. _settings.Employees = _employeeids;
  324. _settings.View = _view;
  325. _settings.TeamName = _teamname;
  326. new LocalConfiguration<AssignmentModuleSettings>().Save(_settings);
  327. Dispatcher.BeginInvokeOnMainThread(Reload);
  328. }));
  329. }
  330. private View CreateTeamSelection(String caption, Action<EmployeeTeamSummary> selected)
  331. {
  332. SelectionView selection = new SelectionView
  333. {
  334. VerticalOptions = LayoutOptions.Fill,
  335. HorizontalOptions = LayoutOptions.Fill
  336. };
  337. selection.Configure += (sender, args) =>
  338. {
  339. args.Columns
  340. .BeginUpdate()
  341. .Clear()
  342. .Add(new MobileGridTextColumn<EmployeeTeamSummary>() { Column = x=>x.Name, Width = GridLength.Star, Caption = caption, Alignment = TextAlignment.Start})
  343. .EndUpdate();
  344. };
  345. selection.Refresh += (sender, args) =>
  346. {
  347. App.Data.EmployeeTeams.Refresh(false);
  348. return App.Data.EmployeeTeams.AvailableTeams;
  349. };
  350. selection.SelectionChanged += (sender, args) =>
  351. {
  352. selected(args.SelectedItems.FirstOrDefault() as EmployeeTeamSummary);
  353. DismissPopup();
  354. };
  355. selection.AddButton("Only Me", () =>
  356. {
  357. selected(null);
  358. DismissPopup();
  359. });
  360. selection.Load();
  361. return selection;
  362. }
  363. private bool _sidebarvisible;
  364. private void ShowHideSources_OnClicked(object sender, EventArgs e)
  365. {
  366. _sidebarvisible = !_sidebarvisible;
  367. ShowHideSources.Source = _sidebarvisible
  368. ? ImageSource.FromFile("arrow_white_right")
  369. : ImageSource.FromFile("arrow_white_down");
  370. JobColumn.Width = _sidebarvisible
  371. ? new GridLength(150, GridUnitType.Absolute)
  372. : new GridLength(0, GridUnitType.Absolute);
  373. }
  374. }
  375. }