| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- // ignore_for_file: use_build_context_synchronously
- 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:tp5/core/core.dart';
- import 'package:tp5/roster/api/crewlink_api.dart';
- import 'package:tp5/roster/models/crewlist_leg.dart';
- import 'package:tp5/roster/widgets/w_crewlist.dart';
- import 'package:tp5/roster/widgets/w_day.dart';
- import 'package:tp5/core/basic_page.dart';
- import 'package:scroll_to_index/scroll_to_index.dart';
- class CrewlistPage extends ConsumerStatefulWidget {
- const CrewlistPage({required this.params, super.key});
- final CrewlistPageParams params;
- @override
- ConsumerState<ConsumerStatefulWidget> createState() => _CrewlistPageState();
- }
- class _CrewlistPageState extends ConsumerState<CrewlistPage> {
- late String crewlinkUser;
- late String crewlinkPass;
- TextEditingController ctrldep = TextEditingController();
- TextEditingController ctrldes = TextEditingController();
- TextEditingController ctrlairline = TextEditingController();
- TextEditingController ctrlfnum = TextEditingController();
- TextEditingController ctrlstartdate = TextEditingController();
- TextEditingController ctrlenddate = TextEditingController();
- Map? apires;
- List<CrewlistLeg> legs = [];
- String get _crewlistKey =>
- "crewlist_${widget.params.datestart}_${widget.params.dateend}_${widget.params.al}_${widget.params.fnum}_${widget.params.dep}_${widget.params.des}";
- String get _crewlistMinMaxKey => "minmax_crewlist";
- final AutoScrollController _scrollCtrl = AutoScrollController();
- late Jiffy now;
- @override
- void initState() {
- crewlinkUser = widget.params.crewlinkuser!;
- crewlinkPass = widget.params.crewlinkpass!;
- Future.delayed(Duration.zero, () => _loadCrewlist());
- super.initState();
- }
- Future<Map?> _loadCrewlistOnline() async {
- if (!ref.read(crewlinkapiProvider).logged) {
- final login = await ref
- .read(crewlinkapiProvider)
- .login(username: crewlinkUser, password: crewlinkPass);
- if (login["data"]?["logged"] != true) {
- context.showError(login["error"] ?? "Unknown error");
- return null;
- }
- }
- final res = await ref.read(crewlinkapiProvider).showCrew(
- crewlist: false,
- start: widget.params.datestart!,
- end: widget.params.dateend!,
- al: widget.params.al!,
- fnum: widget.params.fnum!,
- dep: widget.params.dep!,
- des: widget.params.des!);
- if (res?["data"]?["msg"] != null) {
- context.showError(res?["data"]?["msg"] ?? "Unknown error");
- }
- if (res["error"] == null && res["data"] != null) {
- if (res["msg"] != null) context.showAlert(res["msg"]);
- Hive.box("crewlink").put(_crewlistKey, res);
- return res;
- }
- return null;
- }
- Future<Map?> _loadCrewlistOffline() async {
- return Hive.box("crewlink").get(_crewlistKey);
- }
- _loadCrewlist() async {
- try {
- ref.read(isLoadingProvider.notifier).state = true;
- final crewlistOffline = await _loadCrewlistOffline();
- if (crewlistOffline != null && mounted) {
- _convertCrewlist(crewlistOffline);
- setState(() {});
- _scrollToDate();
- }
- final crewlistOnline = await _loadCrewlistOnline();
- if (crewlistOnline != null && mounted) {
- _convertCrewlist(crewlistOnline);
- setState(() {});
- _scrollToDate();
- }
- } finally {
- ref.read(isLoadingProvider.notifier).state = false;
- }
- }
- _scrollToDate({Jiffy? date}) {
- final jdate = date ?? Jiffy.now().toUtc();
- print("scroll request to ${jdate.Hm}");
- bool found = false;
- int id = 0;
- if (legs.isNotEmpty &&
- jdate.isSameOrAfter(legs.map((e) => e.jdep).first) &&
- jdate.isSameOrBefore(legs.map((e) => e.jdep).last)) {
- for (CrewlistLeg leg in legs) {
- if (leg.jdep.isSameOrAfter(jdate)) {
- found = true;
- break;
- }
- id++;
- }
- }
- Future.delayed(const Duration(milliseconds: 100)).then((value) {
- if ((found || jdate.yMd == Jiffy.now().toUtc().yMd) &&
- mounted &&
- _scrollCtrl.hasClients) {
- print("scrolling to id:$id ${jdate.Hm}");
- _scrollCtrl.scrollToIndex(id,
- duration: const Duration(milliseconds: 1300),
- preferPosition: AutoScrollPosition.begin);
- }
- });
- }
- void _convertCrewlist(Map? input) {
- // print("${input?["data"]["leglist"].first[7].runtimeType}");
- apires = input;
- legs = [];
- for (var leg in input?["data"]?["leglist"] ?? []) {
- final CrewlistLeg newleg = CrewlistLeg(leg);
- if ((newleg.jdep.isSameOrAfter(Jiffy.parse(widget.params.datestart ?? "",
- pattern: "ddMMMyy", isUtc: true)
- .startOf(Unit.day))) &&
- (newleg.jdep.isSameOrBefore(Jiffy.parse(widget.params.dateend ?? "",
- pattern: "ddMMMyy", isUtc: true)
- .endOf(Unit.day))) &&
- (legs.firstWhereOrNull((e) =>
- "${e.al}${e.fnum}${e.dep}${e.des}${e.jdep.millisecondsSinceEpoch}" ==
- "${newleg.al}${newleg.fnum}${newleg.dep}${newleg.des}${newleg.jdep.millisecondsSinceEpoch}") ==
- null)) legs.add(newleg);
- }
- legs.sort((a, b) {
- if (a.jdep.isBefore(b.jdep)) {
- return -1;
- } else if (a.jdep.isAfter(b.jdep)) {
- return 1;
- } else {
- return 0;
- }
- });
- }
- final bottomnavstyle = ElevatedButton.styleFrom(
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(5.0),
- ),
- backgroundColor:
- const Color.fromARGB(255, 0, 36, 53) //elevated btton background color
- );
- @override
- Widget build(BuildContext context) {
- now = ref.watch(clockProvider);
- return BasicPage(
- actions: [
- IconButton(
- onPressed: () => context.push("/crewlink/settings"),
- icon: const Icon(Icons.settings)),
- const Gap(10)
- ],
- bottomNavigationBar: Container(
- padding: const EdgeInsets.all(8),
- // color: Colors.black,
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: [
- Colors.grey[700]!,
- Colors.black,
- ],
- )),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- ElevatedButton.icon(
- onPressed: () {
- _changeDay(context);
- },
- icon: const Icon(
- Icons.calendar_month), //icon data for elevated button
- label: const Text("Change\n Day"), //label text
- style: bottomnavstyle,
- ),
- const Gap(10),
- ElevatedButton.icon(
- onPressed: () {
- _searchFlights(context);
- },
- icon: const Icon(Icons.manage_search),
- label: const Text("Search\nFlights"),
- style: bottomnavstyle,
- ),
- const Gap(10), //icon data for elevated button
- ],
- ),
- ),
- title: "CrewLink / Crewlist",
- body: ListView.builder(
- itemCount: legs.length,
- itemBuilder: (_, index) => AutoScrollTag(
- key: ValueKey(index),
- controller: _scrollCtrl,
- index: index,
- child: _getItem(_, index)),
- shrinkWrap: true,
- controller: _scrollCtrl,
- ),
- );
- }
- Widget _getItem(BuildContext ctx, int i) {
- final leg = legs.elementAt(i);
- CrewlistLeg? firstitem =
- legs.firstWhereOrNull((CrewlistLeg el) => el.date == leg.date);
- bool isfirstitem = false;
- if (firstitem != null && firstitem.data == leg.data) isfirstitem = true;
- return Column(
- children: [
- if (i == 0) ...[
- const Gap(20),
- InkWell(
- onTap: () => context.push("/crewlink/crewlist",
- extra: CrewlistPageParams(
- al: widget.params.al,
- fnum: widget.params.fnum,
- dep: widget.params.dep,
- des: widget.params.des,
- datestart: Jiffy.parse(widget.params.datestart ?? leg.date,
- pattern: "ddMMMyy", isUtc: true)
- .subtract(days: 1)
- .format(pattern: "ddMMMyy"),
- dateend: Jiffy.parse(widget.params.dateend ?? leg.date,
- pattern: "ddMMMyy", isUtc: true)
- .subtract(days: 1)
- .format(pattern: "ddMMMyy"),
- )),
- child: Container(
- margin: const EdgeInsets.symmetric(horizontal: 15),
- width: double.infinity,
- padding: const EdgeInsets.all(5),
- decoration:
- BoxDecoration(border: Border.all(color: Colors.grey)),
- child: const Text(
- "Load Previous Day",
- textAlign: TextAlign.center,
- style: TextStyle(fontSize: 12),
- ),
- )),
- const Gap(20)
- ],
- if (isfirstitem)
- WDay(
- date: leg.jdate,
- highlight: now.yMd == leg.jdate.yMd,
- onTap: () => context.push("/crewlink/crewlist",
- extra: CrewlistPageParams(
- datestart: leg.jdate.format(pattern: "ddMMMyy"))),
- ),
- SingleChildScrollView(
- scrollDirection: Axis.horizontal,
- child: WCrewlist(
- leg: leg,
- ),
- ),
- Divider(
- height: 1,
- thickness: 1,
- color: Colors.grey[700],
- ),
- const Gap(5),
- if (i == legs.length - 1) ...[
- const Gap(20),
- InkWell(
- onTap: () => context.push("/crewlink/crewlist",
- extra: CrewlistPageParams(
- al: widget.params.al,
- fnum: widget.params.fnum,
- dep: widget.params.dep,
- des: widget.params.des,
- datestart: Jiffy.parse(widget.params.datestart ?? leg.date,
- pattern: "ddMMMyy", isUtc: true)
- .add(days: 1)
- .format(pattern: "ddMMMyy"),
- dateend: Jiffy.parse(widget.params.dateend ?? leg.date,
- pattern: "ddMMMyy", isUtc: true)
- .add(days: 1)
- .format(pattern: "ddMMMyy"),
- )),
- child: Container(
- margin: const EdgeInsets.symmetric(horizontal: 15),
- width: double.infinity,
- padding: const EdgeInsets.all(5),
- decoration:
- BoxDecoration(border: Border.all(color: Colors.grey)),
- child: const Text(
- "Load Next Day",
- textAlign: TextAlign.center,
- style: TextStyle(fontSize: 12),
- ),
- )),
- const Gap(20)
- ],
- ],
- );
- }
- void _changeDay(xcontext) async {
- ref.read(isLoadingProvider.notifier).state = true;
- final resoff = Hive.box("crewlink").get(_crewlistMinMaxKey);
- final reson = await ref.read(crewlinkapiProvider).showCrewMinMax();
- ref.read(isLoadingProvider.notifier).state = false;
- dynamic res;
- if (reson["error"] == null && reson?["data"] != null) {
- Hive.box("crewlink").put(_crewlistMinMaxKey, reson["data"]);
- res = reson["data"];
- } else if (resoff == null) {
- context.showError(reson["error"] ?? "Unknown error");
- return;
- }
- res = res ?? resoff;
- if (res != null) {
- final firstDate =
- Jiffy.parse(res["mindate"], pattern: "yyyy-MM-dd", isUtc: true);
- final lastDate =
- Jiffy.parse(res["maxdate"], pattern: "yyyy-MM-dd", isUtc: true);
- final initialDate = Jiffy.parse(widget.params.datestart ?? "",
- pattern: "ddMMMyy", isUtc: true)
- .max(firstDate)
- .min(lastDate);
- final DateTime? pickedDate = await showDatePicker(
- context: xcontext,
- confirmText: null,
- initialDate: initialDate.dateTime,
- firstDate: firstDate.dateTime,
- lastDate: lastDate.dateTime,
- currentDate: Jiffy.now().toUtc().dateTime,
- selectableDayPredicate: (day) => true,
- initialDatePickerMode: DatePickerMode.day,
- );
- if (pickedDate != null) {
- context.push("/crewlink/crewlist",
- extra: CrewlistPageParams(
- datestart: Jiffy.parseFromDateTime(pickedDate)
- .format(pattern: "ddMMMyy")));
- }
- }
- }
- Widget _searchFilter(BuildContext xcontext, DateTime min, DateTime max) =>
- 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),
- Row(
- children: [
- Expanded(
- child: TextField(
- decoration:
- const InputDecoration(labelText: "Starting date"),
- controller: ctrlstartdate,
- readOnly:
- true, //set it true, so that user will not able to edit text
- onTap: () async {
- DateTime? pickedDate = await showDatePicker(
- context: context,
- initialDate: DateTime.now().toUtc(),
- firstDate:
- min, //DateTime.now () - not to allow to choose before today.
- lastDate: max);
- if (pickedDate != null) {
- String formattedDate =
- Jiffy.parseFromDateTime(pickedDate).format(
- pattern:
- "ddMMMyy"); //formatted date output using intl package => 2021-03-16
- setState(() {
- ctrlstartdate.text = formattedDate;
- if (Jiffy.parseFromDateTime(pickedDate).isAfter(
- Jiffy.parse(ctrlenddate.text,
- pattern: "ddMMMyy", isUtc: true))) {
- ctrlenddate.text = formattedDate;
- }
- });
- } else {
- print("Date is not selected");
- }
- }),
- ),
- const SizedBox(width: 10),
- Expanded(
- child: TextField(
- decoration:
- const InputDecoration(labelText: "Ending date"),
- controller: ctrlenddate,
- readOnly:
- true, //set it true, so that user will not able to edit text
- onTap: () async {
- DateTime? pickedDate = await showDatePicker(
- context: context,
- initialDate: DateTime.now().toUtc(),
- firstDate: min,
- lastDate: max);
- if (pickedDate != null) {
- String formattedDate =
- Jiffy.parseFromDateTime(pickedDate)
- .format(pattern: "ddMMMyy");
- setState(() {
- ctrlenddate.text = formattedDate;
- if (Jiffy.parseFromDateTime(pickedDate).isBefore(
- Jiffy.parse(ctrlstartdate.text,
- pattern: "ddMMMyy", isUtc: true))) {
- ctrlstartdate.text = formattedDate;
- }
- });
- } else {
- print("Date is not selected");
- }
- }),
- ),
- ],
- ),
- const SizedBox(height: 35),
- SizedBox(
- width: 360,
- child: ElevatedButton(
- onPressed: () =>
- Navigator.pop(context, true), // 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(
- 'Search Flights',
- style: TextStyle(fontSize: 18, color: Colors.white),
- ),
- ),
- ),
- ],
- ),
- );
- void _searchFlights(xcontext) async {
- ref.read(isLoadingProvider.notifier).state = true;
- final resoff = Hive.box("crewlink").get(_crewlistMinMaxKey);
- final reson = await ref.read(crewlinkapiProvider).showCrewMinMax();
- ref.read(isLoadingProvider.notifier).state = false;
- dynamic res;
- if (reson["error"] == null && reson?["data"] != null) {
- Hive.box("crewlink").put(_crewlistMinMaxKey, reson["data"]);
- res = reson["data"];
- } else if (resoff == null) {
- context.showError(reson["error"] ?? "Unknown error");
- return;
- }
- res = res ?? resoff;
- if (res != null) {
- ctrldep.text = widget.params.dep!;
- ctrldes.text = widget.params.des!;
- ctrlairline.text = widget.params.al!;
- ctrlfnum.text = widget.params.fnum!;
- ctrlstartdate.text = widget.params.datestart!;
- ctrlenddate.text = widget.params.dateend!;
- final out = await showModalBottomSheet(
- context: context,
- builder: (context) => _searchFilter(
- xcontext,
- Jiffy.parse(res["mindate"], isUtc: true, pattern: "yyyy-MM-dd")
- .dateTime,
- Jiffy.parse(res["maxdate"], isUtc: true, pattern: "yyyy-MM-dd")
- .dateTime));
- if (out == true) {
- context.push("/crewlink/crewlist",
- extra: CrewlistPageParams(
- dep: ctrldep.text.toUpperCase(),
- des: ctrldes.text.toUpperCase(),
- al: ctrlairline.text.toUpperCase(),
- fnum: ctrlfnum.text,
- datestart: ctrlstartdate.text,
- dateend: ctrlenddate.text,
- ));
- }
- }
- }
- }
- class CrewlistPageParams {
- const CrewlistPageParams({
- this.dateend,
- this.datestart,
- this.al,
- this.fnum,
- this.dep,
- this.des,
- this.crewlinkuser,
- this.crewlinkpass,
- });
- final String? dateend;
- final String? datestart;
- final String? al;
- final String? fnum;
- final String? dep;
- final String? des;
- final String? crewlinkuser;
- final String? crewlinkpass;
- }
|