import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/helper/utils.dart'; import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/enums/work_order_next_step.dart'; import 'package:test_sa/models/helper_data_models/workorder/work_order_helper_models.dart'; import 'package:test_sa/modules/cm_module/cm_detail_provider.dart'; import 'package:test_sa/modules/cm_module/views/components/bottom_sheets/service_request_bottomsheet.dart'; import 'package:test_sa/modules/cx_module/chat/chat_provider.dart'; import 'package:test_sa/modules/cx_module/chat/chat_widget.dart'; import 'package:test_sa/modules/export_module/provider/export_provider.dart'; import 'package:test_sa/modules/loan_module/pages/create_loan_request_page.dart'; import 'package:test_sa/modules/loan_module/pages/loan_equipment_detail_page.dart'; import 'package:test_sa/modules/loan_module/provider/loan_provider.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; import 'package:open_file/open_file.dart'; import 'package:share_plus/share_plus.dart'; import 'dart:developer'; import 'views/components/history_log_view.dart'; import 'views/components/service_request_detail_view.dart'; class CMDetailPage extends StatefulWidget { final int requestId; final int moduleId; CMDetailPage({Key? key, required this.requestId, required this.moduleId}) : super(key: key); @override _CMDetailPageState createState() { return _CMDetailPageState(); } } class _CMDetailPageState extends State { late CMDetailProvider _requestProvider; static const int moduleId = 1; @override void initState() { super.initState(); _requestProvider = Provider.of(context, listen: false); WidgetsBinding.instance.addPostFrameCallback((_) { Provider.of(context, listen: false).reset(); getInitialData(); }); } Future getInitialData() async { bool isNurse = (Provider.of(context, listen: false).user?.type) == UsersTypes.normal_user; _requestProvider = Provider.of(context, listen: false); await _requestProvider.getWorkOrderById(id: widget.requestId); if (isNurse && (_requestProvider.currentWorkOrder?.data?.nextStep?.workOrderNextStepEnum == WorkOrderNextStepEnum.waitingForRequesterToConfirm)) { ServiceRequestBottomSheet.nurseVerifyArrivalBottomSheet(context: context); } _requestProvider.needVisitHelperModel = NeedVisitHelperModel( workOrderId: _requestProvider.currentWorkOrder?.data?.requestId, visitDate: _requestProvider.currentWorkOrder?.data?.needAVisitDateTime, comment: _requestProvider.currentWorkOrder?.data?.needAVisitComment, ); } @override void dispose() { super.dispose(); } Future stopTimer() async { CMDetailProvider requestDetailProvider = Provider.of(context, listen: false); if (requestDetailProvider.timer != null && requestDetailProvider.timer!.isActive) { requestDetailProvider.stopTimer(); } } @override Widget build(BuildContext context) { bool isNurse = (Provider.of(context, listen: false).user?.type) == UsersTypes.normal_user; return WillPopScope( onWillPop: () async { stopTimer(); return true; }, child: Scaffold( backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: DefaultAppBar( title: context.translation.cmDetails, onBackPress: () { stopTimer(); Navigator.pop(context); }, actions: [ Selector( selector: (_, myModel) => myModel.isLoading, // Selects only the userName builder: (_, isLoading, __) { if (isLoading) { return const SizedBox(); } else { CMDetailProvider provider = Provider.of(context, listen: false); if ((context.userProvider.isEngineer || context.userProvider.isNurse) && provider.currentWorkOrder?.data?.assignedEmployee != null && (provider.currentWorkOrder?.data?.workOrderContactPerson?.isNotEmpty ?? false)) { return ChatWidget( moduleId: widget.moduleId, isShow: context.settingProvider.isUserFlowMedical && (context.userProvider.isEngineer || context.userProvider.isNurse) && provider.currentWorkOrder?.data?.assignedEmployee != null, isReadOnly: _requestProvider.isReadOnlyRequest, requestId: widget.requestId.toInt(), assigneeEmployeeNumber: provider.currentWorkOrder?.data?.assignedEmployee!.employeeId!, myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: provider.currentWorkOrder?.data?.workOrderContactPerson.first.employeeId, ); } return const SizedBox(); } }), if (isNurse) IconButton( icon: 'qr'.toSvgAsset(height: 24, width: 24), onPressed: () { ServiceRequestBottomSheet.getQRCodeBottomSheet(context: context); }, ) else ...[ Selector( selector: (_, myModel) => myModel.isLoading, // Selects only the userName builder: (_, isLoading, __) { if (isLoading) { return const SizedBox(); } else { CMDetailProvider provider = Provider.of(context, listen: false); //TODO need to verify the check with backend workOrderNextStepEnum int? statusValue = provider.currentWorkOrder?.data?.status?.value; WorkOrderNextStepEnum? workOrderNextStepEnum = provider.currentWorkOrder?.data?.nextStep?.workOrderNextStepEnum; if (statusValue == 2 && workOrderNextStepEnum == WorkOrderNextStepEnum.activity) { return Container( padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 6), margin: const EdgeInsets.only(left: 2), decoration: BoxDecoration(color: context.isDark ? AppColor.neutral50 : AppColor.neutral30, borderRadius: BorderRadius.circular(16)), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.add, size: 20, color: AppColor.black1E), Text( "Loan", style: AppTextStyles.bodyText2.copyWith(color: AppColor.black1E), ), 4.width, ], ), ).onPress(() async { LoanProvider loanProvider = Provider.of(context, listen: false); Utils.showLoading(context); var data = await loanProvider.getLoanByWorkOrderId(widget.requestId); Utils.hideLoading(context); if (data == null) { CMDetailProvider cmProvider = Provider.of(context, listen: false); Navigator.push(context, MaterialPageRoute(builder: (context) => CreateLoanRequestPage(workOrderId: widget.requestId, workOrder: cmProvider.currentWorkOrder!.data))); } else { Navigator.push(context, MaterialPageRoute(builder: (context) => LoanEquipmentDetailPage(loanId: data.id!))); } }); } return const SizedBox(); } }), // Print Button IconButton( icon: const Icon(Icons.print), onPressed: _handlePrint, ), IconButton( icon: const Icon(Icons.home), onPressed: () { // stopTimer(); Navigator.pop(context); }, ) ], ], ), body: DefaultTabController( length: 2, child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( margin: EdgeInsets.only(left: 16.toScreenWidth, right: 16.toScreenWidth, top: 12.toScreenHeight), decoration: BoxDecoration(color: context.isDark ? AppColor.neutral50 : AppColor.white10, borderRadius: BorderRadius.circular(10)), child: TabBar( //controller: _tabController, padding: EdgeInsets.symmetric(vertical: 4.toScreenHeight, horizontal: 4.toScreenWidth), labelColor: context.isDark ? AppColor.neutral30 : AppColor.black20, unselectedLabelColor: context.isDark ? AppColor.neutral30 : AppColor.black20, unselectedLabelStyle: AppTextStyles.bodyText, labelStyle: AppTextStyles.bodyText, indicatorPadding: EdgeInsets.zero, indicatorSize: TabBarIndicatorSize.tab, dividerColor: Colors.transparent, indicator: BoxDecoration(color: context.isDark ? AppColor.neutral60 : AppColor.neutral110, borderRadius: BorderRadius.circular(7)), onTap: (index) { // setState(() {}); }, tabs: [ Tab(text: context.translation.details, height: 57.toScreenHeight), Tab(text: context.translation.historyLogs, height: 57.toScreenHeight), ], ), ), 12.height, TabBarView( children: [ ServiceRequestDetailView(), const HistoryLogView(), ], ).expanded, ], ), )), ); } void getChatToken(int moduleId, String title) { ChatProvider cProvider = Provider.of(context, listen: false); if (cProvider.chatLoginResponse != null && cProvider.referenceID == widget.requestId) return; String assigneeEmployeeNumber = Provider.of(context, listen: false).currentWorkOrder?.data?.assignedEmployee?.employeeId ?? ""; String myEmployeeId = context.userProvider.user!.username!; String receiver = context.userProvider.isNurse ? assigneeEmployeeNumber : (context.userProvider.isEngineer ? (Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderContactPerson.isEmpty ? Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderCreatedBy!.employeeId! : Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderContactPerson.first.employeeId!) : ""); cProvider.getUserAutoLoginTokenSilent(moduleId, widget.requestId, title, myEmployeeId, receiver); } /// Show success dialog after file is downloaded - using same implementation as export module Future _showSuccessDialog(String filePath) async { if (!mounted) return; return showDialog( context: context, barrierDismissible: false, builder: (BuildContext dialogContext) { return Dialog( backgroundColor: AppColor.background(context), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: Padding( padding: const EdgeInsets.all(24.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ SvgPicture.asset( 'assets/images/done.svg', width: 80, height: 80, ), const SizedBox(height: 24), Text( 'Print Successful', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: AppColor.headingTextColor(context), ), textAlign: TextAlign.center, ), const SizedBox(height: 12), Text( 'Your PDF has been generated successfully', style: TextStyle( fontSize: 14, color: AppColor.lightTextColor(context), ), textAlign: TextAlign.center, ), const SizedBox(height: 32), Row( children: [ Expanded( child: AppFilledButton( onPressed: () async { Navigator.of(dialogContext).pop(); log('📂 Opening printed file...'); final result = await OpenFile.open(filePath); if (result.type != ResultType.done) { log('⚠️ Could not open file: ${result.message}'); 'No app found to open this file type. File saved to: $filePath'.showToast; } }, label: 'Open', buttonColor: AppColor.primary10, showIcon: true, icon: const Icon(Icons.folder_open, color: Colors.white), height: 48, ), ), const SizedBox(width: 12), Expanded( child: AppFilledButton( onPressed: () async { Navigator.of(dialogContext).pop(); log('📤 Sharing printed file...'); try { final xFile = XFile(filePath); await Share.shareXFiles( [xFile], text: 'Printed file', ); log('✅ File shared successfully'); } catch (e) { log('❌ Error sharing file: $e'); 'Failed to share file: $e'.showToast; } }, label: 'Share', buttonColor: AppColor.green40, textColor: AppColor.green60, showIcon: true, icon: Icon(Icons.share, color: AppColor.green60), height: 48, ), ), ], ), ], ), ), ); }, ); } /// Handle print button press Future _handlePrint() async { try { final exportProvider = Provider.of(context, listen: false); final filePath = await exportProvider.printWorkOrder( widget.requestId, widget.moduleId, context, ); if (filePath != null) { await _showSuccessDialog(filePath); } else if (exportProvider.errorMessage != null) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(exportProvider.errorMessage!), backgroundColor: AppColor.red60, ), ); } } } catch (error) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Print failed: $error'), backgroundColor: AppColor.red60, ), ); } } } }