Feat: Finishing flow booking tickets and UI boarding pass for transit tickets category
This commit is contained in:
parent
ded8ce37b8
commit
7a2b8db157
|
@ -680,7 +680,7 @@
|
||||||
"languageVersion": "3.4"
|
"languageVersion": "3.4"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"generated": "2025-05-07T08:39:04.087158Z",
|
"generated": "2025-05-09T09:48:57.570155Z",
|
||||||
"generator": "pub",
|
"generator": "pub",
|
||||||
"generatorVersion": "3.5.0",
|
"generatorVersion": "3.5.0",
|
||||||
"flutterRoot": "file:///D:/Flutter/flutter_sdk/flutter_3.24.0",
|
"flutterRoot": "file:///D:/Flutter/flutter_sdk/flutter_3.24.0",
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||||
import '../../domain/models/ticket_model.dart';
|
import '../../domain/models/ticket_model.dart';
|
||||||
import '../../domain/repositories/ticket_repository.dart';
|
import '../../domain/repositories/ticket_repository.dart';
|
||||||
|
@ -12,27 +14,35 @@ class TicketRepositoryImpl implements TicketRepository {
|
||||||
required String from,
|
required String from,
|
||||||
required String to,
|
required String to,
|
||||||
required DateTime leavingDate,
|
required DateTime leavingDate,
|
||||||
|
String? seatClass,
|
||||||
}) async {
|
}) async {
|
||||||
final collection = firestore.collection('tickets');
|
// final collection = firestore.collection('tickets');
|
||||||
|
|
||||||
|
// final startOfDay = DateTime(leavingDate.year, leavingDate.month, leavingDate.day);
|
||||||
|
// final endOfDay = startOfDay.add(Duration(days: 1));
|
||||||
|
|
||||||
|
// final snapshot = await collection
|
||||||
|
// .where('from', isEqualTo: from)
|
||||||
|
// .where('to', isEqualTo: to)
|
||||||
|
// .where('leavingDate', isGreaterThanOrEqualTo: Timestamp.fromDate(startOfDay))
|
||||||
|
// .where('leavingDate', isLessThan: Timestamp.fromDate(endOfDay))
|
||||||
|
// .get();
|
||||||
|
|
||||||
|
// snapshot.docs.forEach((doc) {});
|
||||||
|
|
||||||
// Normalisasi tanggal: ambil awal hari dan akhir hari untuk tanggal yang dicari
|
|
||||||
final startOfDay = DateTime(leavingDate.year, leavingDate.month, leavingDate.day);
|
final startOfDay = DateTime(leavingDate.year, leavingDate.month, leavingDate.day);
|
||||||
final endOfDay = startOfDay.add(Duration(days: 1));
|
final endOfDay = startOfDay.add(Duration(days: 1));
|
||||||
|
|
||||||
// logger.d("Fetching tickets with parameters: from = $from, to = $to, leavingDate between = ${Timestamp.fromDate(startOfDay)} and ${Timestamp.fromDate(endOfDay)}");
|
log("Fetching tickets: from=$from, to=$to, leavingDate∈[$startOfDay – $endOfDay]");
|
||||||
|
|
||||||
final snapshot = await collection
|
final snapshot = await firestore
|
||||||
|
.collection('tickets')
|
||||||
.where('from', isEqualTo: from)
|
.where('from', isEqualTo: from)
|
||||||
.where('to', isEqualTo: to)
|
.where('to', isEqualTo: to)
|
||||||
.where('leavingDate', isGreaterThanOrEqualTo: Timestamp.fromDate(startOfDay))
|
.where('leavingDate', isGreaterThanOrEqualTo: Timestamp.fromDate(startOfDay))
|
||||||
.where('leavingDate', isLessThan: Timestamp.fromDate(endOfDay))
|
.where('leavingDate', isLessThan: Timestamp.fromDate(endOfDay))
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
// logger.d("Number of tickets found: ${snapshot.docs.length}");
|
|
||||||
snapshot.docs.forEach((doc) {
|
|
||||||
// logger.d("Doc ID: ${doc.id} => ${doc.data()}");
|
|
||||||
});
|
|
||||||
|
|
||||||
return snapshot.docs.map((doc) => TicketModel.fromDocument(doc)).toList();
|
return snapshot.docs.map((doc) => TicketModel.fromDocument(doc)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,10 +59,7 @@ class TicketRepositoryImpl implements TicketRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
final snapshot = await query.get();
|
final snapshot = await query.get();
|
||||||
// logger.d("Number of flights found for ticket $ticketId with seatClass '$flightClass': ${snapshot.docs.length}");
|
snapshot.docs.forEach((doc) {});
|
||||||
snapshot.docs.forEach((doc) {
|
|
||||||
// logger.d("Flight Doc ID: ${doc.id} => ${doc.data()}");
|
|
||||||
});
|
|
||||||
|
|
||||||
return snapshot.docs.map((doc) => FlightModel.fromDocument(doc)).toList();
|
return snapshot.docs.map((doc) => FlightModel.fromDocument(doc)).toList();
|
||||||
}
|
}
|
||||||
|
@ -62,15 +69,10 @@ class TicketRepositoryImpl implements TicketRepository {
|
||||||
required String ticketId,
|
required String ticketId,
|
||||||
required String flightId,
|
required String flightId,
|
||||||
}) async {
|
}) async {
|
||||||
final doc = await firestore
|
final doc = await firestore.collection('tickets').doc(ticketId).collection('flights').doc(flightId).get();
|
||||||
.collection('tickets')
|
|
||||||
.doc(ticketId)
|
|
||||||
.collection('flights')
|
|
||||||
.doc(flightId)
|
|
||||||
.get();
|
|
||||||
|
|
||||||
// logger.d("getFlightById - TicketID: $ticketId, FlightID: $flightId, Data: ${doc.data()}");
|
// logger.d("getFlightById - TicketID: $ticketId, FlightID: $flightId, Data: ${doc.data()}");
|
||||||
|
|
||||||
return FlightModel.fromDocument(doc);
|
return FlightModel.fromDocument(doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,9 @@ class FlightModel {
|
||||||
final String codeArrival;
|
final String codeArrival;
|
||||||
final DateTime departureTime;
|
final DateTime departureTime;
|
||||||
final DateTime arrivalTime;
|
final DateTime arrivalTime;
|
||||||
|
final DateTime? startDateTransit;
|
||||||
|
final DateTime? endDateTransit;
|
||||||
|
final String cityTransit;
|
||||||
final String transitAirplane;
|
final String transitAirplane;
|
||||||
final String stop;
|
final String stop;
|
||||||
final int price;
|
final int price;
|
||||||
|
@ -64,6 +67,9 @@ class FlightModel {
|
||||||
required this.codeArrival,
|
required this.codeArrival,
|
||||||
required this.departureTime,
|
required this.departureTime,
|
||||||
required this.arrivalTime,
|
required this.arrivalTime,
|
||||||
|
this.startDateTransit,
|
||||||
|
this.endDateTransit,
|
||||||
|
required this.cityTransit,
|
||||||
required this.transitAirplane,
|
required this.transitAirplane,
|
||||||
required this.stop,
|
required this.stop,
|
||||||
required this.price,
|
required this.price,
|
||||||
|
@ -93,11 +99,14 @@ class FlightModel {
|
||||||
codeArrival: data['codeArrival'] ?? '',
|
codeArrival: data['codeArrival'] ?? '',
|
||||||
departureTime: (data['dateDeparture'] as Timestamp).toDate(),
|
departureTime: (data['dateDeparture'] as Timestamp).toDate(),
|
||||||
arrivalTime: (data['dateArrival'] as Timestamp).toDate(),
|
arrivalTime: (data['dateArrival'] as Timestamp).toDate(),
|
||||||
|
startDateTransit: (data['startDateTransit'] as Timestamp?)?.toDate(),
|
||||||
|
endDateTransit: (data['endDateTransit'] as Timestamp?)?.toDate(),
|
||||||
|
cityTransit: data['cityTransit'] ?? '',
|
||||||
transitAirplane: data['transitAirplane'] ?? '',
|
transitAirplane: data['transitAirplane'] ?? '',
|
||||||
stop: data['stop'] ?? '',
|
stop: data['stop'] ?? '',
|
||||||
price: data['price'] ?? 0,
|
price: data['price'] ?? 0,
|
||||||
airlineLogo: data['airlineLogo'] ?? '',
|
airlineLogo: data['airlineLogo'] ?? '',
|
||||||
seat: seatMap,
|
seat: seatMap,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +122,9 @@ class FlightModel {
|
||||||
'codeArrival': codeArrival,
|
'codeArrival': codeArrival,
|
||||||
'dateDeparture': Timestamp.fromDate(departureTime),
|
'dateDeparture': Timestamp.fromDate(departureTime),
|
||||||
'dateArrival': Timestamp.fromDate(arrivalTime),
|
'dateArrival': Timestamp.fromDate(arrivalTime),
|
||||||
|
'startDateTransit': Timestamp.fromDate(startDateTransit!),
|
||||||
|
'endDateTransit': Timestamp.fromDate(endDateTransit!),
|
||||||
|
'cityTransit': cityTransit,
|
||||||
'transitAirplane': transitAirplane,
|
'transitAirplane': transitAirplane,
|
||||||
'stop': stop,
|
'stop': stop,
|
||||||
'price': price,
|
'price': price,
|
||||||
|
|
|
@ -26,7 +26,9 @@ class CardBoardingPass extends StatelessWidget {
|
||||||
final String? arrivalCode;
|
final String? arrivalCode;
|
||||||
final String? departurePlane;
|
final String? departurePlane;
|
||||||
final String? arrivalPlane;
|
final String? arrivalPlane;
|
||||||
|
final String? transitCity;
|
||||||
final String? transitPlane;
|
final String? transitPlane;
|
||||||
|
final String? transitCode;
|
||||||
final String? transitStartDate;
|
final String? transitStartDate;
|
||||||
final String? transitEndDate;
|
final String? transitEndDate;
|
||||||
final String? departureTime;
|
final String? departureTime;
|
||||||
|
@ -55,7 +57,9 @@ class CardBoardingPass extends StatelessWidget {
|
||||||
this.arrivalCode,
|
this.arrivalCode,
|
||||||
this.departurePlane,
|
this.departurePlane,
|
||||||
this.arrivalPlane,
|
this.arrivalPlane,
|
||||||
|
this.transitCity,
|
||||||
this.transitPlane,
|
this.transitPlane,
|
||||||
|
this.transitCode,
|
||||||
this.transitStartDate,
|
this.transitStartDate,
|
||||||
this.transitEndDate,
|
this.transitEndDate,
|
||||||
this.departureTime,
|
this.departureTime,
|
||||||
|
@ -157,12 +161,14 @@ class CardBoardingPass extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
TypographyStyles.caption("$departureTime", color: GrayColors.gray800),
|
TypographyStyles.caption("$departureTime", color: GrayColors.gray800),
|
||||||
TypographyStyles.small("$departureDate", color: GrayColors.gray600, fontWeight: FontWeight.w400),
|
TypographyStyles.small("$departureDate",
|
||||||
|
color: GrayColors.gray600, fontWeight: FontWeight.w400),
|
||||||
SizedBox(height: 20.h),
|
SizedBox(height: 20.h),
|
||||||
TypographyStyles.small("$duration", color: GrayColors.gray600, fontWeight: FontWeight.w400),
|
TypographyStyles.small("$duration", color: GrayColors.gray600, fontWeight: FontWeight.w400),
|
||||||
SizedBox(height: 20.h),
|
SizedBox(height: 20.h),
|
||||||
TypographyStyles.caption("$arrivalTime", color: GrayColors.gray800),
|
TypographyStyles.caption("$arrivalTime", color: GrayColors.gray800),
|
||||||
TypographyStyles.small("$arrivalDate", color: GrayColors.gray600, fontWeight: FontWeight.w400),
|
TypographyStyles.small("$arrivalDate",
|
||||||
|
color: GrayColors.gray600, fontWeight: FontWeight.w400),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(width: 20.w),
|
SizedBox(width: 20.w),
|
||||||
|
@ -180,7 +186,23 @@ class CardBoardingPass extends StatelessWidget {
|
||||||
maxlines: 2,
|
maxlines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
SizedBox(height: 58.h),
|
SizedBox(height: 20.h),
|
||||||
|
if (transitCity != null &&
|
||||||
|
transitCity!.isNotEmpty &&
|
||||||
|
transitCode != null &&
|
||||||
|
transitCode!.isNotEmpty &&
|
||||||
|
transitPlane != null &&
|
||||||
|
transitPlane!.isNotEmpty) ...[
|
||||||
|
TypographyStyles.caption("$transitCity ($transitCode)", color: GrayColors.gray800),
|
||||||
|
TypographyStyles.caption(
|
||||||
|
"$transitPlane",
|
||||||
|
color: GrayColors.gray600,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
maxlines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
SizedBox(height: 20.h),
|
||||||
|
],
|
||||||
TypographyStyles.caption("$arrivalCity ($arrivalCode)", color: GrayColors.gray800),
|
TypographyStyles.caption("$arrivalCity ($arrivalCode)", color: GrayColors.gray800),
|
||||||
TypographyStyles.caption(
|
TypographyStyles.caption(
|
||||||
"$arrivalPlane",
|
"$arrivalPlane",
|
||||||
|
@ -188,7 +210,7 @@ class CardBoardingPass extends StatelessWidget {
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
maxlines: 2,
|
maxlines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
|
||||||
import 'package:e_porter/_core/component/appbar/appbar_component.dart';
|
import 'package:e_porter/_core/component/appbar/appbar_component.dart';
|
||||||
import 'package:e_porter/_core/constants/colors.dart';
|
import 'package:e_porter/_core/constants/colors.dart';
|
||||||
import 'package:e_porter/_core/utils/formatter/date_helper.dart';
|
import 'package:e_porter/_core/utils/formatter/date_helper.dart';
|
||||||
|
@ -283,13 +282,14 @@ class _BoardingPassScreenState extends State<BoardingPassScreen> with SingleTick
|
||||||
arrivalCode: flightDetails['codeArrival'],
|
arrivalCode: flightDetails['codeArrival'],
|
||||||
departurePlane: departurePlane,
|
departurePlane: departurePlane,
|
||||||
arrivalPlane: arrivalPlane,
|
arrivalPlane: arrivalPlane,
|
||||||
|
transitCity: flightDetails['cityTransit'],
|
||||||
|
transitCode: flightDetails['codeTransit'],
|
||||||
|
transitPlane: flightDetails['transitAirplane'],
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final argument = {
|
final argument = {
|
||||||
'id_transaction': transaction.id,
|
'id_transaction': transaction.id,
|
||||||
'id_ticket': transaction.ticketId,
|
'id_ticket': transaction.ticketId,
|
||||||
};
|
};
|
||||||
log('Arrival Time: $arrivalTime');
|
|
||||||
log('Departure Time: $departureTime');
|
|
||||||
|
|
||||||
Get.toNamed(Routes.DETAILTICKET, arguments: argument);
|
Get.toNamed(Routes.DETAILTICKET, arguments: argument);
|
||||||
},
|
},
|
||||||
|
|
|
@ -140,8 +140,11 @@ class _DetailTicketScreenState extends State<DetailTicketScreen> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
TypographyStyles.caption(departureTime, color: GrayColors.gray800),
|
TypographyStyles.caption(departureTime, color: GrayColors.gray800),
|
||||||
TypographyStyles.small(departureDate,
|
TypographyStyles.small(
|
||||||
color: GrayColors.gray600, fontWeight: FontWeight.w400),
|
departureDate,
|
||||||
|
color: GrayColors.gray600,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
SizedBox(height: 20.h),
|
SizedBox(height: 20.h),
|
||||||
TypographyStyles.small(duration, color: GrayColors.gray600, fontWeight: FontWeight.w400),
|
TypographyStyles.small(duration, color: GrayColors.gray600, fontWeight: FontWeight.w400),
|
||||||
SizedBox(height: 20.h),
|
SizedBox(height: 20.h),
|
||||||
|
@ -152,35 +155,16 @@ class _DetailTicketScreenState extends State<DetailTicketScreen> {
|
||||||
SizedBox(width: 20.w),
|
SizedBox(width: 20.w),
|
||||||
SvgPicture.asset('assets/images/garis.svg', height: 100.h),
|
SvgPicture.asset('assets/images/garis.svg', height: 100.h),
|
||||||
SizedBox(width: 20.w),
|
SizedBox(width: 20.w),
|
||||||
Expanded(
|
_buildDataBandara(
|
||||||
child: Column(
|
titleDeparture:
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
"${transaction.flightDetails['cityDeparture']} (${transaction.flightDetails['codeDeparture']})",
|
||||||
children: [
|
planeDeparture: "${transaction.bandaraDetails['departure']?['name']}",
|
||||||
TypographyStyles.caption(
|
titleTransit:
|
||||||
"${transaction.flightDetails['cityDeparture']} (${transaction.flightDetails['codeDeparture']})",
|
"${transaction.flightDetails['cityTransit']} (${transaction.flightDetails['codeTransit']})",
|
||||||
color: GrayColors.gray800,
|
planeTransit: transaction.flightDetails['transitAirplane'],
|
||||||
),
|
titleArrival:
|
||||||
TypographyStyles.caption(
|
"${transaction.flightDetails['cityArrival']} (${transaction.flightDetails['codeArrival']})",
|
||||||
"${transaction.bandaraDetails['departure']?['name']}",
|
planeArrival: "${transaction.bandaraDetails['arrival']?['name']}",
|
||||||
color: GrayColors.gray600,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
maxlines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
SizedBox(height: 58.h),
|
|
||||||
TypographyStyles.caption(
|
|
||||||
"${transaction.flightDetails['cityArrival']} (${transaction.flightDetails['codeArrival']})",
|
|
||||||
color: GrayColors.gray800,
|
|
||||||
),
|
|
||||||
TypographyStyles.caption(
|
|
||||||
"${transaction.bandaraDetails['arrival']?['name']}",
|
|
||||||
color: GrayColors.gray600,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
maxlines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -313,6 +297,51 @@ class _DetailTicketScreenState extends State<DetailTicketScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildDataBandara({
|
||||||
|
required String titleDeparture,
|
||||||
|
required String planeDeparture,
|
||||||
|
required String titleTransit,
|
||||||
|
required String planeTransit,
|
||||||
|
required String titleArrival,
|
||||||
|
required String planeArrival,
|
||||||
|
}) {
|
||||||
|
return Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
TypographyStyles.caption(titleDeparture, color: GrayColors.gray800),
|
||||||
|
TypographyStyles.caption(
|
||||||
|
planeDeparture,
|
||||||
|
color: GrayColors.gray600,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
maxlines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
SizedBox(height: 20.h),
|
||||||
|
if (titleTransit.isNotEmpty && planeTransit.isNotEmpty) ...[
|
||||||
|
TypographyStyles.caption(titleTransit, color: GrayColors.gray800),
|
||||||
|
TypographyStyles.caption(
|
||||||
|
planeTransit,
|
||||||
|
color: GrayColors.gray600,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
maxlines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
SizedBox(height: 20.h),
|
||||||
|
],
|
||||||
|
TypographyStyles.caption(titleArrival, color: GrayColors.gray800),
|
||||||
|
TypographyStyles.caption(
|
||||||
|
planeArrival,
|
||||||
|
color: GrayColors.gray600,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
maxlines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _showCetakBoardingPassBottomSheet(TransactionModel transaction) {
|
void _showCetakBoardingPassBottomSheet(TransactionModel transaction) {
|
||||||
Get.bottomSheet(
|
Get.bottomSheet(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
|
|
|
@ -152,6 +152,18 @@ class _PrintBoardingPassScreenState extends State<PrintBoardingPassScreen> {
|
||||||
final passengerMap = (transaction.passengerDetails as List<dynamic>)[passenger] as Map<String, dynamic>;
|
final passengerMap = (transaction.passengerDetails as List<dynamic>)[passenger] as Map<String, dynamic>;
|
||||||
final idBarcode = passengerMap['idBarcode'] ?? '';
|
final idBarcode = passengerMap['idBarcode'] ?? '';
|
||||||
|
|
||||||
|
final Map<String, dynamic>? svcMap = transaction.porterServiceDetails;
|
||||||
|
final services = <String>[];
|
||||||
|
if (svcMap?['departure'] != null) {
|
||||||
|
services.add(svcMap!['departure']['name'] as String);
|
||||||
|
}
|
||||||
|
if (svcMap?['transit'] != null) {
|
||||||
|
services.add(svcMap!['transit']['name'] as String);
|
||||||
|
}
|
||||||
|
if (svcMap?['arrival'] != null) {
|
||||||
|
services.add(svcMap!['arrival']['name'] as String);
|
||||||
|
}
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 20.h),
|
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 20.h),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
|
@ -192,15 +204,15 @@ class _PrintBoardingPassScreenState extends State<PrintBoardingPassScreen> {
|
||||||
padding: EdgeInsets.symmetric(vertical: 10),
|
padding: EdgeInsets.symmetric(vertical: 10),
|
||||||
child: Divider(thickness: 1, color: GrayColors.gray200),
|
child: Divider(thickness: 1, color: GrayColors.gray200),
|
||||||
),
|
),
|
||||||
Row(
|
Padding(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
padding: EdgeInsets.symmetric(vertical: 10.h),
|
||||||
children: [
|
child: layananClassGateSeat(
|
||||||
_buildColumnText(context, label: 'Layanan', value: 'value'),
|
context: context,
|
||||||
_buildColumnText(context,
|
services: services,
|
||||||
label: 'Class', value: transaction.flightDetails['flightClass']),
|
flightClass: transaction.flightDetails['flightClass'],
|
||||||
_buildColumnText(context, label: 'Gate', value: 'value'),
|
gate: 'Gate',
|
||||||
_buildColumnText(context, label: 'Seat', value: seatNumber),
|
seatNumber: seatNumber,
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 20.h),
|
padding: EdgeInsets.symmetric(vertical: 20.h),
|
||||||
|
@ -227,6 +239,53 @@ class _PrintBoardingPassScreenState extends State<PrintBoardingPassScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget layananClassGateSeat({
|
||||||
|
required BuildContext context,
|
||||||
|
required List<String> services,
|
||||||
|
required String flightClass,
|
||||||
|
required String gate,
|
||||||
|
required String seatNumber,
|
||||||
|
}) {
|
||||||
|
if (services.isEmpty) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
_buildColumnText(context, label: 'Layanan', value: '-'),
|
||||||
|
_buildColumnText(context, label: 'Class', value: flightClass),
|
||||||
|
_buildColumnText(context, label: 'Gate', value: gate),
|
||||||
|
_buildColumnText(context, label: 'Seat', value: seatNumber),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (services.length == 1) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
_buildColumnText(context, label: 'Layanan', value: services.first),
|
||||||
|
_buildColumnText(context, label: 'Class', value: flightClass),
|
||||||
|
_buildColumnText(context, label: 'Gate', value: gate),
|
||||||
|
_buildColumnText(context, label: 'Seat', value: seatNumber),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final allServices = services.join(', ');
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildColumnText(context, label: 'Layanan', value: allServices),
|
||||||
|
SizedBox(height: 8.h),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
_buildColumnText(context, label: 'Class', value: flightClass),
|
||||||
|
_buildColumnText(context, label: 'Gate', value: gate),
|
||||||
|
_buildColumnText(context, label: 'Seat', value: seatNumber),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildColumnText(
|
Widget _buildColumnText(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required String label,
|
required String label,
|
||||||
|
|
|
@ -92,7 +92,7 @@ class CardFlightInformation extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 4.h),
|
SizedBox(height: 4.h),
|
||||||
Row(
|
Wrap(
|
||||||
children: [
|
children: [
|
||||||
TypographyStyles.body(departureCity, color: GrayColors.gray800),
|
TypographyStyles.body(departureCity, color: GrayColors.gray800),
|
||||||
SizedBox(width: 10.w),
|
SizedBox(width: 10.w),
|
||||||
|
|
|
@ -86,10 +86,19 @@ class _SearchTicketsScreenState extends State<SearchTicketsScreen> {
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
);
|
);
|
||||||
} else if (ticketController.ticketFlight.isEmpty) {
|
} else if (ticketController.ticketFlight.isEmpty) {
|
||||||
|
final tanggal = DateFormat('EEE, d MMM yyyy').format(leavingDate);
|
||||||
|
final baseMsg = flightClass.isNotEmpty
|
||||||
|
? 'Maaf, tiket kelas $flightClass dari $from ke $to pada $tanggal tidak ditemukan.'
|
||||||
|
: 'Maaf, tiket dari $from ke $to pada $tanggal tidak ditemukan.';
|
||||||
return Center(
|
return Center(
|
||||||
child: TypographyStyles.body(
|
child: Padding(
|
||||||
"Tiket tidak ditemukan",
|
padding: EdgeInsets.symmetric(horizontal: 24.w),
|
||||||
color: GrayColors.gray600,
|
child: TypographyStyles.caption(
|
||||||
|
baseMsg,
|
||||||
|
color: GrayColors.gray400,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
maxlines: 3,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -59,7 +59,7 @@ class _TicketBookingStep1ScreenState extends State<TicketBookingStep1Screen> {
|
||||||
String? airLines;
|
String? airLines;
|
||||||
String? code;
|
String? code;
|
||||||
String? flightClass;
|
String? flightClass;
|
||||||
String? transitAirplane;
|
String? cityTransit;
|
||||||
String? stop;
|
String? stop;
|
||||||
String? codeDeparture;
|
String? codeDeparture;
|
||||||
String? codeArrival;
|
String? codeArrival;
|
||||||
|
@ -141,7 +141,7 @@ class _TicketBookingStep1ScreenState extends State<TicketBookingStep1Screen> {
|
||||||
airLines = flight.airLines;
|
airLines = flight.airLines;
|
||||||
code = flight.code;
|
code = flight.code;
|
||||||
flightClass = flight.flightClass;
|
flightClass = flight.flightClass;
|
||||||
transitAirplane = flight.transitAirplane;
|
cityTransit = flight.cityTransit;
|
||||||
stop = flight.stop;
|
stop = flight.stop;
|
||||||
codeDeparture = flight.codeDeparture;
|
codeDeparture = flight.codeDeparture;
|
||||||
codeArrival = flight.codeArrival;
|
codeArrival = flight.codeArrival;
|
||||||
|
@ -162,7 +162,7 @@ class _TicketBookingStep1ScreenState extends State<TicketBookingStep1Screen> {
|
||||||
plane: '${airLines} (${code})',
|
plane: '${airLines} (${code})',
|
||||||
seatClass: '$flightClass',
|
seatClass: '$flightClass',
|
||||||
passenger: '$passenger',
|
passenger: '$passenger',
|
||||||
transiAirplane: '$transitAirplane',
|
transiAirplane: '$cityTransit',
|
||||||
stop: '$stop',
|
stop: '$stop',
|
||||||
airlineLogo: airlineLogo,
|
airlineLogo: airlineLogo,
|
||||||
),
|
),
|
||||||
|
@ -188,7 +188,6 @@ class _TicketBookingStep1ScreenState extends State<TicketBookingStep1Screen> {
|
||||||
textColor: Colors.white,
|
textColor: Colors.white,
|
||||||
backgroundColor: isAllPassengersFilled() ? PrimaryColors.primary800 : GrayColors.gray400,
|
backgroundColor: isAllPassengersFilled() ? PrimaryColors.primary800 : GrayColors.gray400,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// logger.d('Selected Passengers: $selectedPassengers');
|
|
||||||
if (selectedPassengers.any((p) => p == null)) {
|
if (selectedPassengers.any((p) => p == null)) {
|
||||||
SnackbarHelper.showError('Error', 'Harap lengkapi slot penumpang');
|
SnackbarHelper.showError('Error', 'Harap lengkapi slot penumpang');
|
||||||
} else {
|
} else {
|
||||||
|
@ -205,7 +204,7 @@ class _TicketBookingStep1ScreenState extends State<TicketBookingStep1Screen> {
|
||||||
'airLines': airLines,
|
'airLines': airLines,
|
||||||
'code': code,
|
'code': code,
|
||||||
'flightClass': flightClass,
|
'flightClass': flightClass,
|
||||||
'transitAirplane': transitAirplane,
|
'transitAirplane': cityTransit,
|
||||||
'stop': stop,
|
'stop': stop,
|
||||||
'codeDeparture': codeDeparture,
|
'codeDeparture': codeDeparture,
|
||||||
'codeArrival': codeArrival,
|
'codeArrival': codeArrival,
|
||||||
|
@ -232,7 +231,6 @@ class _TicketBookingStep1ScreenState extends State<TicketBookingStep1Screen> {
|
||||||
} else if (snapshot.hasData && snapshot.data != null) {
|
} else if (snapshot.hasData && snapshot.data != null) {
|
||||||
final user = snapshot.data!;
|
final user = snapshot.data!;
|
||||||
_loggedUser = user;
|
_loggedUser = user;
|
||||||
// logger.d('Data user: ${user.noId}');
|
|
||||||
return CustomeShadowCotainner(
|
return CustomeShadowCotainner(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -392,7 +390,6 @@ class _TicketBookingStep1ScreenState extends State<TicketBookingStep1Screen> {
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final passenger = profilController.passengerList[index];
|
final passenger = profilController.passengerList[index];
|
||||||
// logger.d("Passenger Models : ${passenger.noId}");
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.only(top: 16.h),
|
padding: EdgeInsets.only(top: 16.h),
|
||||||
child: _buildAddPassenger(
|
child: _buildAddPassenger(
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:e_porter/_core/component/card/custome_shadow_cotainner.dart';
|
import 'package:e_porter/_core/component/card/custome_shadow_cotainner.dart';
|
||||||
import 'package:e_porter/_core/component/icons/icons_library.dart';
|
import 'package:e_porter/_core/component/icons/icons_library.dart';
|
||||||
import 'package:e_porter/_core/constants/colors.dart';
|
import 'package:e_porter/_core/constants/colors.dart';
|
||||||
|
@ -218,7 +216,7 @@ class _TicketBookingStep3ScreenState extends State<TicketBookingStep3Screen> {
|
||||||
seatClass: "${flightData?.flightClass}",
|
seatClass: "${flightData?.flightClass}",
|
||||||
passenger: "$passenger",
|
passenger: "$passenger",
|
||||||
stop: "${flightData?.stop}",
|
stop: "${flightData?.stop}",
|
||||||
transiAirplane: "${flightData?.transitAirplane}",
|
transiAirplane: "${flightData?.cityTransit}",
|
||||||
airlineLogo: "${flightData?.airlineLogo}",
|
airlineLogo: "${flightData?.airlineLogo}",
|
||||||
),
|
),
|
||||||
SizedBox(height: 32.h),
|
SizedBox(height: 32.h),
|
||||||
|
@ -309,13 +307,6 @@ class _TicketBookingStep3ScreenState extends State<TicketBookingStep3Screen> {
|
||||||
'selectedServiceLabels': selectedServiceLabels,
|
'selectedServiceLabels': selectedServiceLabels,
|
||||||
'selectedPorterServices': selectedPorterServices,
|
'selectedPorterServices': selectedPorterServices,
|
||||||
};
|
};
|
||||||
log('[Ticket Booking Step3] From ID: $fromId');
|
|
||||||
log('[Ticket Booking Step3] Ticket ID: $ticketId');
|
|
||||||
log('[Ticket Booking Step3] Flight ID: $flightId');
|
|
||||||
log('[Ticket Booking Step3] Ticket Date: $ticketDate');
|
|
||||||
log('[Ticket Booking Step3] Opsi Penerbangan: $selectedServiceLabels');
|
|
||||||
log('[Ticket Booking Step3] Layanan Porter: $selectedPorterServices');
|
|
||||||
|
|
||||||
Get.toNamed(Routes.TICKETBOOKINGSTEP4, arguments: argument);
|
Get.toNamed(Routes.TICKETBOOKINGSTEP4, arguments: argument);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -175,7 +175,7 @@ class _TicketBookingStep4ScreenState extends State<TicketBookingStep4Screen> {
|
||||||
plane: "${flightData?.airLines} (${flightData?.code})",
|
plane: "${flightData?.airLines} (${flightData?.code})",
|
||||||
seatClass: "${flightData?.flightClass}",
|
seatClass: "${flightData?.flightClass}",
|
||||||
passenger: "$passenger",
|
passenger: "$passenger",
|
||||||
transiAirplane: "${flightData?.transitAirplane}",
|
transiAirplane: "${flightData?.cityTransit}",
|
||||||
departurePorter: hasDeparturePorter ? "Keberangkatan (${getPorterInfo('departure')})" : null,
|
departurePorter: hasDeparturePorter ? "Keberangkatan (${getPorterInfo('departure')})" : null,
|
||||||
arrivalPorter: hasArrivalPorter ? "Kedatangan (${getPorterInfo('arrival')})" : null,
|
arrivalPorter: hasArrivalPorter ? "Kedatangan (${getPorterInfo('arrival')})" : null,
|
||||||
transitPorter: hasTransitPorter ? "Transit (${getPorterInfo('transit')})" : null,
|
transitPorter: hasTransitPorter ? "Transit (${getPorterInfo('transit')})" : null,
|
||||||
|
@ -304,6 +304,10 @@ class _TicketBookingStep4ScreenState extends State<TicketBookingStep4Screen> {
|
||||||
'departureTime': flightData?.departureTime.millisecondsSinceEpoch,
|
'departureTime': flightData?.departureTime.millisecondsSinceEpoch,
|
||||||
'arrivalTime': flightData?.arrivalTime.millisecondsSinceEpoch,
|
'arrivalTime': flightData?.arrivalTime.millisecondsSinceEpoch,
|
||||||
'flightClass': flightData?.flightClass,
|
'flightClass': flightData?.flightClass,
|
||||||
|
'cityTransit': flightData?.cityTransit,
|
||||||
|
'codeTransit': flightData?.codeTransit,
|
||||||
|
'startDateTransit': flightData?.startDateTransit?.millisecondsSinceEpoch,
|
||||||
|
'endDateTransit': flightData?.endDateTransit?.millisecondsSinceEpoch,
|
||||||
'transitAirplane': flightData?.transitAirplane,
|
'transitAirplane': flightData?.transitAirplane,
|
||||||
'stop': flightData?.stop,
|
'stop': flightData?.stop,
|
||||||
'airlineLogo': flightData?.airlineLogo,
|
'airlineLogo': flightData?.airlineLogo,
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:e_porter/_core/component/button/button_outline.dart';
|
||||||
import 'package:e_porter/_core/constants/colors.dart';
|
import 'package:e_porter/_core/constants/colors.dart';
|
||||||
import 'package:e_porter/_core/constants/typography.dart';
|
import 'package:e_porter/_core/constants/typography.dart';
|
||||||
import 'package:e_porter/_core/service/permission_service.dart';
|
import 'package:e_porter/_core/service/permission_service.dart';
|
||||||
|
import 'package:e_porter/domain/bindings/navigation_binding.dart';
|
||||||
import 'package:e_porter/presentation/screens/navigation/main_navigation.dart';
|
import 'package:e_porter/presentation/screens/navigation/main_navigation.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -110,6 +111,7 @@ class _UploadFileScreenState extends State<UploadFileScreen> {
|
||||||
if (isHistoryMode) {
|
if (isHistoryMode) {
|
||||||
Get.offAll(
|
Get.offAll(
|
||||||
() => MainNavigation(initialTabIndex: 1),
|
() => MainNavigation(initialTabIndex: 1),
|
||||||
|
binding: MainNavigationBinding(),
|
||||||
arguments: 'penumpang',
|
arguments: 'penumpang',
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -441,7 +441,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.1"
|
||||||
logger:
|
logger:
|
||||||
dependency: "direct main"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: logger
|
name: logger
|
||||||
sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1
|
sha256: be4b23575aac7ebf01f225a241eb7f6b5641eeaf43c6a8613510fc2f8cf187d1
|
||||||
|
|
|
@ -50,10 +50,9 @@ dependencies:
|
||||||
uuid: ^4.5.1
|
uuid: ^4.5.1
|
||||||
firebase_database: ^11.3.5
|
firebase_database: ^11.3.5
|
||||||
shared_preferences: ^2.4.6
|
shared_preferences: ^2.4.6
|
||||||
logger: ^2.5.0
|
|
||||||
dropdown_button2: ^2.3.9
|
dropdown_button2: ^2.3.9
|
||||||
google_fonts: ^6.2.1
|
google_fonts: ^6.2.1
|
||||||
dotted_dashed_line: ^0.0.3
|
dotted_dashed_line: ^0.0.3
|
||||||
barcode_widget: ^2.0.4
|
barcode_widget: ^2.0.4
|
||||||
file_picker: ^10.0.0
|
file_picker: ^10.0.0
|
||||||
path: ^1.9.0
|
path: ^1.9.0
|
||||||
|
@ -62,14 +61,15 @@ dependencies:
|
||||||
mobile_scanner: ^6.0.10
|
mobile_scanner: ^6.0.10
|
||||||
pdf: ^3.11.3
|
pdf: ^3.11.3
|
||||||
printing: ^5.14.2
|
printing: ^5.14.2
|
||||||
# workmanager: ^0.5.2
|
|
||||||
|
|
||||||
|
# workmanager: ^0.5.2
|
||||||
# pin_code_fields: ^8.0.1
|
# pin_code_fields: ^8.0.1
|
||||||
# dio: ^5.8.0+1
|
# dio: ^5.8.0+1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
logger: ^2.5.0
|
||||||
|
|
||||||
# The "flutter_lints" package below contains a set of recommended lints to
|
# The "flutter_lints" package below contains a set of recommended lints to
|
||||||
# encourage good coding practices. The lint set provided by the package is
|
# encourage good coding practices. The lint set provided by the package is
|
||||||
|
|
Loading…
Reference in New Issue