| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761 | 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<ConsumerStatefulWidget> createState() => _RostersPageState();}class _RostersPageState extends ConsumerState<RostersPage> {  final scrollControllerGroup = LinkedScrollControllerGroup();  Map<int, Map<int, ScrollController>> 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<String, String> 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<String, String>) {      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<String, List<String>> crewFilter = {"college": [], "ac": [], "base": []};  void _searchCrew(xcontext, List<Qualif> qualif) async {    final res = await showModalBottomSheet(        context: context,        builder: (context) =>            RostersCrewFilter(qualif: qualif, crewFilter: crewFilter));    if (res is Map<String, List<String>>) {      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.dutytype == "dhflight" || leg.dutytype == "flight") {      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.dutytype == "dhlimo") {      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.dutytype == "standby") {      return InkWell(        onTap: () {          context.push("/dutyinfo",              extra: DutyinfoParams(                sameday: true, tlc: leg.tlc,                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.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 if (leg.dutytype == "ground") {      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<Pnleg> 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<Jiffy> alldates = [];  List<String> alltlcs = [];  List<String> 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<String> 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<String> 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<String> 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<String>.from(value)));      }      if (savedflightsfilter != null && savedflightsfilter is Map) {        flightFilter =            savedflightsfilter.map((key, value) => MapEntry(key, value));      }      if (savedfavtlcs != null && savedfavtlcs is List<String>) {        favtlcs = savedfavtlcs;      }      if (favtlcs.isEmpty) {        final crewlinkuser = Hive.box("settings").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<String> tlcs;  final List<Jiffy> 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<String> tlcs = {};//   Set<Jiffy> 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(<String>{}, (t, e) => {...t, e.tlc ?? ""}).sortedBy((e) {//         final pn = qualif.firstWhereOrNull((q) => q.tlc == e);//         return "${pn?.lname} ${pn?.fname}";//       }),//       dates: pnleg.fold(//           <Jiffy>{},//           (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));// });
 |