dutyinfo_page.dart 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. // ignore_for_file: unused_import
  2. import 'dart:developer';
  3. import 'package:collection/collection.dart';
  4. import 'package:flutter/foundation.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:flutter_riverpod/flutter_riverpod.dart';
  7. import 'package:gap/gap.dart';
  8. import 'package:go_router/go_router.dart';
  9. import 'package:google_fonts/google_fonts.dart';
  10. import 'package:jiffy/jiffy.dart';
  11. import 'package:linear_progress_bar/linear_progress_bar.dart';
  12. import 'package:multi_select_flutter/multi_select_flutter.dart';
  13. import 'package:tp5/core/core.dart';
  14. import 'package:tp5/csv/csv_data.dart';
  15. import 'package:tp5/csv/data.dart';
  16. import 'package:tp5/flightslist/w_flt.dart';
  17. import 'package:tp5/fltinfo/delaycodes.dart';
  18. import 'package:tp5/fltinfo/widget/w_cadre.dart';
  19. import 'package:tp5/ftl/widget/w_shadowbox.dart';
  20. import 'package:tp5/lido/lido_api.dart';
  21. import 'package:tp5/lido/model/Lidoapi_list.dart';
  22. import 'package:tp5/lido/model/Lidoapi_ofp.dart';
  23. import 'package:tp5/lido/w_lidoflt.dart';
  24. import 'package:tp5/pdf/pdf_page.dart';
  25. import 'package:tp5/roster/api/crewlink_api.dart';
  26. import 'package:tp5/roster/models/duty.dart';
  27. import 'package:tp5/roster/widgets/w_airport.dart';
  28. import 'package:tp5/roster/widgets/w_citypair.dart';
  29. import 'package:tp5/roster/widgets/w_crew.dart';
  30. import 'package:tp5/roster/widgets/w_day.dart';
  31. import 'package:tp5/roster/widgets/w_fnum.dart';
  32. import 'package:tp5/roster/widgets/w_hour.dart';
  33. import 'package:tp5/widgets/my_row.dart';
  34. class DutyinfoPage extends ConsumerStatefulWidget {
  35. const DutyinfoPage({required this.params, super.key});
  36. final DutyinfoParams params;
  37. @override
  38. ConsumerState<ConsumerStatefulWidget> createState() => _DutyinfoPageState();
  39. }
  40. class _DutyinfoPageState extends ConsumerState<DutyinfoPage> {
  41. @override
  42. void initState() {
  43. Future.delayed(Duration.zero, () {
  44. if (widget.params.tlc != null) {
  45. final qualif = ref.read(dataProvider).qualif;
  46. collegeSelected = qualif
  47. .where((e) => e.tlc == widget.params.tlc)
  48. .map((e) => e.college ?? "")
  49. .toSet()
  50. .toList();
  51. acSelected = qualif
  52. .where((e) => e.tlc == widget.params.tlc)
  53. .map((e) => e.ac ?? "")
  54. .toSet()
  55. .toList();
  56. baseSelected = qualif
  57. .where((e) => e.tlc == widget.params.tlc)
  58. .map((e) => e.base ?? "")
  59. .toSet()
  60. .toList();
  61. }
  62. crewPnlegLoad();
  63. });
  64. super.initState();
  65. }
  66. // List crew = [];
  67. Jiffy _now = Jiffy.now().toUtc();
  68. @override
  69. Widget build(BuildContext context) {
  70. //final data = ref.watch(dataProvider);
  71. //final pnleg = data.pnleg;
  72. // final _dateNotifiedcrew = data.pnlegupdate;
  73. _now = ref.watch(clockProvider);
  74. //final qualif = ref.watch(dataProvider).qualif;
  75. return Scaffold(
  76. appBar: AppBar(
  77. title: MyRow(
  78. // mainAxisAlignment: MainAxisAlignment.spaceBetween,
  79. children: [
  80. Text(
  81. '${widget.params.dutytype ?? ""} ${widget.params.label ?? ""} ${widget.params.dep}${widget.params.des != null ? "-${widget.params.des}" : ""}'),
  82. const Gap(10),
  83. Text(widget.params.jdep!.format(pattern: "EEE ddMMM'yy"),
  84. style: const TextStyle(fontSize: 12)),
  85. ],
  86. ),
  87. leading: IconButton(
  88. icon: const Icon(Icons.arrow_back),
  89. onPressed: () {
  90. context.pop(context);
  91. },
  92. ),
  93. actions: [
  94. Text(
  95. '${widget.params.dutytype ?? "Duty"} info',
  96. style: TextStyle(
  97. fontSize: 12,
  98. fontWeight: FontWeight.w700,
  99. color: Colors.yellow),
  100. ),
  101. Gap(10)
  102. ],
  103. ),
  104. body: ListView(
  105. padding: const EdgeInsets.all(5),
  106. children: [
  107. WCadre(
  108. child: Column(
  109. children: [
  110. WDay(date: widget.params.jdep!),
  111. if (widget.params.label != null)
  112. Text(
  113. widget.params.label!,
  114. style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
  115. )
  116. else if (widget.params.dutytype != null)
  117. Text(
  118. widget.params.dutytype!,
  119. style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
  120. ),
  121. Row(
  122. mainAxisAlignment: MainAxisAlignment.center,
  123. mainAxisSize: MainAxisSize.max,
  124. children: [
  125. if (widget.params.des != null)
  126. WCitypair(
  127. iata1: "${widget.params.dep}",
  128. iata2: widget.params.des ?? "---")
  129. else
  130. WAirport(iata: widget.params.dep ?? "---"),
  131. ],
  132. ),
  133. Row(
  134. mainAxisAlignment: MainAxisAlignment.center,
  135. mainAxisSize: MainAxisSize.max,
  136. children: [
  137. WHour(jiffy: widget.params.jdep!),
  138. if (widget.params.jdes != null) ...[
  139. const Gap(20),
  140. WHour(jiffy: widget.params.jdes!)
  141. ],
  142. ]),
  143. const Gap(10)
  144. ],
  145. ),
  146. ),
  147. const Gap(20),
  148. if (widget.params.dutytype == "standby")
  149. _stdbyCrew()
  150. else
  151. _notifiedCrew(),
  152. ],
  153. ),
  154. );
  155. }
  156. Jiffy? _dateNotifiedcrew;
  157. static List<Pnleg> filterPnleg(Map data) {
  158. final List<Pnleg> pnleg = data["pnleg"];
  159. final DutyinfoParams filter = data["filter"];
  160. return pnleg
  161. .where((leg) => ((filter.jdep == null ||
  162. (filter.sameday
  163. ? (leg.jdep?.startOf(Unit.day) ==
  164. filter.jdep?.startOf(Unit.day))
  165. : ((leg.jdep == filter.jdep) &&
  166. (filter.jdes == null || leg.jarr == filter.jdes)))) &&
  167. (filter.dep == null || leg.dep == filter.dep) &&
  168. (filter.des == null || leg.arr == filter.des) &&
  169. (filter.label == null || leg.label == filter.label) &&
  170. (filter.dutytype == null || leg.dutytype == filter.dutytype)))
  171. .toList();
  172. }
  173. List<Pnleg> crewPnleg = [];
  174. bool crewPnlegLoading = false;
  175. crewPnlegLoad() async {
  176. crewPnlegLoading = true;
  177. setState(() {});
  178. final data = ref.watch(dataProvider);
  179. final pnleg = data.pnleg;
  180. _dateNotifiedcrew = data.pnlegupdate;
  181. crewPnleg =
  182. await compute(filterPnleg, {"pnleg": pnleg, "filter": widget.params});
  183. crewPnlegLoading = false;
  184. setState(() {});
  185. }
  186. _notifiedCrew() {
  187. // final qualif = ref.watch(qualifProvider(CrewFilter()));
  188. final qualif = ref.watch(dataProvider).qualif;
  189. return WCadre(
  190. titleright: crewPnlegLoading
  191. ? Padding(
  192. padding: const EdgeInsets.all(5.0),
  193. child: CircularProgressIndicator(),
  194. )
  195. : ElevatedButton.icon(
  196. label: const Text("Update", style: TextStyle(fontSize: 9)),
  197. onPressed: () async {
  198. await crewPnlegLoad();
  199. },
  200. icon: const Icon(Icons.update)),
  201. title: "Notified Crew (alt)",
  202. bottomright: _dateNotifiedcrew != null
  203. ? Text("data retrieved ${_dateNotifiedcrew?.from(_now)}",
  204. style: GoogleFonts.robotoMono(
  205. fontSize: 10, fontWeight: FontWeight.w300))
  206. : null,
  207. child: crewPnleg.isEmpty
  208. ? const Text("No data found !")
  209. : Column(
  210. children: [
  211. Padding(
  212. padding: const EdgeInsets.all(8.0),
  213. child: Column(
  214. children: crewPnleg
  215. .mapIndexed((int i, leg) => {
  216. "index": i,
  217. "college": (["F", ""].contains(leg.type))
  218. ? "DH"
  219. : (qualif
  220. .firstWhereOrNull((e) =>
  221. (e.tlc == leg.tlc) &&
  222. (e.ac == leg.actype))
  223. ?.college ??
  224. qualif
  225. .firstWhereOrNull(
  226. (e) => e.tlc == leg.tlc)
  227. ?.college ??
  228. "--")
  229. })
  230. .sortedByCompare(
  231. (e) => e["college"].toString(), collegeSort)
  232. .map<Widget>((e) {
  233. final x = crewPnleg.elementAt(e["index"] as int);
  234. // final pn = qualif.firstWhereOrNull((e) => e.tlc == x.tlc);
  235. final pn = (qualif.firstWhereOrNull(
  236. (e) => (e.tlc == x.tlc) && (e.ac == x.actype)) ??
  237. qualif.firstWhereOrNull((e) => e.tlc == x.tlc));
  238. return WCrew(
  239. college: pn?.college,
  240. mle: x.tlc,
  241. base: pn?.base,
  242. name: "${pn?.lname}, ${pn?.fname}",
  243. );
  244. }).toList()),
  245. )
  246. ],
  247. ),
  248. );
  249. }
  250. List<String> collegeSelected = [];
  251. List<String> acSelected = [];
  252. List<String> baseSelected = [];
  253. _stdbyCrew() {
  254. // final qualif = ref.watch(qualifProvider(CrewFilter()));
  255. final qualif = ref.watch(dataProvider).qualif;
  256. final tlcs = crewPnleg.map((e) => e.tlc).toSet();
  257. final pairs = qualif
  258. .where((e) => tlcs.contains(e.tlc))
  259. .map((e) => "${e.college},${e.ac},${e.base}")
  260. .toSet()
  261. .map((e) => e.split(","))
  262. .toList();
  263. final List<String> pnfilterd = qualif
  264. .where((q) =>
  265. ((collegeSelected.isEmpty || collegeSelected.contains(q.college)) &&
  266. (acSelected.isEmpty || acSelected.contains(q.ac)) &&
  267. (baseSelected.isEmpty || baseSelected.contains(q.base))))
  268. .map((e) => e.tlc ?? "")
  269. .toSet()
  270. .toList();
  271. final List<Pnleg> crewPnlegFiltered =
  272. crewPnleg.where((e) => pnfilterd.contains(e.tlc)).toList();
  273. return WCadre(
  274. titleright: crewPnlegLoading
  275. ? Padding(
  276. padding: const EdgeInsets.all(5.0),
  277. child: CircularProgressIndicator(),
  278. )
  279. : ElevatedButton.icon(
  280. label: const Text("Update", style: TextStyle(fontSize: 9)),
  281. onPressed: () async {
  282. await crewPnlegLoad();
  283. },
  284. icon: const Icon(Icons.update)),
  285. title: "STDBY Crew",
  286. bottomright: _dateNotifiedcrew != null
  287. ? Text("data retrieved ${_dateNotifiedcrew?.from(_now)}",
  288. style: GoogleFonts.robotoMono(
  289. fontSize: 10, fontWeight: FontWeight.w300))
  290. : null,
  291. child: crewPnleg.isEmpty
  292. ? const Text("No data found !")
  293. : Column(
  294. children: [
  295. MyRow(
  296. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  297. children: [
  298. Gap(5),
  299. Column(
  300. children: [
  301. const Text('College:', style: TextStyle(fontSize: 11)),
  302. Text("(${(collegeSelected.length)})",
  303. style: TextStyle(fontSize: 11))
  304. ],
  305. ),
  306. Gap(5),
  307. MultiSelectChipDisplay(
  308. colorator: (value) => (collegeSelected.contains(value))
  309. ? Colors.green[800]
  310. : Colors.grey[900],
  311. //chipColor: Colors.blue[900],
  312. icon: null,
  313. textStyle: TextStyle(fontSize: 9, color: Colors.white),
  314. scrollBar: HorizontalScrollBar(),
  315. scroll: true,
  316. items: pairs
  317. .map((e) => e[0])
  318. .sortedByCompare((e) => e, collegeSort)
  319. .toSet()
  320. .map((e) => MultiSelectItem(e, "$e"))
  321. .toList(),
  322. onTap: (value) {
  323. setState(() {
  324. if (collegeSelected.contains(value)) {
  325. collegeSelected.remove(value);
  326. } else {
  327. collegeSelected.add(value);
  328. }
  329. });
  330. },
  331. ),
  332. ],
  333. ),
  334. MyRow(
  335. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  336. children: [
  337. Gap(5),
  338. Column(
  339. children: [
  340. const Text('ACs:', style: TextStyle(fontSize: 11)),
  341. Text("(${(acSelected.length)})",
  342. style: TextStyle(fontSize: 11))
  343. ],
  344. ),
  345. Gap(5),
  346. MultiSelectChipDisplay(
  347. colorator: (value) => (acSelected.contains(value))
  348. ? Colors.green[800]
  349. : Colors.grey[900],
  350. //chipColor: Colors.blue[900],
  351. icon: null,
  352. textStyle: TextStyle(fontSize: 9, color: Colors.white),
  353. scrollBar: HorizontalScrollBar(),
  354. scroll: true,
  355. items: pairs
  356. .map((e) => e[1])
  357. .sortedByCompare((e) => e, acSort)
  358. .toSet()
  359. .map((e) => MultiSelectItem(e, "$e"))
  360. .toList(),
  361. onTap: (value) {
  362. setState(() {
  363. if (acSelected.contains(value)) {
  364. acSelected.remove(value);
  365. } else {
  366. acSelected.add(value);
  367. }
  368. });
  369. },
  370. ),
  371. ],
  372. ),
  373. MyRow(
  374. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  375. children: [
  376. const Gap(5),
  377. Column(
  378. children: [
  379. const Text('Base:', style: TextStyle(fontSize: 11)),
  380. Text("(${(baseSelected.length)})",
  381. style: TextStyle(fontSize: 11))
  382. ],
  383. ),
  384. const Gap(5),
  385. MultiSelectChipDisplay(
  386. colorator: (value) => (baseSelected.contains(value))
  387. ? Colors.green[800]
  388. : Colors.grey[900],
  389. //chipColor: Colors.blue[900],
  390. icon: null,
  391. textStyle: TextStyle(fontSize: 9, color: Colors.white),
  392. scrollBar: HorizontalScrollBar(),
  393. scroll: true,
  394. items: pairs
  395. .map((e) => e[2])
  396. .sortedByCompare((e) => e, baseSort)
  397. .toSet()
  398. .map((e) => MultiSelectItem(e, "$e"))
  399. .toList(),
  400. onTap: (value) {
  401. setState(() {
  402. if (baseSelected.contains(value)) {
  403. baseSelected.remove(value);
  404. } else {
  405. baseSelected.add(value);
  406. }
  407. });
  408. },
  409. ),
  410. ],
  411. ),
  412. Padding(
  413. padding: const EdgeInsets.all(8.0),
  414. child: Column(
  415. children: crewPnlegFiltered
  416. .mapIndexed((int i, leg) {
  417. // print(leg);
  418. return {
  419. "index": i,
  420. "college": (qualif
  421. .where((e) => e.tlc == leg.tlc)
  422. .map((e) => e.college)
  423. .sortedByCompare(
  424. (e) => e ?? "", collegeSort))
  425. .firstOrNull ??
  426. "--",
  427. // "jdep": (leg.jdep?.millisecondsSinceEpoch ?? 0)
  428. "jdep": (leg.label ?? "0")
  429. };
  430. })
  431. .sortedByCompare(
  432. (e) => [
  433. e["jdep"].toString(),
  434. e["college"].toString()
  435. ],
  436. jdepcollegeSort)
  437. .map<Widget>((e) {
  438. final x =
  439. crewPnlegFiltered.elementAt(e["index"] as int);
  440. // final pn =
  441. // qualif.firstWhereOrNull((e) => e.tlc == x.tlc);
  442. final pn = (qualif.firstWhereOrNull((e) =>
  443. (e.tlc == x.tlc) && (e.ac == x.actype)) ??
  444. qualif.firstWhereOrNull((e) => e.tlc == x.tlc));
  445. return Row(
  446. children: [
  447. Text("${e["jdep"]}"),
  448. WCrew(
  449. college: pn?.college,
  450. mle: x.tlc,
  451. base: pn?.base,
  452. name: "${pn?.lname}, ${pn?.fname}",
  453. ),
  454. ],
  455. );
  456. })
  457. .toList()),
  458. )
  459. ],
  460. ),
  461. );
  462. }
  463. }
  464. class DutyinfoParams {
  465. const DutyinfoParams({
  466. this.tlc,
  467. this.sameday = false,
  468. this.date,
  469. this.start,
  470. this.end,
  471. this.label,
  472. this.dutytype,
  473. this.jdep,
  474. this.jdes,
  475. this.dep,
  476. this.des,
  477. });
  478. final String? tlc;
  479. final bool sameday;
  480. final Jiffy? start;
  481. final Jiffy? end;
  482. final String? date;
  483. final String? label;
  484. final String? dutytype;
  485. final Jiffy? jdep;
  486. final Jiffy? jdes;
  487. final String? dep;
  488. final String? des;
  489. }
  490. int collegeSort(String a, String b) {
  491. // Define the order based on your requirements
  492. final order = ['CP', 'FO', 'PU', 'SE', 'ST', 'JU'];
  493. // Get the index of each element in the order list
  494. final indexA = !order.contains(a) ? 99 : order.indexOf(a);
  495. final indexB = !order.contains(b) ? 99 : order.indexOf(b);
  496. // Compare based on the index
  497. return indexA.compareTo(indexB);
  498. }
  499. int acSort(String a, String b) {
  500. // Define the order based on your requirements
  501. final order = ['320', '330', '32N', '319', '32T'];
  502. // Get the index of each element in the order list
  503. final indexA = !order.contains(a) ? 99 : order.indexOf(a);
  504. final indexB = !order.contains(b) ? 99 : order.indexOf(b);
  505. // Compare based on the index
  506. return indexA.compareTo(indexB);
  507. }
  508. int baseSort(String a, String b) {
  509. // Define the order based on your requirements
  510. final order = ['TUN', 'MIR', 'DJE'];
  511. // Get the index of each element in the order list
  512. final indexA = !order.contains(a) ? 99 : order.indexOf(a);
  513. final indexB = !order.contains(b) ? 99 : order.indexOf(b);
  514. // Compare based on the index
  515. return indexA.compareTo(indexB);
  516. }
  517. int jdepcollegeSort(List<String> a, List<String> b) {
  518. final String x = a[0];
  519. final String y = b[0];
  520. final res = x.compareTo(y);
  521. if (res == 0) {
  522. return collegeSort(a[1], b[1]);
  523. } else {
  524. return res;
  525. }
  526. }