6177214e-ce7c-49e3-99de-ff9721b26f63 — Commit 57c4e60f
Changed files
comwell_key_app/assets/images/master.png | Bin 0 -> 802 bytes comwell_key_app/assets/translations/da-DK.json | 12 +- comwell_key_app/assets/translations/en-US.json | 13 +- .../lib/booking_details/booking_details_page.dart | 17 +- .../booking_details/components/share_button.dart | 14 +- .../lib/check_in/check_in_repository.dart | 1 + .../lib/common/components/bottom_sheet_widget.dart | 4 +- .../lib/contact/cubit/contact_cubit.dart | 2 +- .../components/balance_bottom_sheet.dart | 245 +++++++++++++++++ .../lib/my_booking/cubit/my_booking_cubit.dart | 21 +- .../lib/my_booking/my_booking_page.dart | 290 ++++++++++++--------- .../components/current_bookings_tab_view.dart | 2 +- .../lib/overview/cubit/overview_cubit.dart | 7 +- comwell_key_app/lib/overview/models/booking.dart | 13 +- .../overview/repository/overview_repository.dart | 9 +- .../lib/profile/cubit/profile_cubit.dart | 3 +- .../lib/profile/profile_repository.dart | 15 +- .../components/address_bottom_sheet.dart | 4 +- .../components/date_time_picker.dart | 2 +- comwell_key_app/lib/routing/app_router.dart | 11 +- .../lib/services/mappers/booking_mapper.dart | 4 +- comwell_key_app/lib/utils/share_button_utils.dart | 4 + .../test/overview_test/overview_cubic_test.dart | 18 +- .../overview_test/overview_repository_test.dart | 8 +- 24 files changed, 539 insertions(+), 180 deletions(-)
Diff
diff --git a/comwell_key_app/assets/images/master.png b/comwell_key_app/assets/images/master.png
new file mode 100644
index 00000000..2148a882
Binary files /dev/null and b/comwell_key_app/assets/images/master.png differ
diff --git a/comwell_key_app/assets/translations/da-DK.json b/comwell_key_app/assets/translations/da-DK.json
index 56130ba4..6a096ded 100644
--- a/comwell_key_app/assets/translations/da-DK.json
+++ b/comwell_key_app/assets/translations/da-DK.json
@@ -227,5 +227,15 @@
"newsletter_accept": "Ja tak, jeg vil gerne opdateres på aktuelle medlemstilbud, Comwell Club overraskelser og andre anbefalinger tilpasset mig. Jeg kan til enhver tid afmelde mig igen.",
"read_more": "Læs mere",
"comwell_club_inactive": "Inaktiv",
- "points": "Point"
+ "points": "Point",
+ "my_balance": "Min balance",
+ "cancel_booking": "Annuller ophold",
+ "cancellation_policy": "Annuleringsbetingelser",
+ "cancellation_policy_description": "Dette ophold kan annuleres omkostningsfrit indtil d. 12.11.2024 kl. 15:00 på ankomstdagen. Annulleres senere end d. 14.01 skal del faktureres v/r for den første nat.",
+ "payment_date": "Betalingsdato",
+ "not_shared": "Ikke delt",
+ "share_booking": "Del ophold",
+ "total_charge": "I alt til betaling",
+ "early_checkin": "Tidlig check-in",
+ "payed": "BETALT"
}
\ No newline at end of file
diff --git a/comwell_key_app/assets/translations/en-US.json b/comwell_key_app/assets/translations/en-US.json
index 3d27211f..77d0fa1e 100644
--- a/comwell_key_app/assets/translations/en-US.json
+++ b/comwell_key_app/assets/translations/en-US.json
@@ -227,5 +227,16 @@
"newsletter_accept": "Yes, I would like to receive updates on current member offers, Comwell Club surprises and other recommendations tailored to me. I can unsubscribe at any time.",
"read_more": "Read more",
"comwell_club_inactive": "Inactive",
- "points": "Points"
+ "points": "Points",
+ "my_balance": "My balance",
+ "cancel_booking": "Cancel booking",
+ "cancellation_policy": "Cancellation policy",
+ "cancellation_policy_description": "This booking can be cancelled free of charge until the day of arrival. If cancelled later than 14.01, the first night will be charged.",
+ "payment_date": "Payment date",
+ "not_shared": "Not shared",
+ "share_booking": "Share booking",
+ "total_charge": "I alt til betaling",
+ "early_checkin": "Early check-in",
+ "payed": "PAYED"
+
}
diff --git a/comwell_key_app/lib/booking_details/booking_details_page.dart b/comwell_key_app/lib/booking_details/booking_details_page.dart
index a244217d..55f98239 100644
--- a/comwell_key_app/lib/booking_details/booking_details_page.dart
+++ b/comwell_key_app/lib/booking_details/booking_details_page.dart
@@ -26,8 +26,11 @@ class BookingDetailsPage extends StatelessWidget {
return BlocConsumer<BookingDetailsBloc, BookingDetailsState>(
listener: (context, state) {},
builder: (context, state) {
+ final cubit = context.read<BookingDetailsBloc>();
+ print("Booking: ${cubit.booking}");
if (state.status == BookingDetailsStatus.initial) {
- context.read<BookingDetailsBloc>().add(InitialEvent());
+
+ cubit.add(InitialEvent());
}
return Scaffold(
extendBodyBehindAppBar: true,
@@ -66,7 +69,7 @@ class BookingDetailsPage extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Spacer(),
- _buildBookingDetails(context, state),
+ _buildBookingDetails(context, state, cubit),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Divider(color: Colors.grey[400]),
@@ -144,12 +147,12 @@ class BookingDetailsPage extends StatelessWidget {
);
}
- Widget _buildBookingDetails(BuildContext context, BookingDetailsState state) {
+ Widget _buildBookingDetails(BuildContext context, BookingDetailsState state, BookingDetailsBloc cubit) {
final theme = Theme.of(context);
return InkWell(
onTap: () {
- context.pushNamed(AppRoutes.myBooking.name);
+ context.pushNamed(AppRoutes.myBooking.name, extra: cubit.booking);
},
child: Container(
width: double.infinity,
@@ -172,7 +175,7 @@ class BookingDetailsPage extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
- '20100868-1',
+ cubit.booking.id,
style: theme.textTheme.bodySmall?.copyWith(
color: Colors.white,
fontWeight: FontWeight.w400,
@@ -192,14 +195,14 @@ class BookingDetailsPage extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
- 'Standard Double Room',
+ cubit.booking.roomType,
style: theme.textTheme.bodyMedium?.copyWith(
color: Colors.white,
),
),
const SizedBox(width: 12),
Text(
- '-200 kr.',
+ "${cubit.booking.totalCharge.toString()} kr",
style: theme.textTheme.bodyMedium?.copyWith(
color: Colors.white,
),
diff --git a/comwell_key_app/lib/booking_details/components/share_button.dart b/comwell_key_app/lib/booking_details/components/share_button.dart
index c707828f..9179d3d2 100644
--- a/comwell_key_app/lib/booking_details/components/share_button.dart
+++ b/comwell_key_app/lib/booking_details/components/share_button.dart
@@ -13,15 +13,21 @@ import 'package:go_router/go_router.dart';
class ShareButton extends StatelessWidget {
final Iterable<Guest> guests;
- const ShareButton({super.key, required this.guests});
+ final Color? buttonColor;
+ final double userButtonWidth;
+ final double userButtonOverlap;
+ const ShareButton(
+ {super.key,
+ required this.guests,
+ this.buttonColor,
+ this.userButtonWidth = 50,
+ this.userButtonOverlap = 15});
@override
Widget build(BuildContext context) {
final bloc = context.read<BookingDetailsBloc>();
final booking = bloc.booking;
- const userButtonWidth = 50.0;
- const userButtonOverlap = 15.0;
final numberOfUsers = guests.length;
final allInitials = generateInitials(guests);
@@ -48,7 +54,7 @@ class ShareButton extends StatelessWidget {
extra: booking);
},
style: ElevatedButton.styleFrom(
- backgroundColor: Colors.white,
+ backgroundColor: buttonColor ?? Colors.white,
elevation: 0,
shape: const CircleBorder(),
padding: EdgeInsets.zero,
diff --git a/comwell_key_app/lib/check_in/check_in_repository.dart b/comwell_key_app/lib/check_in/check_in_repository.dart
index d22b616f..8d53da27 100644
--- a/comwell_key_app/lib/check_in/check_in_repository.dart
+++ b/comwell_key_app/lib/check_in/check_in_repository.dart
@@ -59,6 +59,7 @@ class CheckInRepository {
children: 0,
adults: 2,
hotelCode: "hotelCode",
+ totalCharge: 100,
booker: const Guest(name: "Booker", id: "123"),
bookingDate: DateTime.now(),
digitalCard: true,
diff --git a/comwell_key_app/lib/common/components/bottom_sheet_widget.dart b/comwell_key_app/lib/common/components/bottom_sheet_widget.dart
index e0d1605e..2e53608b 100644
--- a/comwell_key_app/lib/common/components/bottom_sheet_widget.dart
+++ b/comwell_key_app/lib/common/components/bottom_sheet_widget.dart
@@ -6,6 +6,7 @@ class BottomSheetWidget extends StatelessWidget {
final double initialExtent;
final double maxExtent;
final double minExtent;
+ final Color? backgroundColor;
final List<Widget> widgetChildren;
@@ -14,13 +15,14 @@ class BottomSheetWidget extends StatelessWidget {
this.initialExtent = 200,
this.maxExtent = 600,
this.minExtent = 100,
+ this.backgroundColor,
super.key,
});
@override
Widget build(BuildContext context) {
return Sheet(
- backgroundColor: sandColor[40],
+ backgroundColor: backgroundColor ?? sandColor[40],
initialExtent: initialExtent,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
diff --git a/comwell_key_app/lib/contact/cubit/contact_cubit.dart b/comwell_key_app/lib/contact/cubit/contact_cubit.dart
index 836e2e7d..9eccc328 100644
--- a/comwell_key_app/lib/contact/cubit/contact_cubit.dart
+++ b/comwell_key_app/lib/contact/cubit/contact_cubit.dart
@@ -13,7 +13,7 @@ class ContactCubit extends Cubit<ContactState> {
void sendContact(String hotelCode) async {
emit(const ContactState.contactSend());
try {
- final bookings = await overviewRepository.fetchAllBookingsForUser('1');
+ final bookings = await overviewRepository.fetchAllBookingsForUser();
// Send contact
contactRepository.sendContact(bookings.current.first.hotelCode);
emit(const ContactState.contactSent());
diff --git a/comwell_key_app/lib/my_booking/components/balance_bottom_sheet.dart b/comwell_key_app/lib/my_booking/components/balance_bottom_sheet.dart
new file mode 100644
index 00000000..b813a834
--- /dev/null
+++ b/comwell_key_app/lib/my_booking/components/balance_bottom_sheet.dart
@@ -0,0 +1,245 @@
+import 'package:comwell_key_app/common/components/bottom_sheet_widget.dart';
+import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:comwell_key_app/utils/share_button_utils.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+
+class BalanceBottomSheet extends StatefulWidget {
+ final num balance;
+ final String userName;
+ final Booking booking;
+ final List<BalanceItem> items;
+
+ const BalanceBottomSheet({
+ super.key,
+ required this.booking,
+ required this.balance,
+ required this.userName,
+ required this.items,
+ });
+
+ @override
+ State<BalanceBottomSheet> createState() => _BalanceBottomSheetState();
+}
+
+class _BalanceBottomSheetState extends State<BalanceBottomSheet> {
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return Container(
+ decoration: const BoxDecoration(
+ color: Colors.white,
+ ),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 16),
+ child: Container(
+ padding:
+ const EdgeInsets.symmetric(vertical: 8),
+ decoration: const BoxDecoration(
+ border: Border(
+ bottom: BorderSide(color: colorDivider),
+ ),
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ const SizedBox(width: 16),
+ Text(
+ 'my_balance'.tr(),
+ style: theme.textTheme.titleLarge?.copyWith(
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ CircleAvatar(
+ radius: 16,
+ backgroundColor: sandColor[40],
+ child: IconButton(
+ padding: EdgeInsets.zero,
+ constraints: const BoxConstraints(),
+ icon: const Icon(Icons.keyboard_arrow_down, size: 20),
+ onPressed: () => Navigator.pop(context),
+ color: Colors.black54,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+
+ // Content
+ Padding(
+ padding: const EdgeInsets.all(10),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // User info
+ Row(
+ children: [
+ CircleAvatar(
+ backgroundColor: sandColor,
+ child: Text(
+ generateInitialsString(widget.userName),
+ style: const TextStyle(color: Colors.white),
+ ),
+ ),
+ const SizedBox(width: 12),
+ Text(
+ widget.userName,
+ style: theme.textTheme.bodyMedium?.copyWith(
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 16),
+ ...widget.items.map((item) => _buildBalanceItem(item, theme)),
+ const SizedBox(height: 24),
+
+ Container(
+ padding: const EdgeInsets.all(16),
+ decoration: BoxDecoration(
+ color: sandColor[10],
+ borderRadius: BorderRadius.circular(4),
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'total_charge'.tr(),
+ style: theme.textTheme.bodyMedium?.copyWith(
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ ),
+ ),
+ Text(
+ '${widget.booking.totalCharge} kr.',
+ style: theme.textTheme.titleLarge
+ ?.copyWith(fontWeight: FontWeight.bold),
+ ),
+ ],
+ ),
+ const SizedBox(width: 12),
+ CircleAvatar(
+ backgroundColor: sandColor[40],
+ child: const Icon(
+ Icons.credit_card,
+ color: Colors.black54,
+ size: 20,
+ ),
+ ),
+ ],
+ ),
+ ),
+
+ const SizedBox(height: 16),
+ ],
+ ),
+ ),
+ ],
+ ),
+
+ // Payment button
+ Column(
+ children: [
+ const Divider(color: colorDivider),
+ Padding(
+ padding: const EdgeInsets.all(16),
+ child: SizedBox(
+ width: double.infinity,
+ child: ElevatedButton(
+ onPressed: () {
+ // Handle payment
+ },
+ style: ElevatedButton.styleFrom(
+ backgroundColor: sandColor,
+ elevation: 0,
+ padding: const EdgeInsets.symmetric(vertical: 16),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(32),
+ ),
+ ),
+ child: Text(
+ 'Gå til betaling ${widget.balance.toInt()} kr.',
+ style: const TextStyle(
+ color: Colors.white,
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+
+ Widget _buildBalanceItem(BalanceItem item, ThemeData theme) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 8),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ item.description,
+ style: TextStyle(
+ fontSize: 16,
+ color: Colors.grey[600],
+ ),
+ ),
+ Row(
+ children: [
+ Text(
+ '${item.amount.toInt()}',
+ style: const TextStyle(
+ fontSize: 16,
+ ),
+ ),
+ if (item.isPaid)
+ Container(
+ margin: const EdgeInsets.only(left: 8),
+ padding: const EdgeInsets.symmetric(
+ horizontal: 6,
+ vertical: 2,
+ ),
+ decoration: BoxDecoration(
+ color: Colors.grey[200],
+ borderRadius: BorderRadius.circular(2),
+ ),
+ child: Text(
+ 'payed'.tr(),
+ style: theme.textTheme.bodySmall
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+class BalanceItem {
+ final String description;
+ final double amount;
+ final bool isPaid;
+
+ const BalanceItem({
+ required this.description,
+ required this.amount,
+ this.isPaid = false,
+ });
+}
diff --git a/comwell_key_app/lib/my_booking/cubit/my_booking_cubit.dart b/comwell_key_app/lib/my_booking/cubit/my_booking_cubit.dart
index cfc5e590..36583860 100644
--- a/comwell_key_app/lib/my_booking/cubit/my_booking_cubit.dart
+++ b/comwell_key_app/lib/my_booking/cubit/my_booking_cubit.dart
@@ -1,21 +1,24 @@
-
import 'package:comwell_key_app/my_booking/cubit/my_booking_state.dart';
import 'package:comwell_key_app/my_booking/my_booking_repository.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
+import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/utils/locator.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class MyBookingCubit extends Cubit<MyBookingState> {
final MyBookingRepository myBookingRepository;
- MyBookingCubit(this.myBookingRepository)
- : super(const MyBookingState(
- booking: null,
+ final ProfileRepository profileRepository = locator<ProfileRepository>();
+ late Booking booking;
+
+ MyBookingCubit(
+ this.myBookingRepository,
+ {required Booking initialBooking})
+ : super(MyBookingState(
+ booking: initialBooking,
isLoading: false,
error: null,
- ));
-
- Future<void> fetchBooking() async {
- final booking = await myBookingRepository.fetchBooking();
-
+ )) {
+ booking = initialBooking;
}
+
}
diff --git a/comwell_key_app/lib/my_booking/my_booking_page.dart b/comwell_key_app/lib/my_booking/my_booking_page.dart
index 0a5b4d37..659c7e8e 100644
--- a/comwell_key_app/lib/my_booking/my_booking_page.dart
+++ b/comwell_key_app/lib/my_booking/my_booking_page.dart
@@ -1,10 +1,19 @@
+import 'package:comwell_key_app/booking_details/bloc/booking_details_bloc.dart';
+import 'package:comwell_key_app/booking_details/booking_details_repository.dart';
+import 'package:comwell_key_app/booking_details/components/share_button.dart';
import 'package:comwell_key_app/common/components/bottom_sheet_widget.dart';
import 'package:comwell_key_app/common/components/comwell_app_bar.dart';
+import 'package:comwell_key_app/my_booking/components/balance_bottom_sheet.dart';
+import 'package:comwell_key_app/overview/models/booking.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:comwell_key_app/utils/locator.dart';
+import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
class MyBookingPage extends StatelessWidget {
- const MyBookingPage({super.key});
+ final Booking booking;
+ const MyBookingPage({super.key, required this.booking});
@override
Widget build(BuildContext context) {
@@ -24,64 +33,85 @@ class MyBookingPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
- 'Min booking',
+ 'my_booking'.tr(),
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
- 'Bookingsreference',
- style: TextStyle(
+ 'booking_reference'.tr(),
+ style: theme.textTheme.bodyMedium?.copyWith(
color: Colors.grey[600],
- fontSize: 14,
),
),
- const Text(
- '201004868-1',
- style: TextStyle(fontSize: 14),
+ Text(
+ booking.id,
+ style: theme.textTheme.bodyMedium,
),
const SizedBox(height: 16),
const Divider(color: colorDivider),
_buildCheckInOutSection(theme),
const Divider(color: colorDivider),
-
_buildBookingDetails(theme),
const Divider(color: colorDivider),
-
_buildPaymentSection(theme),
const Divider(color: colorDivider),
-
_buildCancellationPolicy(theme),
const Divider(color: colorDivider),
-
- TextButton(
- onPressed: () {},
- child: const Text(
- 'Annuller ophold',
- style: TextStyle(
- color: Colors.red,
- fontSize: 16,
+ Center(
+ child: OutlinedButton(
+ style: OutlinedButton.styleFrom(
+ side: const BorderSide(color: Colors.red),
+ ),
+ onPressed: () {},
+ child: Text(
+ 'cancel_booking'.tr(),
+ style: theme.textTheme.bodyMedium?.copyWith(
+ color: Colors.red,
+ ),
),
),
),
+ const SizedBox(height: 100)
],
),
),
),
- const BottomSheetWidget(
- initialExtent: 100,
- maxExtent: 600,
- minExtent: 100,
- widgetChildren: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text('Hello'),
- Text('Hello'),
- ],
- ),
- ],
+ Positioned(
+ bottom: 0,
+ left: 0,
+ right: 0,
+ child: GestureDetector(
+ onTap: () {
+ showModalBottomSheet<void>(
+ isScrollControlled: true,
+ context: context,
+ builder: (context) => BalanceBottomSheet(
+ balance: booking.totalCharge,
+ userName: booking.booker.name,
+ booking: booking,
+ items: [
+ BalanceItem(
+ description: booking.roomType,
+ amount: 1634,
+ isPaid: true,
+ ),
+ const BalanceItem(
+ description: 'Romantisk kurv',
+ amount: 500,
+ isPaid: true,
+ ),
+ const BalanceItem(
+ description: 'Tidlig check-in',
+ amount: 200,
+ ),
+ ],
+ ),
+ );
+ },
+ child: _buildBalanceBottomSheet(theme),
+ ),
),
],
),
@@ -95,39 +125,29 @@ class MyBookingPage extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text('Check-in', style: theme.textTheme.titleMedium),
+ Text('check_in'.tr(), style: theme.textTheme.titleMedium),
const SizedBox(height: 4),
- Text(
- '13. nov. 2024',
- style: theme.textTheme.bodyMedium
- ),
- Text(
- '15:00',
- style: theme.textTheme.bodyMedium
- ),
+ Text(DateFormat('d. MMM').format(booking.startDate),
+ style: theme.textTheme.bodyMedium),
+ Text(DateFormat('HH:mm').format(booking.startDate),
+ style: theme.textTheme.bodyMedium),
],
),
),
- const Expanded(
- child: Icon(
- Icons.arrow_forward,
- color: Colors.grey,
- size: 32,
- ),
- ),
+ const Expanded(child: Icon(Icons.arrow_forward)),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text('Checkout', style: theme.textTheme.titleMedium),
+ Text('check_out'.tr(), style: theme.textTheme.titleMedium),
const SizedBox(height: 4),
Text(
- '14. nov. 2024',
+ DateFormat('d. MMM').format(booking.endDate),
style: theme.textTheme.bodyMedium,
),
const SizedBox(height: 4),
Text(
- '10:00',
+ DateFormat('HH:mm').format(booking.endDate),
style: theme.textTheme.bodyMedium,
),
],
@@ -141,50 +161,59 @@ class MyBookingPage extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text(
- 'Booking',
- style: theme.textTheme.titleMedium
- ),
- const SizedBox(height: 8),
- Text('Standard Double Room', style: theme.textTheme.bodyMedium),
- Text('Overnatning med morgenmad', style: theme.textTheme.bodyMedium),
+ Text('booking_details'.tr(), style: theme.textTheme.titleMedium),
+
+ Text(booking.roomType, style: theme.textTheme.bodyMedium),
const SizedBox(height: 16),
const Divider(color: colorDivider),
Text(
- 'Personer',
+ 'number_of_guests'.tr(),
style: theme.textTheme.titleMedium,
),
+ Text(
+ '${booking.adults} ${booking.adults > 1 ? 'adults'.tr() : 'adult'.tr()}${booking.children > 0 ? ' | ${booking.children} ${booking.children > 1 ? 'children'.tr() : 'child'.tr()}' : ''}',
+ style: theme.textTheme.bodyMedium),
const SizedBox(height: 8),
- Text('2 voksne', style: theme.textTheme.bodyMedium),
- const SizedBox(height: 16),
const Divider(color: colorDivider),
Text(
- 'Booker',
+ 'booker'.tr(),
style: theme.textTheme.titleMedium,
),
+ Text(booking.booker.name, style: theme.textTheme.bodyMedium),
const SizedBox(height: 8),
- Text('Jeppe Bjørholm Andersen', style: theme.textTheme.bodyMedium),
- const SizedBox(height: 16),
const Divider(color: colorDivider),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
- crossAxisAlignment: CrossAxisAlignment.end,
children: [
- Text(
- 'Del ophold',
- style: theme.textTheme.titleMedium,
+ Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'share_booking'.tr(),
+ style: theme.textTheme.titleMedium,
+ ),
+ Text('not_shared'.tr(), style: theme.textTheme.bodyMedium),
+ ],
),
- Container(
- padding: const EdgeInsets.all(8),
- decoration: BoxDecoration(
- color: Colors.brown.shade300,
- shape: BoxShape.circle,
+ SizedBox(
+ width: 100,
+ height: 70,
+ child: BlocProvider(
+ create: (context) => BookingDetailsBloc(
+ booking,
+ bookingDetailsRepository: locator<BookingDetailsRepository>(),
+ ),
+ child: ShareButton(
+ guests: booking.guests,
+ buttonColor: sandColor[40],
+ userButtonWidth: 40,
+ userButtonOverlap: 10,
+ ),
),
- child: const Icon(Icons.add, color: Colors.white, size: 20),
),
],
),
- Text('Ikke delt', style: theme.textTheme.bodyMedium),
],
);
}
@@ -194,21 +223,33 @@ class MyBookingPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
- 'Betaling',
- style: theme.textTheme.titleLarge,
+ 'payment'.tr(),
+ style: theme.textTheme.titleLarge?.copyWith(
+ fontSize: 24,
+ fontWeight: FontWeight.bold,
+ ),
),
const SizedBox(height: 16),
Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Text('Betalingsmetode', style: theme.textTheme.bodyMedium),
- SizedBox(width: 8),
- Text('•••• •••• •••• 1234', style: theme.textTheme.bodyMedium),
- SizedBox(width: 8),
+ Column(
+ children: [
+ Text('payment_method'.tr(), style: theme.textTheme.bodyMedium),
+ SizedBox(width: 8),
+ Text('•••• •••• •••• 1234', style: theme.textTheme.bodyMedium),
+ SizedBox(width: 8),
+ ],
+ ),
+ Image.asset('assets/images/master.png', width: 42, height: 42),
],
),
- SizedBox(height: 16),
- Text('Betalingsdato'),
- Text('15. jul 2024'),
+ const SizedBox(height: 16),
+ const Divider(color: colorDivider),
+ const SizedBox(height: 16),
+ Text('payment_date'.tr(), style: theme.textTheme.bodyMedium),
+ Text(DateFormat('d. MMM').format(booking.bookingDate),
+ style: theme.textTheme.bodyMedium),
],
);
}
@@ -217,13 +258,13 @@ class MyBookingPage extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- const Text(
- 'Annuleringsbetingelser',
- style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
+ Text(
+ 'cancellation_policy'.tr(),
+ style: theme.textTheme.titleMedium,
),
const SizedBox(height: 8),
Text(
- 'Dette ophold kan annuleres omkostningsfrit indtil d. 12.11.2024 kl. 15:00 på ankomstdagen. Annulleres senere end d. 14.01 skal del faktureres v/r for den første nat.',
+ 'cancellation_policy_description'.tr(),
style: theme.textTheme.bodyMedium,
),
],
@@ -231,49 +272,42 @@ class MyBookingPage extends StatelessWidget {
}
Widget _buildBalanceBottomSheet(ThemeData theme) {
- return Positioned(
- bottom: 0,
- left: 0,
- right: 0,
- child: Container(
- decoration: BoxDecoration(
- color: Colors.brown.shade300,
- borderRadius: const BorderRadius.only(
- topLeft: Radius.circular(16),
- topRight: Radius.circular(16),
- ),
+ return Container(
+ height: 100,
+ decoration: const BoxDecoration(
+ color: sandColor,
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(16),
+ topRight: Radius.circular(16),
),
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- 'Min balance',
- style: theme.textTheme.bodyMedium?.copyWith(
- color: Colors.white,
- fontSize: 16,
- fontWeight: FontWeight.w500,
- ),
+ ),
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ 'my_balance'.tr(),
+ style: theme.textTheme.bodyMedium?.copyWith(
+ color: Colors.white,
),
- Row(
- children: [
- const Text(
- '-200 kr',
- style: TextStyle(
- color: Colors.white,
- fontSize: 16,
- fontWeight: FontWeight.w500,
- ),
- ),
- const SizedBox(width: 8),
- Icon(
- Icons.keyboard_arrow_up,
- color: Colors.white.withOpacity(0.8),
+ ),
+ Row(
+ children: [
+ Text(
+ '${booking.totalCharge} kr',
+ style: theme.textTheme.bodyMedium?.copyWith(
+ color: Colors.white,
),
- ],
- ),
- ],
- ),
+ ),
+ const SizedBox(width: 8),
+ Icon(
+ Icons.keyboard_arrow_up,
+ color: Colors.white.withOpacity(0.8),
+ ),
+ ],
+ ),
+ ],
),
);
}
diff --git a/comwell_key_app/lib/overview/components/current_bookings_tab_view.dart b/comwell_key_app/lib/overview/components/current_bookings_tab_view.dart
index 8234b321..4f6b6ac9 100644
--- a/comwell_key_app/lib/overview/components/current_bookings_tab_view.dart
+++ b/comwell_key_app/lib/overview/components/current_bookings_tab_view.dart
@@ -29,7 +29,7 @@ class CurrentBookingsTabView extends StatelessWidget {
height: 276,
decoration: BoxDecoration(
image: DecorationImage(
- image: AssetImage(bookingsImage),
+ image: AssetImage("assets/images/no_current_bookings_background.jpeg"),
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(10),
diff --git a/comwell_key_app/lib/overview/cubit/overview_cubit.dart b/comwell_key_app/lib/overview/cubit/overview_cubit.dart
index 51e87dca..1d82fe2c 100644
--- a/comwell_key_app/lib/overview/cubit/overview_cubit.dart
+++ b/comwell_key_app/lib/overview/cubit/overview_cubit.dart
@@ -14,8 +14,9 @@ class OverviewCubit extends Cubit<OverviewState> {
Future<void> fetchBookings() async {
emit(OverviewLoading());
try {
- //TODO: add correct paramenter when API is ready
- final bookings = await overviewRepository.fetchAllBookingsForUser("1");
+
+ final bookings = await overviewRepository.fetchAllBookingsForUser();
+
emit(OverviewLoaded(bookings: bookings));
} catch (e, st) {
if (kDebugMode) print("err=$e, $st");
@@ -83,7 +84,7 @@ class OverviewCubit extends Cubit<OverviewState> {
Future<void> findBookingById(String bookingId) async {
emit(OverviewLoading());
try {
- final bookings = await overviewRepository.fetchAllBookingsForUser("1");
+ final bookings = await overviewRepository.fetchAllBookingsForUser();
final booking = bookings.current.firstWhere((b) => b.id == bookingId,
orElse: () => bookings.past.firstWhere((b) => b.id == bookingId,
orElse: () =>
diff --git a/comwell_key_app/lib/overview/models/booking.dart b/comwell_key_app/lib/overview/models/booking.dart
index 01faa70c..3c973c4b 100644
--- a/comwell_key_app/lib/overview/models/booking.dart
+++ b/comwell_key_app/lib/overview/models/booking.dart
@@ -19,6 +19,7 @@ class Booking extends Equatable {
final DateTime bookingDate;
final bool digitalCard;
final Iterable<Guest> guests;
+ final num totalCharge;
Booking({
required this.id,
@@ -36,6 +37,7 @@ class Booking extends Equatable {
required this.booker,
required this.bookingDate,
required this.digitalCard,
+ required this.totalCharge,
Iterable<Guest>? guests,
}) : guests = _ensureBookerInGuestList(booker, guests ?? []);
@@ -51,6 +53,11 @@ class Booking extends Equatable {
return guests;
}
+ @override
+ String toString() {
+ return "Booking(id: $id, confirmationId: $confirmationId, roomNumber: $roomNumber, startDate: $startDate, endDate: $endDate, status: $status, image: $image, hotelName: $hotelName, hotelCode: $hotelCode, roomType: $roomType, adults: $adults, children: $children, booker: $booker, bookingDate: $bookingDate, digitalCard: $digitalCard, guests: $guests)";
+ }
+
@override
List<Object?> get props => [
id,
@@ -67,6 +74,7 @@ class Booking extends Equatable {
booker,
bookingDate,
guests,
+ totalCharge,
];
Booking copyWith({
@@ -87,6 +95,7 @@ class Booking extends Equatable {
PaymentDetails? paymentDetails,
String? confirmationId,
Iterable<Guest>? guests,
+ num? totalCharge,
}) {
return Booking(
id: id ?? this.id,
@@ -105,6 +114,7 @@ class Booking extends Equatable {
confirmationId: confirmationId ?? this.confirmationId,
digitalCard: digitalCard,
guests: guests ?? this.guests,
+ totalCharge: totalCharge ?? this.totalCharge,
);
}
@@ -122,6 +132,7 @@ enum BookingStatus {
cancelled;
static BookingStatus fromString(String value) {
- return BookingStatus.values.firstWhere((status) => status.name == value.toLowerCase());
+ return BookingStatus.values
+ .firstWhere((status) => status.name == value.toLowerCase());
}
}
diff --git a/comwell_key_app/lib/overview/repository/overview_repository.dart b/comwell_key_app/lib/overview/repository/overview_repository.dart
index c012b2d8..be36a5e8 100644
--- a/comwell_key_app/lib/overview/repository/overview_repository.dart
+++ b/comwell_key_app/lib/overview/repository/overview_repository.dart
@@ -17,7 +17,7 @@ class OverviewRepository {
OverviewRepository();
- Future<Bookings> fetchAllBookingsForUser(String userId) async {
+ Future<Bookings> fetchAllBookingsForUser() async {
try {
final response = await api.fetchAllBookingsForUser();
final user = await profileRepository.fetchProfileSettings();
@@ -28,6 +28,10 @@ class OverviewRepository {
}
}
+ Iterable<Booking> fetchAllBookingsForUserMock(String userId) {
+ return mockBookings;
+ }
+
Future<Booking?> findBooking(String bookingReference, String lastName) async {
// needs implementation
final user = await database.userDAO.getUser();
@@ -58,9 +62,10 @@ final mockBookings = [1, 2, 3].map((i) => Booking(
startDate: DateTime.now(),
endDate: DateTime.now(),
status: BookingStatus.current,
- image: "",
+ image: "assets/images/no_current_bookings_background.jpeg",
hotelName: "hotelName$i",
roomType: "roomType$i",
+ totalCharge: 100,
children: 3,
adults: 3,
hotelCode: "hotelCode$i",
diff --git a/comwell_key_app/lib/profile/cubit/profile_cubit.dart b/comwell_key_app/lib/profile/cubit/profile_cubit.dart
index 348cd0d5..a1b3e26d 100644
--- a/comwell_key_app/lib/profile/cubit/profile_cubit.dart
+++ b/comwell_key_app/lib/profile/cubit/profile_cubit.dart
@@ -35,8 +35,7 @@ class ProfileCubit extends Cubit<ProfileState> {
final user = await profileRepository.fetchProfileSettings();
emit(ProfileState(user: user, isLoading: false));
} catch (e) {
- print("error in profile cubit: ${e.toString()}");
- emit(ProfileState(user: null, isLoading: false, error: Error()));
+ emit(state.setError(Error()));
}
}
diff --git a/comwell_key_app/lib/profile/profile_repository.dart b/comwell_key_app/lib/profile/profile_repository.dart
index 03cc435f..4659a595 100644
--- a/comwell_key_app/lib/profile/profile_repository.dart
+++ b/comwell_key_app/lib/profile/profile_repository.dart
@@ -29,15 +29,22 @@ class ProfileRepository {
Future<User> fetchProfileSettings() async {
try {
- final user = await db.userDAO.getUser();
- return user;
- } catch (e) {
final response = await api.fetchProfileSettings();
final data = response.data as Json;
final userDto = UserDto.fromJson(data);
final user = userDto.toUser();
- await db.userDAO.saveUser(userDto);
+
+ try {
+ await db.userDAO.saveUser(userDto);
+ } catch (dbError) {
+ print("Database error while saving user: ${dbError.toString()}");
+ // Continue execution even if database save fails
+ }
+
return user;
+ } catch (e) {
+ print("Error fetching profile settings: ${e.toString()}");
+ rethrow;
}
}
diff --git a/comwell_key_app/lib/profile_settings/components/address_bottom_sheet.dart b/comwell_key_app/lib/profile_settings/components/address_bottom_sheet.dart
index 76ce3a35..bf1faac9 100644
--- a/comwell_key_app/lib/profile_settings/components/address_bottom_sheet.dart
+++ b/comwell_key_app/lib/profile_settings/components/address_bottom_sheet.dart
@@ -77,7 +77,7 @@ class _AddressBottomSheetState extends State<AddressBottomSheet> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Text('profil_settings_address'.tr(),
+ Text('profile_settings_address'.tr(),
style: theme.textTheme.headlineLarge),
RoundIconButton(
icon: 'assets/icons/close-icon.svg',
@@ -95,7 +95,7 @@ class _AddressBottomSheetState extends State<AddressBottomSheet> {
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ComwellTextField(
- fieldName: "profil_settings_address".tr(),
+ fieldName: "profile_settings_address".tr(),
initialValue: widget.user.address.street,
readOnly: false,
controller: _addressController)),
diff --git a/comwell_key_app/lib/profile_settings/components/date_time_picker.dart b/comwell_key_app/lib/profile_settings/components/date_time_picker.dart
index 910284f5..872efb39 100644
--- a/comwell_key_app/lib/profile_settings/components/date_time_picker.dart
+++ b/comwell_key_app/lib/profile_settings/components/date_time_picker.dart
@@ -30,7 +30,7 @@ class _DatePickerState extends State<DateTimePicker> {
initialDate: widget.initialValue,
firstDate: DateTime(1930),
lastDate: _maxDate,
- errorInvalidText: "profil_settings_invalid_date".tr(),
+ errorInvalidText: "profile_settings_invalid_date".tr(),
).then((DateTime? selected) {
if (selected != null && selected != _selectedDate) {
setState(() => _selectedDate = selected);
diff --git a/comwell_key_app/lib/routing/app_router.dart b/comwell_key_app/lib/routing/app_router.dart
index d123ccaf..c259cf67 100644
--- a/comwell_key_app/lib/routing/app_router.dart
+++ b/comwell_key_app/lib/routing/app_router.dart
@@ -16,7 +16,9 @@ import 'package:comwell_key_app/hotel_information/hotel_information_page.dart';
import 'package:comwell_key_app/housekeeping/housekeeping_page.dart';
import 'package:comwell_key_app/key/key_page.dart';
import 'package:comwell_key_app/login/login_page.dart';
+import 'package:comwell_key_app/my_booking/cubit/my_booking_cubit.dart';
import 'package:comwell_key_app/my_booking/my_booking_page.dart';
+import 'package:comwell_key_app/my_booking/my_booking_repository.dart';
import 'package:comwell_key_app/notifications/cubit/notifications_cubit.dart';
import 'package:comwell_key_app/notifications/models/notification_permission.dart';
import 'package:comwell_key_app/notifications/notifications_page.dart';
@@ -270,7 +272,14 @@ GoRouter goRouter(AuthenticationBloc authBloc) {
path: "/${AppRoutes.myBooking.name}",
name: AppRoutes.myBooking.name,
builder: (context, state) {
- return const MyBookingPage();
+ final booking = state.extra as Booking;
+ return BlocProvider(
+ create: (context) => MyBookingCubit(
+ locator<MyBookingRepository>(),
+ initialBooking: booking,
+ ),
+ child: MyBookingPage(booking: booking),
+ );
}),
GoRoute(
path: "/${AppRoutes.paymentCards.name}",
diff --git a/comwell_key_app/lib/services/mappers/booking_mapper.dart b/comwell_key_app/lib/services/mappers/booking_mapper.dart
index 1bf2a0e4..509fd5eb 100644
--- a/comwell_key_app/lib/services/mappers/booking_mapper.dart
+++ b/comwell_key_app/lib/services/mappers/booking_mapper.dart
@@ -2,6 +2,7 @@ import 'package:comwell_key_app/overview/models/booking.dart';
import 'package:comwell_key_app/overview/models/guest.dart';
import 'package:comwell_key_app/services/models/booking_dto.dart';
+//TODO: Fix actual image
extension BookingDTOMapper on BookingDTO {
Booking toBooking(int userId, BookingStatus status) {
final startDate = DateTime.parse(dayIn);
@@ -13,9 +14,10 @@ extension BookingDTOMapper on BookingDTO {
startDate: startDate,
endDate: endDate,
status: status,
- image: "",
+ image: "assets/images/no_current_bookings_background.jpeg",
hotelName: "Hotel $hotelCode",
roomType: roomType,
+ totalCharge: totalCharge,
children: children,
booker: Guest(name: "$firstName $lastName", id: "$userId"),
adults: adults,
diff --git a/comwell_key_app/lib/utils/share_button_utils.dart b/comwell_key_app/lib/utils/share_button_utils.dart
index e3b75a51..98448109 100644
--- a/comwell_key_app/lib/utils/share_button_utils.dart
+++ b/comwell_key_app/lib/utils/share_button_utils.dart
@@ -4,3 +4,7 @@ Iterable<String> generateInitials(Iterable<Guest> guests) {
return guests
.map((guest) => guest.name.split(' ').map((name) => name[0]).join(''));
}
+
+String generateInitialsString(String name) {
+ return name.split(' ').map((name) => name[0]).join('');
+}
diff --git a/comwell_key_app/test/overview_test/overview_cubic_test.dart b/comwell_key_app/test/overview_test/overview_cubic_test.dart
index 536825b1..99b120a5 100644
--- a/comwell_key_app/test/overview_test/overview_cubic_test.dart
+++ b/comwell_key_app/test/overview_test/overview_cubic_test.dart
@@ -27,7 +27,7 @@ void main() {
blocTest<OverviewCubit, OverviewState>(
'emits [OverviewLoading, OverviewLoaded] when fetchBookings is successful',
build: () {
- when(() => mockOverviewRepository.fetchAllBookingsForUser('1'))
+ when(() => mockOverviewRepository.fetchAllBookingsForUser())
.thenAnswer((_) async => Bookings(
current: [
Booking(
@@ -40,12 +40,13 @@ void main() {
image: '',
hotelName: '',
roomType: '',
- children: 2,
+ children: 2,
adults: 2,
hotelCode: '',
booker: const Guest(name: "name", id: "id"),
bookingDate: DateTime(2021, 10, 10),
- digitalCard: true)
+ digitalCard: true,
+ totalCharge: 100)
],
past: const [],
cancelled: const [],
@@ -73,7 +74,8 @@ void main() {
hotelCode: '',
booker: const Guest(name: "name", id: "id"),
bookingDate: DateTime(2021, 10, 10),
- digitalCard: true)
+ digitalCard: true,
+ totalCharge: 100)
],
past: const [],
cancelled: const [],
@@ -85,7 +87,7 @@ void main() {
blocTest<OverviewCubit, OverviewState>(
'emits [OverviewLoading, OverviewError] when fetchBookings fails',
build: () {
- when(() => mockOverviewRepository.fetchAllBookingsForUser(any()))
+ when(() => mockOverviewRepository.fetchAllBookingsForUser())
.thenThrow(Exception('Failed to fetch bookings'));
return overviewCubit;
},
@@ -123,7 +125,8 @@ void main() {
hotelCode: '',
booker: const Guest(name: "name", id: "id"),
bookingDate: DateTime(2021, 10, 10),
- digitalCard: true)),
+ digitalCard: true,
+ totalCharge: 100)),
expect: () => [
OverviewLoaded(
bookings: Bookings(
@@ -143,7 +146,8 @@ void main() {
hotelCode: '',
booker: const Guest(name: "name", id: "id"),
bookingDate: DateTime(2021, 10, 10),
- digitalCard: true)
+ digitalCard: true,
+ totalCharge: 100)
],
past: const [],
cancelled: const [],
diff --git a/comwell_key_app/test/overview_test/overview_repository_test.dart b/comwell_key_app/test/overview_test/overview_repository_test.dart
index 820a3223..dc1db5ed 100644
--- a/comwell_key_app/test/overview_test/overview_repository_test.dart
+++ b/comwell_key_app/test/overview_test/overview_repository_test.dart
@@ -36,19 +36,20 @@ void main() {
booker: const Guest(name: "name", id: userId),
bookingDate: DateTime.now(),
digitalCard: true,
+ totalCharge: 100,
),
],
past: const [],
cancelled: const [],
);
- when(() => overviewRepository.fetchAllBookingsForUser(userId))
+ when(() => overviewRepository.fetchAllBookingsForUser())
.thenAnswer((_) async => bookings);
- final result = await overviewRepository.fetchAllBookingsForUser(userId);
+ final result = await overviewRepository.fetchAllBookingsForUser();
expect(result, bookings);
- verify(() => overviewRepository.fetchAllBookingsForUser(userId))
+ verify(() => overviewRepository.fetchAllBookingsForUser())
.called(1);
});
@@ -71,6 +72,7 @@ void main() {
booker: const Guest(name: lastName, id: "id"),
bookingDate: DateTime.now(),
digitalCard: true,
+ totalCharge: 100,
);
when(() => overviewRepository.findBooking(bookingReference, lastName))