import 'package:comwell_key_app/common/components/comwell_app_bar.dart';
import 'package:comwell_key_app/common/components/comwell_error_widget.dart';
import 'package:comwell_key_app/common/components/generic_dialog.dart';
import 'package:comwell_key_app/overview/components/bookings_tab_view.dart';
import 'package:comwell_key_app/overview/components/current_bookings_tab_view.dart';
import 'package:comwell_key_app/overview/components/find_booking_button.dart';
import 'package:comwell_key_app/overview/cubit/overview_cubit.dart';
import 'package:comwell_key_app/overview/cubit/overview_state.dart';
import 'package:comwell_key_app/overview/models/bookings.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:comwell_key_app/utils/context_utils.dart';
import 'package:comwell_key_app/utils/cubit_utils.dart';
import 'package:comwell_key_app/utils/l10n_utils.dart';
import 'package:comwell_key_app/utils/urls.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:url_launcher/url_launcher.dart';
class OverviewPage extends StatefulWidget {
const OverviewPage({super.key});
@override
OverviewTabViewState createState() => OverviewTabViewState();
}
class OverviewTabViewState extends State<OverviewPage>
with WidgetsBindingObserver, SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
WidgetsBinding.instance.addObserver(this);
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed && mounted) {
_checkAndNavigateToBookingDetails();
}
}
void _checkAndNavigateToBookingDetails() {
if (!mounted) return;
// Only check if the overview state is loaded
final overviewCubit = context.read<OverviewCubit>();
if (overviewCubit.state.isLoading) {
return; // Don't check if bookings aren't loaded yet
}
// Only execute if the user is on the overview page
final router = GoRouter.of(context);
final currentLocation = router.routerDelegate.currentConfiguration.last.matchedLocation;
// Check if the current location is the overview page
// The matchedLocation format for overview is "overview"
final isOnOverviewPage =
currentLocation == AppRoutes.overview || currentLocation == AppRoutes.overview;
if (!isOnOverviewPage) {
return; // Only execute if user is on the overview page
}
final checkedInBooking = overviewCubit.getCheckedInBooking();
if (checkedInBooking != null) {
context.push(AppRoutes.bookingDetails, extra: checkedInBooking);
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final tracker = context.tracker;
return MultiBlocListener(
listeners: [
BlocListener<OverviewCubit, OverviewState>(
listener: (context, state) {
if (state.errorType == OverviewStateError.noBookingsFound) {
Future.delayed(const Duration(seconds: 4), () {
if (context.mounted) {
showNotFoundDialog(context);
}
});
}
},
),
BlocListener<OverviewCubit, OverviewState>(
listener: (context, state) {
if (state.errorType == OverviewStateError.noBookingsFound) {
Future.delayed(const Duration(seconds: 4), () {
if (context.mounted) {
showNotFoundDialog(context);
}
});
}
},
),
BlocListener<OverviewCubit, OverviewState>(
listenWhen: (prev, curr) => !curr.isLoading && !curr.bookings.isEmpty,
listener: (context, state) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_checkAndNavigateToBookingDetails();
});
},
),
],
child: BlocBuilder<OverviewCubit, OverviewState>(
builder: (context, state) {
return Scaffold(
backgroundColor: colorBackground,
appBar: const ComwellAppBar(
shouldShowBackButton: false,
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: Container(
height: 39,
decoration: BoxDecoration(
color: sandColor[20],
borderRadius: BorderRadius.circular(25),
),
child: TabBar(
controller: _tabController,
indicatorPadding: const EdgeInsets.all(8),
indicatorSize: TabBarIndicatorSize.tab,
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: sandColor,
),
labelColor: colorBackground,
labelStyle: theme.textTheme.bodyMedium,
unselectedLabelColor: colorTertiary,
dividerColor: Colors.transparent,
tabs: [
Tab(
text: context.strings.overview_tabbar_active,
),
Tab(text: context.strings.overview_tabbar_past),
Tab(text: context.strings.overview_tabbar_cancelled),
],
),
),
),
Builder(
builder: (context) {
if (state.errorType == OverviewStateError.error) {
return Column(
children: [
ComwellErrorWidget(
title: context.strings.overview_error_title,
subtitle: context.strings.overview_error_subtitle,
border: true,
),
IconButton(
onPressed: () {
context.read<OverviewCubit>().fetchBookings();
},
icon: const Icon(
Icons.refresh,
color: colorTertiary,
),
tooltip: context.strings.refresh_bookings,
),
const FindBookingButton(),
],
);
}
return OverviewTabView(
tabController: _tabController,
bookings: state.bookings,
);
},
),
Column(
children: [
Divider(
color: theme.colorScheme.outline,
thickness: 1,
height: 0,
),
Padding(
padding: const EdgeInsets.only(
left: 18.0,
right: 18.0,
bottom: 32.0,
top: 16.0,
),
child: ElevatedButton(
onPressed: () async {
tracker.trackBookNewReservation();
bool success = await launchUrl(Uri.parse(ComwellUrls.comwellWebsite));
if (!success) {
if (context.mounted) {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: colorBackground,
contentPadding: const EdgeInsets.all(24),
title: Text(
context.strings.error_opening_website_title,
style: context.textStyles.headingMedium,
textAlign: TextAlign.center,
),
content: Text(
context.strings.error_opening_website,
style: context.textStyles.body,
textAlign: TextAlign.center,
),
actions: [
Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
style: ElevatedButton.styleFrom(
foregroundColor: colorBackground,
),
child: Text(context.strings.generic_ok),
),
),
],
);
},
);
}
}
},
style: Theme.of(context).elevatedButtonTheme.style?.copyWith(
minimumSize: const WidgetStatePropertyAll(Size(double.infinity, 50)),
shape: WidgetStatePropertyAll(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
),
),
child: Text(
context.strings.new_booking,
style: theme.textTheme.bodyMedium?.copyWith(color: colorBackground),
),
),
),
],
),
],
),
);
},
),
);
}
Future<void> showNotFoundDialog(BuildContext context) {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return GenericDialog(
title: context.strings.booking_not_found,
content: context.strings.booking_not_found_subtitle,
confirmButtonText: context.strings.booking_not_found_button,
confirmButtonTextColor: colorTertiary,
cancelButtonText: context.strings.booking_not_found_cancel,
onConfirm: () {
context.push(AppRoutes.findBooking);
Navigator.of(context).pop(); // Close the dialog
},
onCancel: () {
Navigator.of(context).pop(); // Close the dialog
},
);
},
);
}
}
class OverviewTabView extends StatelessWidget {
const OverviewTabView({
super.key,
required TabController tabController,
required this.bookings,
}) : _tabController = tabController;
final TabController _tabController;
final Bookings bookings;
@override
Widget build(BuildContext context) {
return Expanded(
child: TabBarView(
controller: _tabController,
children: [
CurrentBookingsTabView(
bookings: bookings.current,
bookingsTitle: context.strings.no_current_bookings,
bookingsSubtitle: context.strings.no_current_bookings_subtitle,
bookingsImage: 'assets/images/no_current_bookings_background.jpeg',
),
BookingsTabView(
bookings: bookings.past,
bookingsTitle: context.strings.no_past_bookings_title,
bookingsSubtitle: context.strings.no_past_bookings_subtitle,
bookingsIcon: 'assets/icons/account.svg',
isCancelled: false,
),
BookingsTabView(
bookings: bookings.cancelled,
bookingsTitle: context.strings.no_cancelled_booking_title,
bookingsSubtitle: context.strings.no_cancelled_booking_subtitle,
bookingsIcon: 'assets/icons/thumbs-up.svg',
isCancelled: true,
),
],
),
);
}
}