Feat: Lacks overall total functionality

This commit is contained in:
orangdeso 2025-03-27 23:39:11 +07:00
parent 491ba3b55f
commit 4e3d4b5d33
8 changed files with 252 additions and 59 deletions

View File

@ -1,6 +1,4 @@
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:e_porter/_core/service/logger_service.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';
@ -21,8 +19,7 @@ class TicketRepositoryImpl implements TicketRepository {
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( // logger.d("Fetching tickets with parameters: from = $from, to = $to, leavingDate between = ${Timestamp.fromDate(startOfDay)} and ${Timestamp.fromDate(endOfDay)}");
"Fetching tickets with parameters: from = $from, to = $to, leavingDate between = ${Timestamp.fromDate(startOfDay)} and ${Timestamp.fromDate(endOfDay)}");
final snapshot = await collection final snapshot = await collection
.where('from', isEqualTo: from) .where('from', isEqualTo: from)
@ -31,9 +28,9 @@ class TicketRepositoryImpl implements TicketRepository {
.where('leavingDate', isLessThan: Timestamp.fromDate(endOfDay)) .where('leavingDate', isLessThan: Timestamp.fromDate(endOfDay))
.get(); .get();
logger.d("Number of tickets found: ${snapshot.docs.length}"); // logger.d("Number of tickets found: ${snapshot.docs.length}");
snapshot.docs.forEach((doc) { snapshot.docs.forEach((doc) {
logger.d("Doc ID: ${doc.id} => ${doc.data()}"); // 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();
@ -52,9 +49,9 @@ 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}"); // 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()}"); // 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();
@ -72,7 +69,7 @@ class TicketRepositoryImpl implements TicketRepository {
.doc(flightId) .doc(flightId)
.get(); .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);
} }

View File

@ -18,6 +18,9 @@ class CardFlightInformation extends StatelessWidget {
final String passenger; final String passenger;
final String? transiAirplane; final String? transiAirplane;
final String? stop; final String? stop;
final String? departurePorter;
final String? arrivalPorter;
final String? transitPorter;
const CardFlightInformation({ const CardFlightInformation({
Key? key, Key? key,
@ -31,6 +34,9 @@ class CardFlightInformation extends StatelessWidget {
required this.passenger, required this.passenger,
this.transiAirplane, this.transiAirplane,
this.stop, this.stop,
this.departurePorter,
this.arrivalPorter,
this.transitPorter,
}); });
@override @override
@ -85,12 +91,23 @@ class CardFlightInformation extends StatelessWidget {
servicePorter != null ? _buildText(context, text: servicePorter!) : SizedBox.shrink(), servicePorter != null ? _buildText(context, text: servicePorter!) : SizedBox.shrink(),
], ],
), ),
if (departurePorter != null && departurePorter!.isNotEmpty) ... [
SizedBox(height: 4.h),
TypographyStyles.small(departurePorter!, color: GrayColors.gray600, fontWeight: FontWeight.w400),
],
if (arrivalPorter != null && arrivalPorter!.isNotEmpty) ... [
SizedBox(height: 4.h),
TypographyStyles.small(arrivalPorter!, color: GrayColors.gray600, fontWeight: FontWeight.w400),
],
if (transitPorter != null && transitPorter!.isNotEmpty) ... [
SizedBox(height: 4.h),
TypographyStyles.small(transitPorter!, color: GrayColors.gray600, fontWeight: FontWeight.w400),
],
SizedBox(height: 4.h), SizedBox(height: 4.h),
TypographyStyles.small( TypographyStyles.small(
'$passenger Dewasa', '$passenger Dewasa',
color: GrayColors.gray600, color: GrayColors.gray600,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
letterSpacing: 0.2,
) )
], ],
), ),

View File

@ -1,5 +1,7 @@
// ignore_for_file: unnecessary_null_comparison // ignore_for_file: unnecessary_null_comparison
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/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';
@ -253,7 +255,7 @@ class _ChooseSeatScreenState extends State<ChooseSeatScreen> {
if (selectedSeatNumbers.any((seat) => seat.isEmpty)) { if (selectedSeatNumbers.any((seat) => seat.isEmpty)) {
return; return;
} }
logger.d('Kursi: $selectedSeatNumbers'); log('Nomor Kursi: $selectedSeatNumbers');
Get.back(result: selectedSeatNumbers); Get.back(result: selectedSeatNumbers);
}, },
), ),

View File

@ -1,5 +1,4 @@
// ignore_for_file: unnecessary_null_comparison // ignore_for_file: unnecessary_null_comparison
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/constants/typography.dart'; import 'package:e_porter/_core/constants/typography.dart';
@ -9,8 +8,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import '../../../../_core/service/logger_service.dart';
import '../../../controllers/ticket_controller.dart'; import '../../../controllers/ticket_controller.dart';
class SearchTicketsScreen extends StatefulWidget { class SearchTicketsScreen extends StatefulWidget {
@ -141,8 +138,7 @@ class _SearchTicketsScreenState extends State<SearchTicketsScreen> {
"ticketDate": ticketDate, "ticketDate": ticketDate,
"passenger": passengerCount, "passenger": passengerCount,
}; };
logger.d( // logger.d('ID Ticket: $ticketId \nID Flight: $flightId \nTicket Date: $ticketDate \nPassenger: $passengerCount');
'ID Ticket: $ticketId \nID Flight: $flightId \nTicket Date: $ticketDate \nPassenger: $passengerCount');
Get.toNamed(Routes.TICKETBOOKINGSTEP1, arguments: argument); Get.toNamed(Routes.TICKETBOOKINGSTEP1, arguments: argument);
}, },
), ),

View File

@ -7,7 +7,6 @@ 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';
import 'package:e_porter/_core/constants/typography.dart'; import 'package:e_porter/_core/constants/typography.dart';
import 'package:e_porter/_core/service/logger_service.dart';
import 'package:e_porter/_core/service/preferences_service.dart'; import 'package:e_porter/_core/service/preferences_service.dart';
import 'package:e_porter/_core/utils/snackbar/snackbar_helper.dart'; import 'package:e_porter/_core/utils/snackbar/snackbar_helper.dart';
import 'package:e_porter/domain/models/ticket_model.dart'; import 'package:e_porter/domain/models/ticket_model.dart';
@ -90,7 +89,7 @@ class _TicketBookingStep1ScreenState extends State<TicketBookingStep1Screen> {
} }
final userId = userData.uid; final userId = userData.uid;
await profilController.fetchPassangerById(userId); await profilController.fetchPassangerById(userId);
logger.d('User ID: $userId'); // logger.d('User ID: $userId');
} }
bool isAllPassengersFilled() { bool isAllPassengersFilled() {
@ -176,7 +175,7 @@ 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'); // 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 {
@ -216,7 +215,7 @@ 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}'); // logger.d('Data user: ${user.noId}');
return CustomeShadowCotainner( return CustomeShadowCotainner(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -376,7 +375,7 @@ 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}"); // 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(
@ -574,7 +573,7 @@ class _TicketBookingStep1ScreenState extends State<TicketBookingStep1Screen> {
itemBuilder: (context, pIndex) { itemBuilder: (context, pIndex) {
final passenger = profilController.passengerList[pIndex]; final passenger = profilController.passengerList[pIndex];
final isUsed = usedNoIds.contains(passenger.noId); final isUsed = usedNoIds.contains(passenger.noId);
logger.d("Passenger Models : ${passenger.noId}"); // 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(

View File

@ -1,7 +1,6 @@
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/component/card/custome_shadow_cotainner.dart'; import 'package:e_porter/_core/component/card/custome_shadow_cotainner.dart';
import 'package:e_porter/_core/constants/colors.dart'; import 'package:e_porter/_core/constants/colors.dart';
import 'package:e_porter/_core/service/logger_service.dart';
import 'package:e_porter/domain/models/user_entity.dart'; import 'package:e_porter/domain/models/user_entity.dart';
import 'package:e_porter/presentation/controllers/ticket_controller.dart'; import 'package:e_porter/presentation/controllers/ticket_controller.dart';
import 'package:e_porter/presentation/screens/routes/app_rountes.dart'; import 'package:e_porter/presentation/screens/routes/app_rountes.dart';
@ -63,7 +62,7 @@ class _TicketBookingStep2ScreenState extends State<TicketBookingStep2Screen> {
selectedPassengers = args['selectedPassenger'] ?? []; selectedPassengers = args['selectedPassenger'] ?? [];
selectedSeatNumbers = args['selectedSeatNumbers'] ?? List.filled(passenger, ''); selectedSeatNumbers = args['selectedSeatNumbers'] ?? List.filled(passenger, '');
logger.d('Ticket ID: $ticketId \nFlight ID: $flightId'); // logger.d('Ticket ID: $ticketId \nFlight ID: $flightId');
} }
@override @override
@ -152,7 +151,7 @@ class _TicketBookingStep2ScreenState extends State<TicketBookingStep2Screen> {
'selectedPassenger': selectedPassengers, 'selectedPassenger': selectedPassengers,
'numberSeat': selectedSeatNumbers 'numberSeat': selectedSeatNumbers
}; };
logger.d('Number Seat: $selectedSeatNumbers \n Passenger: $selectedPassengers'); // logger.d('Number Seat: $selectedSeatNumbers \n Passenger: $selectedPassengers');
Get.toNamed(Routes.TICKETBOOKINGSTEP3, arguments: argument); Get.toNamed(Routes.TICKETBOOKINGSTEP3, arguments: argument);
}, },
), ),

View File

@ -1,3 +1,5 @@
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';
@ -49,6 +51,7 @@ class _TicketBookingStep3ScreenState extends State<TicketBookingStep3Screen> {
String? departureTime; String? departureTime;
String? arrivalTime; String? arrivalTime;
double totalPrice = 0.0;
double totalPriceService = 0.0; double totalPriceService = 0.0;
PorterServiceModel? selectedDepartureService; PorterServiceModel? selectedDepartureService;
PorterServiceModel? selectedArrivalService; PorterServiceModel? selectedArrivalService;
@ -190,7 +193,7 @@ class _TicketBookingStep3ScreenState extends State<TicketBookingStep3Screen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
double totalPrice = calculateTotalPrice(flightData?.price.toDouble() ?? 0.0, passenger); totalPrice = calculateTotalPrice(flightData?.price.toDouble() ?? 0.0, passenger);
double grandTotal = totalPrice + totalPriceService; double grandTotal = totalPrice + totalPriceService;
return Scaffold( return Scaffold(
@ -295,16 +298,37 @@ class _TicketBookingStep3ScreenState extends State<TicketBookingStep3Screen> {
selectedPorterServices['transit'] = selectedTransitService; selectedPorterServices['transit'] = selectedTransitService;
} }
final argument = { final argument = {
'tickedId': ticketId, 'ticketId': ticketId,
'flightId': flightId, 'flightId': flightId,
'ticketDate': ticketDate, 'date': ticketDate,
'passenger': passenger, 'passenger': passenger,
'selectedPassenger': selectedPassengers, 'selectedPassenger': selectedPassengers,
'numberSeat': numberSeat, 'numberSeat': numberSeat,
'totalPrice': totalPrice,
'grandTotal': grandTotal, 'grandTotal': grandTotal,
'selectedServiceLabels': selectedServiceLabels, 'selectedServiceLabels': selectedServiceLabels,
'selectedPorter': selectedPorterServices, 'selectedPorterServices': selectedPorterServices,
}; };
log('Ticket ID: $ticketId');
log('Flight ID: $flightId');
log('Ticket Date: $ticketDate');
log('Opsi Penerbangan: $selectedServiceLabels');
log('Layanan Porter: $selectedPorterServices');
// final Map<String, dynamic> debugPorterServices = {};
// selectedPorterServices.forEach((key, value) {
// if (value != null) {
// debugPorterServices[key] = {
// 'id': value.id,
// 'name': value.name,
// 'price': value.price,
// };
// } else {
// debugPorterServices[key] = null;
// }
// });
// log('Layanan Porter: $debugPorterServices');
Get.toNamed(Routes.TICKETBOOKINGSTEP4, arguments: argument); Get.toNamed(Routes.TICKETBOOKINGSTEP4, arguments: argument);
}, },
), ),

View File

@ -10,8 +10,11 @@ import 'package:get/get.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import '../../../../_core/component/appbar/appbar_component.dart'; import '../../../../_core/component/appbar/appbar_component.dart';
import '../../../../_core/component/icons/icons_library.dart'; import '../../../../_core/component/icons/icons_library.dart';
import '../../../../_core/service/logger_service.dart';
import '../../../../domain/models/porter_service_model.dart'; import '../../../../domain/models/porter_service_model.dart';
import '../../../../domain/models/ticket_model.dart';
import '../../../../domain/models/user_entity.dart'; import '../../../../domain/models/user_entity.dart';
import '../../../controllers/ticket_controller.dart';
import '../component/card_flight_information.dart'; import '../component/card_flight_information.dart';
class TicketBookingStep4Screen extends StatefulWidget { class TicketBookingStep4Screen extends StatefulWidget {
@ -28,27 +31,91 @@ class _TicketBookingStep4ScreenState extends State<TicketBookingStep4Screen> {
late final int passenger; late final int passenger;
late final List<PassengerModel?> selectedPassengers; late final List<PassengerModel?> selectedPassengers;
late List<String> numberSeat; late List<String> numberSeat;
late double? grandTotal;
late List<String> selectedServiceLabels; late List<String> selectedServiceLabels;
late Map<String, PorterServiceModel?> selectedPorterServices; late Map<String, PorterServiceModel?> selectedPorterServices;
double? totalPrice;
double? grandTotal;
final TicketController ticketController = Get.find<TicketController>();
FlightModel? flightData;
String? departureTime;
String? arrivalTime;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
final args = Get.arguments as Map<String, dynamic>; final args = Get.arguments as Map<String, dynamic>;
ticketId = args['ticketId'] ?? ''; ticketId = args['ticketId'] ?? '';
flightId = args['flightId'] ?? ''; flightId = args['flightId'] ?? '';
ticketDate = args['date'] ?? ''; ticketDate = args['date'];
passenger = args['passenger'] ?? 0; passenger = args['passenger'] ?? 0;
selectedPassengers = args['selectedPassenger'] ?? []; selectedPassengers = args['selectedPassenger'] ?? [];
numberSeat = args['numberSeat'] ?? ''; numberSeat = args['numberSeat'] ?? '';
grandTotal = args['grandTotal'] ?? 0; totalPrice = args['totalPrice'] ?? 0.0;
grandTotal = args['grandTotal'] ?? 0.0;
selectedServiceLabels = args['selectedServiceLabels'] ?? []; selectedServiceLabels = args['selectedServiceLabels'] ?? [];
selectedPorterServices = args['selectedPorterServices'] ?? {}; selectedPorterServices = args['selectedPorterServices'] ?? {};
// log('Ticket ID Step 4: $ticketId');
// log('Opsi Penerbangan Step 4: $selectedServiceLabels');
// if (selectedServiceLabels.isNotEmpty) {
// log('Opsi Penerbangan Step 4: ${selectedServiceLabels.join(", ")}');
// } else {
// log('Tidak ada opsi penerbangan yang dipilih');
// }
// selectedPorterServices.forEach((key, value) {
// if (value != null) {
// log('Porter $key: ${value.name} - Rp ${value.price}');
// }
// });
fetchDataFlight();
}
Future<void> fetchDataFlight() async {
try {
FlightModel flight = await ticketController.getFlightById(ticketId: ticketId, flightId: flightId);
setState(() {
flightData = flight;
departureTime = DateFormat.jm().format(flightData!.departureTime);
arrivalTime = DateFormat.jm().format(flightData!.arrivalTime);
});
} catch (e) {
logger.e('Terjadi kesalahan: $e');
}
}
String getPorterInfo(String type) {
final porter = selectedPorterServices[type];
if (porter != null) {
return '${porter.name}';
}
return '';
}
String getPorterPrice(String type) {
final porter = selectedPorterServices[type];
if (porter != null) {
try {
return NumberFormat.decimalPattern('id_ID').format(porter.price);
} catch (e) {
print("Error formatting porter price: $e");
return '0';
}
}
return '0';
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final hasDeparturePorter =
selectedPorterServices.containsKey('departure') && selectedPorterServices['departure'] != null;
final hasArrivalPorter = selectedPorterServices.containsKey('arrival') && selectedPorterServices['arrival'] != null;
final hasTransitPorter = selectedPorterServices.containsKey('transit') && selectedPorterServices['transit'] != null;
return Scaffold( return Scaffold(
backgroundColor: GrayColors.gray50, backgroundColor: GrayColors.gray50,
appBar: ProgressAppbarComponent( appBar: ProgressAppbarComponent(
@ -68,14 +135,18 @@ class _TicketBookingStep4ScreenState extends State<TicketBookingStep4Screen> {
Padding( Padding(
padding: EdgeInsets.symmetric(horizontal: 16.w), padding: EdgeInsets.symmetric(horizontal: 16.w),
child: CardFlightInformation( child: CardFlightInformation(
date: "Sen, 27 Jan 2025", date: "$ticketDate",
time: "12.20 - 06.00 AM", time: "$departureTime - $arrivalTime",
departureCity: "Yogyakarta", departureCity: "${flightData?.cityDeparture}",
arrivalCity: "Lombok", arrivalCity: "${flightData?.cityArrival}",
plane: "Citilink (103)", plane: "${flightData?.airLines} (${flightData?.code})",
seatClass: "Economy", seatClass: "${flightData?.flightClass}",
servicePorter: "Fast Track (FT)", passenger: "$passenger",
passenger: "2", transiAirplane: "${flightData?.transitAirplane}",
departurePorter: hasDeparturePorter ? "Keberangkatan (${getPorterInfo('departure')})" : null,
arrivalPorter: hasArrivalPorter ? "Kedatangan (${getPorterInfo('arrival')})" : null,
transitPorter: hasTransitPorter ? "Transit (${getPorterInfo('transit')})" : null,
stop: "${flightData?.stop}",
), ),
), ),
SizedBox(height: 32.h), SizedBox(height: 32.h),
@ -86,13 +157,13 @@ class _TicketBookingStep4ScreenState extends State<TicketBookingStep4Screen> {
SizedBox(height: 20.h), SizedBox(height: 20.h),
CustomeShadowCotainner( CustomeShadowCotainner(
sizeRadius: 0.r, sizeRadius: 0.r,
height: MediaQuery.of(context).size.height * 0.5, height: MediaQuery.of(context).size.height * 0.4,
child: Column( child: Column(
children: [ children: [
Row( Row(
children: [ children: [
TypographyStyles.body( TypographyStyles.body(
"Citilink (103)", "${flightData?.airLines} (${flightData?.code})",
color: GrayColors.gray800, color: GrayColors.gray800,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
@ -101,30 +172,23 @@ class _TicketBookingStep4ScreenState extends State<TicketBookingStep4Screen> {
], ],
), ),
SizedBox(height: 2.h), SizedBox(height: 2.h),
Row( _buildRowPorterWithClass(
children: [ detailDeparturePorter: hasDeparturePorter ? "${getPorterInfo('departure')}" : null,
TypographyStyles.small( detailArrivalPorter: hasArrivalPorter ? "${getPorterInfo('arrival')}" : null,
"Fast Track (FT)", detailTransitPorter: hasTransitPorter ? "${getPorterInfo('transit')}" : null,
color: GrayColors.gray600, detailSeatClass: "${flightData?.flightClass}",
letterSpacing: 0.2,
fontWeight: FontWeight.w400,
),
SizedBox(width: 10.w),
CircleAvatar(radius: 2.r, backgroundColor: Color(0xFFD9D9D9)),
SizedBox(width: 10.w),
TypographyStyles.small("Economy", color: GrayColors.gray600, fontWeight: FontWeight.w400),
],
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Row( Row(
children: [ children: [
TypographyStyles.caption( TypographyStyles.caption(
"Rp 1.200.000", "Rp ${NumberFormat.decimalPattern('id_ID').format(flightData?.price ?? 0.0)}",
color: GrayColors.gray600, color: GrayColors.gray600,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
), ),
SizedBox(width: 8.w), SizedBox(width: 8.w),
TypographyStyles.small("x 2", color: GrayColors.gray600, fontWeight: FontWeight.w400) TypographyStyles.small("x ${passenger}",
color: GrayColors.gray600, fontWeight: FontWeight.w400)
], ],
), ),
Padding( Padding(
@ -140,14 +204,14 @@ class _TicketBookingStep4ScreenState extends State<TicketBookingStep4Screen> {
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
), ),
TypographyStyles.body( TypographyStyles.body(
"Rp 2.400.000", "Rp ${NumberFormat.decimalPattern('id_ID').format(totalPrice ?? 0.0)}",
color: GrayColors.gray600, color: GrayColors.gray600,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
], ],
), ),
SizedBox(height: 2.h), SizedBox(height: 2.h),
_buildRowText(context, text: "Layanan porter (Fast Track)", valueText: "50.000"), _buildTextService(),
SizedBox(height: 2.h), SizedBox(height: 2.h),
_buildRowText(context, text: "Biaya layanan", valueText: "10.000"), _buildRowText(context, text: "Biaya layanan", valueText: "10.000"),
], ],
@ -167,6 +231,84 @@ class _TicketBookingStep4ScreenState extends State<TicketBookingStep4Screen> {
)); ));
} }
Widget _buildRowPorterWithClass({
required String? detailDeparturePorter,
required String? detailArrivalPorter,
required String? detailTransitPorter,
required String detailSeatClass,
}) {
return Row(
children: [
if (detailDeparturePorter != null && detailDeparturePorter.isNotEmpty) ...[
TypographyStyles.small(
detailDeparturePorter,
color: GrayColors.gray600,
fontWeight: FontWeight.w400,
),
SizedBox(width: 10.w),
CircleAvatar(radius: 2.r, backgroundColor: Color(0xFFD9D9D9)),
SizedBox(width: 10.w),
],
if (detailArrivalPorter != null && detailArrivalPorter.isNotEmpty) ...[
TypographyStyles.small(
detailArrivalPorter,
color: GrayColors.gray600,
fontWeight: FontWeight.w400,
),
SizedBox(width: 10.w),
CircleAvatar(radius: 2.r, backgroundColor: Color(0xFFD9D9D9)),
SizedBox(width: 10.h),
],
if (detailTransitPorter != null && detailTransitPorter.isNotEmpty) ...[
TypographyStyles.small(
detailTransitPorter,
color: GrayColors.gray600,
fontWeight: FontWeight.w400,
),
SizedBox(width: 10.w),
CircleAvatar(radius: 2.r, backgroundColor: Color(0xFFD9D9D9)),
SizedBox(width: 10.h),
],
TypographyStyles.small(detailSeatClass, color: GrayColors.gray600, fontWeight: FontWeight.w400),
],
);
}
Widget _buildTextService() {
final hasDeparturePorter =
selectedPorterServices.containsKey('departure') && selectedPorterServices['departure'] != null;
final hasArrivalPorter = selectedPorterServices.containsKey('arrival') && selectedPorterServices['arrival'] != null;
final hasTransitPorter = selectedPorterServices.containsKey('transit') && selectedPorterServices['transit'] != null;
final hasAnyPorter = hasDeparturePorter || hasArrivalPorter || hasTransitPorter;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!hasAnyPorter)
_buildContainerText(left: 0.w, label: 'Layanan Porter', price: '-')
else
_buildContainerText(left: 0.w, label: 'Layanan Porter', price: ''),
if (hasDeparturePorter)
_buildContainerText(
left: 8.w,
label: "Keberangkatan (${getPorterInfo('departure')})",
price: "Rp ${getPorterPrice('departure')}",
),
if (hasArrivalPorter)
_buildContainerText(
left: 8.w,
label: "Kedatangan (${getPorterInfo('arrival')})",
price: "Rp ${getPorterPrice('arrival')}",
),
if (hasTransitPorter)
_buildContainerText(
left: 8.w,
label: "Transit (${getPorterInfo('transit')})",
price: "Rp ${getPorterPrice('transit')}",
),
],
);
}
Widget _buildRowText( Widget _buildRowText(
BuildContext context, { BuildContext context, {
required String text, required String text,
@ -188,4 +330,21 @@ class _TicketBookingStep4ScreenState extends State<TicketBookingStep4Screen> {
], ],
); );
} }
Widget _buildContainerText({
required String label,
required String price,
required double left,
}) {
return Container(
padding: EdgeInsets.only(left: left),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TypographyStyles.caption(label, color: GrayColors.gray600, fontWeight: FontWeight.w400),
TypographyStyles.caption(price, color: GrayColors.gray600, fontWeight: FontWeight.w400),
],
),
);
}
} }