AssignmentList.xaml.cs 15 KB

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