import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:jiffy/jiffy.dart'; import 'package:super_sliver_list/super_sliver_list.dart'; import 'package:tp5/core/basic_page.dart'; import 'package:tp5/core/utils.dart'; import 'package:tp5/csv/data.dart'; import 'package:linked_scroll_controller/linked_scroll_controller.dart'; import 'package:tp5/fltinfo/view/dutyinfo_page.dart'; import 'package:tp5/fltinfo/view/fltinfo_page.dart'; import 'package:tp5/rosters/rosters_crew_filter.dart'; import 'package:tp5/widgets/my_col.dart'; import 'package:tp5/widgets/my_row.dart'; import 'package:google_fonts/google_fonts.dart'; class RostersPage extends ConsumerStatefulWidget { const RostersPage({super.key}); @override ConsumerState createState() => _RostersPageState(); } class _RostersPageState extends ConsumerState { final scrollControllerGroup = LinkedScrollControllerGroup(); Map> scrollCtrls = {}; final _listControllerV = ListController(); final _listControllerH = ListController(); ScrollController? _getCtrl(int i, {int cat = 0}) { if (!scrollCtrls.containsKey(cat)) { // print("rosterspage: getctrl: create cat: $cat"); scrollCtrls.addAll({cat: {}}); } if (!scrollCtrls[cat]!.containsKey(i)) { scrollCtrls[cat]![i] = scrollControllerGroup.addAndGet(); // print("rosterspage: getctrl: create index: $i"); } // scrollControllerGroup.jumpTo(scrollControllerGroup.offset); return scrollCtrls[cat]![i]!; } final bottomnavstyle = ElevatedButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5.0), ), backgroundColor: const Color.fromARGB(255, 0, 36, 53) //elevated btton background color ); TextEditingController ctrldep = TextEditingController(); TextEditingController ctrldes = TextEditingController(); TextEditingController ctrlairline = TextEditingController(); TextEditingController ctrlfnum = TextEditingController(); Map flightFilter = { "dep": "", "des": "", "al": "", "fnum": "" }; void _searchFlights(xcontext) async { ctrldep.text = flightFilter["dep"] ?? ""; ctrldes.text = flightFilter["des"] ?? ""; ctrlairline.text = flightFilter["al"] ?? ""; ctrlfnum.text = flightFilter["fnum"] ?? ""; final res = await showModalBottomSheet( context: context, builder: (context) => _flightFilter(xcontext)); if (res is Map) { Hive.box("settings").put("rosters.filter.flights", (res)); setState(() { flightFilter = res; }); } } Widget _flightFilter(BuildContext context) { return Container( padding: const EdgeInsets.all(20), child: Column(mainAxisSize: MainAxisSize.min, children: [ Row( children: [ Expanded( child: TextField( decoration: const InputDecoration(labelText: "Departure Airport"), maxLength: 3, // Set maximum length controller: ctrldep, ), ), const SizedBox(width: 10), Expanded( child: TextField( decoration: const InputDecoration(labelText: "Arrival Airport"), maxLength: 3, // Set maximum length controller: ctrldes, ), ), ], ), const SizedBox(height: 15), Row( children: [ Expanded( child: TextField( controller: ctrlairline, decoration: const InputDecoration(labelText: "Airline IATA code"), maxLength: 2, // Set maximum length ), ), const SizedBox(width: 10), Expanded( child: TextField( controller: ctrlfnum, decoration: const InputDecoration(labelText: "Flight number"), maxLength: 6, // Set maximum length ), ), ], ), const SizedBox(height: 15), const SizedBox(height: 35), SizedBox( width: 360, child: ElevatedButton( onPressed: () async => Navigator.pop(context, { "dep": ctrldep.text.toUpperCase(), "des": ctrldes.text.toUpperCase(), "al": ctrlairline.text.toUpperCase(), "fnum": ctrlfnum.text.toUpperCase() }), // Close the bottom sheet style: ElevatedButton.styleFrom( backgroundColor: Colors.green, // Set button color to green shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), // Add rounded corners ), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), ), child: const Text( 'Flight filter', style: TextStyle(fontSize: 18, color: Colors.white), ), ), ), ])); } Map> crewFilter = {"college": [], "ac": [], "base": []}; void _searchCrew(xcontext, List qualif) async { final res = await showModalBottomSheet( context: context, builder: (context) => RostersCrewFilter(qualif: qualif, crewFilter: crewFilter)); if (res is Map>) { Hive.box("settings").put("rosters.filter.crew", (res)); setState(() { crewFilter = res; }); } } final double rosterswidth = 110; final double rostersheight = 60; Widget _duty2line(Pnleg leg, {bool showdatedep = true}) { // print( // ">>${leg.label},${leg.type},${leg.dep},${leg.arr},${leg.jdep?.format(pattern: "HHmm")},${leg.jarr?.format(pattern: "HHmm")},"); final whdep = showdatedep ? Text( "${leg.jdep?.format(pattern: "HHmm")}", style: const TextStyle(fontSize: 10, color: Colors.teal), ) : const Row(children: [ Icon( Icons.arrow_back_ios_new_outlined, size: 12, color: Colors.teal, ), Icon( Icons.arrow_back_ios_new_outlined, size: 12, color: Colors.teal, ), ]); final wharr = Text( "${leg.jarr?.format(pattern: "HHmm")}", style: const TextStyle(fontSize: 10, color: Colors.teal), ); if (leg.type == "L" || (leg.type == "F")) { return InkWell( onTap: () { context.push("/fltinfo", extra: FltinfoParams( al: leg.al, fnum: leg.fnum, dep: leg.dep, des: leg.arr, jdep: leg.jdep, jdes: leg.jarr)); }, child: MyRow(children: [ whdep, const Gap(3), Text( "${leg.dep}-${leg.arr}", style: GoogleFonts.robotoMono( fontSize: 11, fontWeight: FontWeight.w300), ), const Gap(3), wharr, ]), ); } else if ((leg.type == "G") || ((leg.dep ?? "") != "" && (leg.arr ?? "") != "")) { return InkWell( onTap: () { context.push("/dutyinfo", extra: DutyinfoParams( dutytype: leg.dutytype, label: leg.label, dep: leg.dep, des: leg.arr, jdep: leg.jdep, jdes: leg.jarr)); }, child: MyRow(children: [ whdep, const Gap(3), const Text("Limo ", style: TextStyle(fontSize: 11, color: Colors.amber)), Text( "${leg.dep}-${leg.arr}", style: GoogleFonts.robotoMono( fontSize: 11, fontWeight: FontWeight.w300), ), const Gap(3), wharr, ]), ); } // else if ((leg.type == "F") || // ((leg.dep ?? "") != "" && (leg.arr ?? "") != "")) { // return MyRow(children: [ // whdep, // const Gap(3), // const Text("DH ", style: TextStyle(fontSize: 11, color: Colors.amber)), // Text( // "${leg.dep}-${leg.arr}", // style: // GoogleFonts.robotoMono(fontSize: 11, fontWeight: FontWeight.w300), // ), // const Gap(3), // wharr, // ]); // } else if ((!["OFF", "CM", "CA", "PP"].contains(leg.label)) && (leg.jarr != null && leg.jdep != null && DTInterval(leg.jdep!, leg.jarr!).duration.inHours < 18)) { return InkWell( onTap: () { context.push("/dutyinfo", extra: DutyinfoParams( dutytype: leg.dutytype, label: leg.label, dep: leg.dep, des: leg.arr, jdep: leg.jdep, jdes: leg.jarr)); }, child: MyRow(children: [ whdep, const Gap(3), Text("${leg.type == "S" ? "SIMU " : ""}${leg.label}", style: const TextStyle(fontSize: 11, color: Colors.amber)), Text( " ${leg.dep}", style: GoogleFonts.robotoMono( fontSize: 8, fontWeight: FontWeight.w300), ), const Gap(3), wharr, ]), ); } else { return Text( leg.label ?? "", style: TextStyle( fontSize: 16, color: Colors.blueGrey[800], fontWeight: FontWeight.w700, letterSpacing: 1), ); } } Widget _rostersDay( {required Jiffy date, required List legs, Color? color, bool border = false}) { return Container( decoration: BoxDecoration( border: border ? Border.all(color: Colors.white, width: 1) : null, color: color ?? Colors.grey[900]), width: rosterswidth, height: rosterswidth, child: Column(children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( date.format(pattern: "EEE"), style: const TextStyle( fontSize: 14, color: Colors.blueGrey, fontWeight: FontWeight.w700), ), Text( date.format(pattern: "dd MMM "), style: const TextStyle( fontSize: 14, color: Color.fromARGB(255, 151, 163, 55), fontWeight: FontWeight.w500), ) ], ), Expanded( child: MyCol( crossAxisAlignment: CrossAxisAlignment.start, children: legs .map((leg) => _duty2line(leg, showdatedep: date.yMEd == leg.jdep?.yMEd)) .toList())) ]), ); } bool showalldates = false; List alldates = []; List alltlcs = []; List favtlcs = []; @override Widget build(BuildContext context) { final data = ref.watch(dataProvider); final pnleg = data.pnleg; final qualif = data.qualif; // final now = ref.watch(clockProvider); final now = Jiffy.now().toUtc(); // final pnlegmeta = ref.watch(pnlegMeta); // alltlcs = pnlegmeta.tlcs; // alldates = pnlegmeta.dates; alltlcs = data.pnleg_tlcs; alldates = data.pnleg_dates; final dates = alldates.where( (e) => e.isAfter(now.toUtc().startOf(Unit.day).subtract(days: 3))); // final dates = data.pnleg_dates.where( // (e) => e.isAfter(now.toUtc().startOf(Unit.day).subtract(days: 3))); final Set tlcsCrewfilter = qualif .where((e) => ((crewFilter["college"]!.isEmpty || crewFilter["college"]!.contains(e.college)) && (crewFilter["ac"]!.isEmpty || crewFilter["ac"]!.contains(e.ac)) && (crewFilter["base"]!.isEmpty || crewFilter["base"]!.contains(e.base)))) .map((e) => e.tlc ?? "") .toSet(); final Set tlcsFlightfilter = (flightFilter.values.every((e) => e == "") ? alltlcs : alltlcs.where((e) => ((flightFilter["al"] == "" || pnleg.firstWhereOrNull((k) => (k.tlc == e) && (k.al == flightFilter["al"])) != null) && (flightFilter["fnum"] == "" || pnleg.firstWhereOrNull((k) => (k.tlc == e) && (k.fnum == flightFilter["fnum"])) != null) && (flightFilter["dep"] == "" || pnleg.firstWhereOrNull((k) => (k.tlc == e) && (k.dep == flightFilter["dep"])) != null) && (flightFilter["des"] == "" || pnleg.firstWhereOrNull((k) => (k.tlc == e) && (k.arr == flightFilter["des"])) != null)))) .toSet(); // final Set tlcsFlightfilter = pnleg // .where((k) => (k.jdep != null && // (k.jdep!.isSameOrAfter(dates.first.startOf(Unit.day))) && // (flightFilter["al"] == "" || k.al == flightFilter["al"]) && // (flightFilter["fnum"] == "" || k.fnum == flightFilter["fnum"]) && // (flightFilter["dep"] == "" || k.dep == flightFilter["dep"]) && // (flightFilter["des"] == "" || k.arr == flightFilter["des"]))) // .map((e) => e.tlc ?? "") // .toSet(); final tlcs = (tlcsCrewfilter.intersection(tlcsFlightfilter).sortedBy((e) => "${data.qualif.firstWhereOrNull((k) => k.tlc == e)?.lname} ${data.qualif.firstWhereOrNull((k) => k.tlc == e)?.fname}")); Widget getPnIndex(index, tlcs, cat) { final qualifData = qualif.where((k) => k.tlc == tlcs.elementAt(index)); final tlc = tlcs.elementAt(index); final dutiesTlcAll = ref.watch(pnlegByTlcProvider(tlc)); final dutiesTlc = dutiesTlcAll; // final dutiesTlc = flightFilter.values.every((x) => x == "") // ? dutiesTlcAll // : (dutiesTlcAll.where((e) => ((flightFilter["al"] == "" || // e.al == flightFilter["al"]) && // (flightFilter["fnum"] == "" || e.fnum == flightFilter["fnum"]) && // (flightFilter["dep"] == "" || e.dep == flightFilter["dep"]) && // (flightFilter["des"] == "" || e.arr == flightFilter["des"])))); final name = "${qualifData.firstOrNull?.lname?.capitalizeword()}, ${qualifData.firstOrNull?.fname?.capitalizeword()}"; final base = "${qualifData.map((e) => e.base).toSet()}"; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(2), child: Row(children: [ const Gap(10), SizedBox.square( dimension: 15, child: InkWell( onTap: () { if (favtlcs.contains(tlc)) { favtlcs.remove(tlc); } else { favtlcs.add(tlc); } setState(() {}); Hive.box("settings").put("rosters.favtlcs", favtlcs); }, child: Icon( (favtlcs.contains(tlc)) ? Icons.favorite : Icons.favorite_border, size: 18, color: (favtlcs.contains(tlc)) ? Colors.red : Colors.grey, ), )), const Gap(10), Text(name, style: const TextStyle( fontWeight: FontWeight.w700, letterSpacing: 1)), const Gap(10), Text( tlc, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w300), ), const Gap(5), Text( base, style: GoogleFonts.robotoMono( fontSize: 11, fontWeight: FontWeight.w300), ) ])), SizedBox( height: rostersheight, child: (dates.isEmpty) ? const Text("No Pnlegs data found") : SuperListView.builder( listController: _listControllerH, delayPopulatingCacheArea: true, // controller: scrollControllerGroup.addAndGet(), //_scrollCtrl, controller: _getCtrl(index, cat: cat), //_scrollCtrl, scrollDirection: Axis.horizontal, itemCount: dates.length, // Number of horizontal items itemBuilder: (context, horizontalIndex) { final date = dates.elementAt(horizontalIndex); final duties = dutiesTlc.where((e) => DTInterval( e.jdep ?? e.jdate?.startOf(Unit.day) ?? Jiffy.now(), e.jarr ?? e.jdate?.endOf(Unit.day) ?? Jiffy.now()) .isOverlap(DTInterval( date.startOf(Unit.day), date.endOf(Unit.day)))); if (pnleg.isEmpty) { return const Padding( padding: EdgeInsets.all(5.0), child: Center( child: Text("No Pnlegs data found"), ), ); } return Padding( padding: const EdgeInsets.symmetric( horizontal: 2, vertical: 1), child: _rostersDay( date: date, legs: duties.toList(), color: date .endOf(Unit.day) .isBefore(now.startOf(Unit.day)) ? Colors.grey[900] : Colors.black, border: date.yMEd == now.yMEd), ); }, ), ), ], ); } return BasicPage( actions: [Text("showing ${tlcs.length} crew member"), const Gap(5)], bottomNavigationBar: Container( padding: const EdgeInsets.only(left: 8, right: 8, bottom: 2, top: 6), // color: Colors.black, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.grey[700]!, Colors.black, ], )), child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Row( children: [ ElevatedButton.icon( onPressed: () { _searchCrew(context, qualif); }, icon: const Icon(Icons .calendar_month), //icon data for elevated button label: const Text("Filter\nCrew"), //label text style: bottomnavstyle, ), const Gap(10), Row( children: [ Column( mainAxisSize: MainAxisSize.min, children: crewFilter.keys .where((e) => crewFilter[e]!.isNotEmpty) .map((e) => Text( "${crewFilter[e]}", style: const TextStyle( fontSize: 12, color: Colors.yellow), )) .toList()), const Gap(10), if (crewFilter.values.flattened.isNotEmpty) Badge( label: Text(crewFilter.values.flattened.length .toString()), child: IconButton.outlined( onPressed: () { for (var k in crewFilter.keys) { crewFilter[k]!.clear(); } Hive.box("settings") .delete("rosters.filter.crew"); setState(() {}); }, icon: const Icon(Icons.filter_alt_off_rounded, size: 18)), ), ], ) ], ), const Gap(10), Row( children: [ ElevatedButton.icon( onPressed: () { _searchFlights(context); }, icon: const Icon(Icons.manage_search), label: const Text("Filter\nFlights"), style: bottomnavstyle, ), const Gap(10), if (flightFilter.values.any((e) => e != "")) Badge( label: Text(flightFilter.values .where((e) => e != "") .length .toString()), child: IconButton.outlined( onPressed: () { for (var k in flightFilter.keys) { flightFilter[k] = ""; } Hive.box("settings") .delete("rosters.filter.flights"); setState(() {}); }, icon: const Icon(Icons.filter_alt_off_rounded, size: 18)), ) ], ), const Gap(10), //icon data for elevated button ], ), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ data.aclegupdate != null ? Text("data retrieved ${data.pnlegupdate?.from(now)}", style: GoogleFonts.robotoMono( fontSize: 10, fontWeight: FontWeight.w300, color: Colors.white)) : Text("no data found", style: GoogleFonts.robotoMono( fontSize: 10, fontWeight: FontWeight.w300)) ], ), ], ), ), title: "Rosters", body: tlcs.isEmpty ? const Text("no Pnleg data") : Column( children: [ Column( children: favtlcs .mapIndexed( (index, tlc) => getPnIndex(index, favtlcs, 1)) .toList()), const Divider(), Expanded( child: SuperListView.builder( listController: _listControllerV, delayPopulatingCacheArea: true, itemCount: tlcs.length, // Number of list items itemBuilder: (context, index) { return getPnIndex(index, tlcs, 0); }, ), ), ], )); } @override void initState() { Future.delayed(Duration.zero).then((x) async { final savedcrewfilter = await Hive.box("settings").get("rosters.filter.crew"); final savedflightsfilter = await Hive.box("settings").get("rosters.filter.flights"); final savedfavtlcs = await Hive.box("settings").get("rosters.favtlcs"); // print( // "rosters: saved crew filter: ${savedcrewfilter.runtimeType} $savedcrewfilter"); // print("rosters: saved flights filter: $savedflightsfilter"); if (savedcrewfilter != null && savedcrewfilter is Map) { crewFilter = savedcrewfilter .map((key, value) => MapEntry(key, List.from(value))); } if (savedflightsfilter != null && savedflightsfilter is Map) { flightFilter = savedflightsfilter.map((key, value) => MapEntry(key, value)); } if (savedfavtlcs != null && savedfavtlcs is List) { favtlcs = savedfavtlcs; } if (favtlcs.isEmpty) { final crewlinkuser = Hive.box("profile").get("crewlink_user"); if (crewlinkuser is String && crewlinkuser.isNotEmpty) { favtlcs.add(crewlinkuser); } } setState(() {}); }); super.initState(); } } class PnlegMeta { PnlegMeta({required this.tlcs, required this.dates}); final List tlcs; final List dates; } // final pnlegMeta = Provider((ref) { // print("data: start analyzing pnleg"); // final now = Jiffy.now(); // final data = ref.watch(dataProvider); // final pnlegs = data.pnleg; // final qualif = data.qualif; // Set tlcs = {}; // Set dates = {}; // for (Pnleg pnleg in pnlegs) { // tlcs.add(pnleg.tlc ?? ""); // dates.add(pnleg.jdep?.startOf(Unit.day) ?? // Jiffy.parse(pnleg.date ?? "01/01/1970", // pattern: "dd/MM/yyyy", isUtc: true)); // } // // final restlcs = tlcs.sortedBy((k) =>qualif.firstWhereOrNull((q) => q.tlc == k)?.lname ?? "zzzzzzzzzzzzz"); // final restlcs = tlcs.sortedBy((e) => // "${qualif.firstWhereOrNull((k) => k.tlc == e)?.lname} ${qualif.firstWhereOrNull((k) => k.tlc == e)?.fname}"); // final resdates = dates.sortedBy((k) => k.millisecondsSinceEpoch.toString()); // print( // "data: end analyzing pnleg ${Duration(milliseconds: Jiffy.now().millisecondsSinceEpoch - now.millisecondsSinceEpoch).inSeconds} sec"); // return PnlegMeta(tlcs: restlcs, dates: resdates); // }); // final pnlegMeta = Provider((ref) { // print("data: start analyzing pnleg"); // final now = Jiffy.now(); // final data = ref.watch(dataProvider); // final pnleg = data.pnleg; // final qualif = data.qualif; // print( // "data: end analyzing pnleg ${Duration(milliseconds: Jiffy.now().millisecondsSinceEpoch - now.millisecondsSinceEpoch).inSeconds} sec"); // return PnlegMeta( // tlcs: pnleg.fold({}, (t, e) => {...t, e.tlc ?? ""}).sortedBy((e) { // final pn = qualif.firstWhereOrNull((q) => q.tlc == e); // return "${pn?.lname} ${pn?.fname}"; // }), // dates: pnleg.fold( // {}, // (t, e) => { // ...t, // e.jdep?.startOf(Unit.day) ?? // Jiffy.parse(e.date ?? "01/01/1970", // pattern: "dd/MM/yyyy", isUtc: true) // }).sortedBy((e) => e.dateTime)); // });