From 6d20e6f30775873ab84be6feb9b112cb80a6b753 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Mon, 22 Dec 2025 11:24:05 +0300 Subject: [PATCH 01/15] chat enable for cm module when engineer assigned. --- .../firebase_notification_manger.dart | 6 +- .../views/nurse/create_new_request_view.dart | 2 +- .../service_request_detail_main_view.dart | 56 +++++++++---------- .../requests/service_request_item_view.dart | 4 +- .../create__device_transfer_request.dart | 4 -- .../requests/pending_requests_screen.dart | 5 +- 6 files changed, 34 insertions(+), 43 deletions(-) diff --git a/lib/controllers/notification/firebase_notification_manger.dart b/lib/controllers/notification/firebase_notification_manger.dart index 77f0a55c..5d3117ba 100644 --- a/lib/controllers/notification/firebase_notification_manger.dart +++ b/lib/controllers/notification/firebase_notification_manger.dart @@ -101,13 +101,13 @@ class FirebaseNotificationManger { break; //these three request are same corrective maintenance.... case "3": - serviceClass = ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString())); + serviceClass = ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); break; case "8": - serviceClass = ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString())); + serviceClass = ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); break; case "11": - serviceClass = ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString())); + serviceClass = ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); break; case "7": serviceClass = DeviceTransferDetails(model: DeviceTransfer(id: int.parse(messageData["requestNumber"].toString())), moduleId: 3); diff --git a/lib/modules/cm_module/views/nurse/create_new_request_view.dart b/lib/modules/cm_module/views/nurse/create_new_request_view.dart index c6e603dc..80e90d94 100644 --- a/lib/modules/cm_module/views/nurse/create_new_request_view.dart +++ b/lib/modules/cm_module/views/nurse/create_new_request_view.dart @@ -300,7 +300,7 @@ class _CreateNewRequestState extends State with TickerProvider bool checkPendingRequest = false; void showPendingRequests() { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => PendingServiceRequestScreen(pendingAssetServiceRequest!))); + Navigator.of(context).push(MaterialPageRoute(builder: (_) => PendingServiceRequestScreen(pendingAssetServiceRequest!, 1))); } void showPendingRequestBottomSheet() async { diff --git a/lib/modules/cm_module/views/service_request_detail_main_view.dart b/lib/modules/cm_module/views/service_request_detail_main_view.dart index 78029461..3e2fab9c 100644 --- a/lib/modules/cm_module/views/service_request_detail_main_view.dart +++ b/lib/modules/cm_module/views/service_request_detail_main_view.dart @@ -13,6 +13,7 @@ import 'package:test_sa/modules/cm_module/service_request_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_page.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/cx_module/survey/survey_page.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; @@ -22,8 +23,9 @@ import 'components/service_request_detail_view.dart'; class ServiceRequestDetailMain extends StatefulWidget { final int requestId; + final int moduleId; - ServiceRequestDetailMain({Key? key, required this.requestId}) : super(key: key); + ServiceRequestDetailMain({Key? key, required this.requestId, required this.moduleId}) : super(key: key); @override _ServiceRequestDetailMainState createState() { @@ -88,37 +90,29 @@ class _ServiceRequestDetailMainState extends State { Navigator.pop(context); }, actions: [ - if (context.userProvider.isEngineer || context.userProvider.isNurse) - Selector( - selector: (_, myModel) => myModel.isLoading, // Selects only the userName - builder: (_, isLoading, __) { - if (isLoading) { - return const SizedBox(); - } else { - ServiceRequestDetailProvider provider = Provider.of(context, listen: false); - int? statusValue = provider.currentWorkOrder?.data?.status?.value; - if (statusValue == 2 || statusValue == 3 || statusValue == 5 || statusValue == 6) { - getChatToken(moduleId, provider.currentWorkOrder?.data?.workOrderNo ?? ""); - return Consumer(builder: (pContext, requestProvider, _) { - return IconButton( - icon: const Icon(Icons.chat_bubble), - onPressed: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => ChatPage( - moduleId: moduleId, - requestId: widget.requestId, - title: _requestProvider.currentWorkOrder?.data?.workOrderNo ?? "", - readOnly: _requestProvider.isReadOnlyRequest, - ))); - }, - ).toShimmer(context: context, isShow: requestProvider.chatLoginTokenLoading, radius: 30, height: 30, width: 30); - }); - } - return const SizedBox(); + Selector( + selector: (_, myModel) => myModel.isLoading, // Selects only the userName + builder: (_, isLoading, __) { + if (isLoading) { + return const SizedBox(); + } else { + ServiceRequestDetailProvider 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.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!.username!, + contactEmployeeINumber: provider.currentWorkOrder?.data?.workOrderContactPerson.first.employeeId, + ); } - }), + return const SizedBox(); + } + }), isNurse ? IconButton( icon: 'qr'.toSvgAsset( diff --git a/lib/new_views/pages/land_page/requests/service_request_item_view.dart b/lib/new_views/pages/land_page/requests/service_request_item_view.dart index 0fac59f4..c6b32c87 100644 --- a/lib/new_views/pages/land_page/requests/service_request_item_view.dart +++ b/lib/new_views/pages/land_page/requests/service_request_item_view.dart @@ -81,7 +81,7 @@ class ServiceRequestItemView extends StatelessWidget { ), ], ).toShadowContainer(context, withShadow: showShadow).onPress(() async { - await Navigator.of(context).push(MaterialPageRoute(builder: (_) => ServiceRequestDetailMain(requestId: requestData!.id!))); + await Navigator.of(context).push(MaterialPageRoute(builder: (_) => ServiceRequestDetailMain(requestId: requestData!.id!, moduleId: requestData!.transactionNo!))); if (refreshData) { Provider.of(context, listen: false).refreshDashboard(userType: Provider.of(context, listen: false).user!.type!, context: context); } @@ -140,7 +140,7 @@ class ServiceRequestItemView extends StatelessWidget { ), ], ).toShadowContainer(context, withShadow: showShadow).onPress(() { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => ServiceRequestDetailMain(requestId: requestDetails!.id!))); + Navigator.of(context).push(MaterialPageRoute(builder: (_) => ServiceRequestDetailMain(requestId: requestDetails!.id!, moduleId: requestDetails!.transactionType!))); }); } diff --git a/lib/views/pages/device_transfer/create__device_transfer_request.dart b/lib/views/pages/device_transfer/create__device_transfer_request.dart index 319e9be8..861dbc9b 100644 --- a/lib/views/pages/device_transfer/create__device_transfer_request.dart +++ b/lib/views/pages/device_transfer/create__device_transfer_request.dart @@ -62,10 +62,6 @@ class _CreateDeviceTransferRequestState extends State PendingServiceRequestScreen(pendingAssetServiceRequest!))); - } - void _onSubmit() async { _transferModel.assetId = _pickedAsset?.id; _transferModel.destSiteId = _assetDestination.site?.id; diff --git a/lib/views/pages/user/requests/pending_requests_screen.dart b/lib/views/pages/user/requests/pending_requests_screen.dart index edd02de4..30b4a814 100644 --- a/lib/views/pages/user/requests/pending_requests_screen.dart +++ b/lib/views/pages/user/requests/pending_requests_screen.dart @@ -9,9 +9,10 @@ import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; import 'package:test_sa/views/widgets/sound/sound_player.dart'; class PendingServiceRequestScreen extends StatelessWidget { + final int moduleId; final PendingAssetServiceRequest pendingAssetServiceRequest; - const PendingServiceRequestScreen(this.pendingAssetServiceRequest, {Key? key}) : super(key: key); + const PendingServiceRequestScreen(this.pendingAssetServiceRequest, this.moduleId, {Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -63,7 +64,7 @@ class PendingServiceRequestScreen extends StatelessWidget { ], ), ).onPress(() { - Navigator.of(context).push(MaterialPageRoute(builder: (_) => ServiceRequestDetailMain(requestId: pendingAssetServiceRequest.details![index].id ?? 0))); + Navigator.of(context).push(MaterialPageRoute(builder: (_) => ServiceRequestDetailMain(requestId: pendingAssetServiceRequest.details![index].id ?? 0, moduleId: moduleId))); }))); } } From 6d6be40b9e4949b677868d992610996cdc079cf9 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 25 Dec 2025 11:34:41 +0300 Subject: [PATCH 02/15] chat notification redirection added. --- .../firebase_notification_manger.dart | 39 +++++++++++++++++++ lib/main.dart | 1 + .../cx_module/chat/chat_api_client.dart | 2 +- .../tasks_wo/task_request_detail_view.dart | 24 +++++++----- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/lib/controllers/notification/firebase_notification_manger.dart b/lib/controllers/notification/firebase_notification_manger.dart index 5d3117ba..cc32e159 100644 --- a/lib/controllers/notification/firebase_notification_manger.dart +++ b/lib/controllers/notification/firebase_notification_manger.dart @@ -12,6 +12,7 @@ import 'package:test_sa/modules/cm_module/views/service_request_detail_main_view import 'package:test_sa/modules/cx_module/survey/survey_page.dart'; import 'package:test_sa/modules/pm_module/ppm_wo/ppm_details_page.dart'; import 'package:test_sa/modules/pm_module/recurrent_wo/recurrent_work_order_view.dart'; +import 'package:test_sa/modules/tm_module/tasks_wo/task_request_detail_view.dart'; import 'package:test_sa/views/pages/device_transfer/device_transfer_details.dart'; import 'package:test_sa/views/pages/user/gas_refill/gas_refill_details.dart'; import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; @@ -88,6 +89,44 @@ class FirebaseNotificationManger { String? transactionType = messageData["transactionType"]?.toString(); + if (transactionType == null) { + return; + } else if (transactionType == "17" && messageData["requestType"] == "chat") { + int moduleId = int.parse(messageData["moduleId"].toString()); + int requestNumber = int.parse(messageData["requestNumber"].toString()); + + switch (moduleId) { + case 1: // cm + serviceClass = ServiceRequestDetailMain(requestId: requestNumber, moduleId: moduleId); + break; + case 2: // gas refill + serviceClass = GasRefillDetailsPage( + priority: messageData["priority"], + date: messageData["createdOn"], + moduleId: moduleId, + model: GasRefillModel(id: requestNumber), + ); + break; + case 3: //transfer + serviceClass = DeviceTransferDetails(model: DeviceTransfer(id: requestNumber), moduleId: moduleId); + break; + case 6: // task + serviceClass = TaskRequestDetailsView( + taskId: requestNumber, + moduleId: moduleId, + // requestDetails: RequestsDetails(nameOfType: requestData?.nameOfType, status: requestData?.statusName, priority: requestData?.priorityName, date: requestData?.transactionDate, ), + ); + ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); + break; + + default: + serviceClass = const Scaffold(body: Center(child: NoDataFound())); + } + + Navigator.of(context).push(MaterialPageRoute(builder: (_) => serviceClass!)); + return; + } + // PPM=1, // ServiceRequestEngineer = 3, // AssetTransfer=7, diff --git a/lib/main.dart b/lib/main.dart index 8855155a..0d138e27 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -126,6 +126,7 @@ import 'providers/service_request_providers/reject_reason_provider.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); // HttpOverrides.global = MyHttpOverrides(); // for later use. _configureLocalTimeZone(); NotificationManger.initialisation((notificationDetails) {}, (id, title, body, payload) async {}); diff --git a/lib/modules/cx_module/chat/chat_api_client.dart b/lib/modules/cx_module/chat/chat_api_client.dart index b859e305..a98cac23 100644 --- a/lib/modules/cx_module/chat/chat_api_client.dart +++ b/lib/modules/cx_module/chat/chat_api_client.dart @@ -82,7 +82,7 @@ class ChatApiClient { Future loadParticipants(int moduleId, int referenceId, String? assigneeEmployeeNumber) async { Response response = - await ApiClient().getJsonForResponse("${URLs.chatHubUrlApi}/chat/context/$moduleId/$referenceId?assigneeEmployeeNumber=$assigneeEmployeeNumber", token: chatLoginResponse!.token); + await ApiClient().getJsonForResponse("${URLs.chatHubUrlApi}/chat/context/$moduleId/$referenceId?assigneeEmployeeNumber=$assigneeEmployeeNumber&requestType=chat&transactionType=17", token: chatLoginResponse!.token); if (!kReleaseMode) { // logger.i("login-res: " + response.body); diff --git a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart index 330ce2f2..e4777fec 100644 --- a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart +++ b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart @@ -75,16 +75,20 @@ class _TaskRequestDetailsViewState extends State { selector: (_, myModel) => myModel.taskRequestModel, // Selects only the userName builder: (_, _taskReqModel, __) { if (_taskReqModel == null) return const SizedBox(); - - return ChatWidget( - moduleId: widget.moduleId, - isShow: _taskReqModel.taskJobStatus!.value! != 1, - isReadOnly: _taskReqModel.taskJobStatus!.value! != 2, - requestId: widget.taskId.toInt(), - assigneeEmployeeNumber: _taskReqModel.assignedEngineer?.employeeId!, - myLoginUserID: context.userProvider.user!.username!, - contactEmployeeINumber: _taskReqModel.taskJobContactPersons!.first.user?.employeeId, - ); + if ((context.userProvider.isEngineer || context.userProvider.isNurse) && + _taskReqModel.assignedEngineer?.employeeId != null && + (_taskReqModel.taskJobContactPersons?.isNotEmpty ?? false)) { + return ChatWidget( + moduleId: widget.moduleId, + isShow: _taskReqModel.taskJobStatus!.value! != 1, + isReadOnly: _taskReqModel.taskJobStatus!.value! != 2, + requestId: widget.taskId.toInt(), + assigneeEmployeeNumber: _taskReqModel.assignedEngineer?.employeeId!, + myLoginUserID: context.userProvider.user!.username!, + contactEmployeeINumber: _taskReqModel.taskJobContactPersons!.first.user?.employeeId, + ); + } else + return const SizedBox(); }) ], ), From a7c206e6f40ca1752031dedad55456bcc986066e Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 25 Dec 2025 11:36:01 +0300 Subject: [PATCH 03/15] chat notification redirection added. --- lib/modules/cx_module/chat/chat_api_client.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/modules/cx_module/chat/chat_api_client.dart b/lib/modules/cx_module/chat/chat_api_client.dart index a98cac23..29d9239a 100644 --- a/lib/modules/cx_module/chat/chat_api_client.dart +++ b/lib/modules/cx_module/chat/chat_api_client.dart @@ -81,8 +81,9 @@ class ChatApiClient { } Future loadParticipants(int moduleId, int referenceId, String? assigneeEmployeeNumber) async { - Response response = - await ApiClient().getJsonForResponse("${URLs.chatHubUrlApi}/chat/context/$moduleId/$referenceId?assigneeEmployeeNumber=$assigneeEmployeeNumber&requestType=chat&transactionType=17", token: chatLoginResponse!.token); + Response response = await ApiClient().getJsonForResponse( + "${URLs.chatHubUrlApi}/chat/context/$moduleId/$referenceId?assigneeEmployeeNumber=$assigneeEmployeeNumber&requestType=chat&transactionType=17", + token: chatLoginResponse!.token); if (!kReleaseMode) { // logger.i("login-res: " + response.body); From 2425c78cf934578dd5cd5cb19895774adf076b84 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Sun, 28 Dec 2025 11:10:17 +0300 Subject: [PATCH 04/15] chat count implemented --- lib/modules/cx_module/chat/chat_widget.dart | 46 +++-- .../chat/model/chat_participant_model.dart | 39 +++-- .../common_widgets/custom_badge.dart | 157 +++++++++++++++++- 3 files changed, 201 insertions(+), 41 deletions(-) diff --git a/lib/modules/cx_module/chat/chat_widget.dart b/lib/modules/cx_module/chat/chat_widget.dart index 4d428abb..f84c7032 100644 --- a/lib/modules/cx_module/chat/chat_widget.dart +++ b/lib/modules/cx_module/chat/chat_widget.dart @@ -1,9 +1,12 @@ +import 'dart:developer'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/modules/cm_module/service_request_detail_provider.dart'; +import 'package:test_sa/new_views/common_widgets/custom_badge.dart'; import 'chat_page.dart'; import 'chat_provider.dart'; @@ -74,24 +77,31 @@ class _ChatWidgetState extends State { @override Widget build(BuildContext context) { return widget.isShow - ? Consumer(builder: (pContext, requestProvider, _) { - return IconButton( - icon: const Icon(Icons.chat_bubble), - onPressed: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (context) => ChatPage( - moduleId: widget.moduleId, - requestId: widget.requestId, - title: widget.title, - readOnly: widget.isReadOnly, - assigneeEmployeeNumber: widget.assigneeEmployeeNumber, - contactEmployeeINumber: widget.contactEmployeeINumber, - myLoginUserID: widget.myLoginUserID, - ))); - }, - ).toShimmer(context: context, isShow: requestProvider.chatLoginTokenLoading, radius: 30, height: 30, width: 30); + ? Consumer(builder: (pContext, chatProvider, _) { + final int unreadCount = (!chatProvider.chatLoginTokenLoading && (chatProvider.chatParticipantModel?.unreadCount ?? 0) > 0) ? chatProvider.chatParticipantModel!.unreadCount! : 0; + return CustomBadge2( + value: unreadCount, + top: 2, + right: 2, + minSize: 18, + child: IconButton( + icon: const Icon(Icons.chat_bubble), + onPressed: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (context) => ChatPage( + moduleId: widget.moduleId, + requestId: widget.requestId, + title: widget.title, + readOnly: widget.isReadOnly, + assigneeEmployeeNumber: widget.assigneeEmployeeNumber, + contactEmployeeINumber: widget.contactEmployeeINumber, + myLoginUserID: widget.myLoginUserID, + ))); + }, + ).toShimmer(context: context, isShow: chatProvider.chatLoginTokenLoading, radius: 30, height: 30, width: 30), + ); }) : const SizedBox(); } diff --git a/lib/modules/cx_module/chat/model/chat_participant_model.dart b/lib/modules/cx_module/chat/model/chat_participant_model.dart index 6ef1d4e4..671bc4c4 100644 --- a/lib/modules/cx_module/chat/model/chat_participant_model.dart +++ b/lib/modules/cx_module/chat/model/chat_participant_model.dart @@ -1,12 +1,15 @@ +import 'dart:developer'; + class ChatParticipantModel { int? id; String? title; String? conversationType; List? participants; - String? lastMessage; + // String? lastMessage; String? createdAt; + int? unreadCount; - ChatParticipantModel({this.id, this.title, this.conversationType, this.participants, this.lastMessage, this.createdAt}); + ChatParticipantModel({this.id, this.title, this.conversationType, this.participants, this.createdAt, this.unreadCount}); ChatParticipantModel.fromJson(Map json) { id = json['id']; @@ -18,20 +21,22 @@ class ChatParticipantModel { participants!.add(new Participants.fromJson(v)); }); } - lastMessage = json['lastMessage']; + // lastMessage = json['lastMessage']; createdAt = json['createdAt']; + unreadCount = json['unreadCount']; } Map toJson() { - final Map data = new Map(); - data['id'] = this.id; - data['title'] = this.title; - data['conversationType'] = this.conversationType; - if (this.participants != null) { - data['participants'] = this.participants!.map((v) => v.toJson()).toList(); + final Map data = {}; + data['id'] = id; + data['title'] = title; + data['conversationType'] = conversationType; + if (participants != null) { + data['participants'] = participants!.map((v) => v.toJson()).toList(); } - data['lastMessage'] = this.lastMessage; - data['createdAt'] = this.createdAt; + // data['lastMessage'] = lastMessage; + data['createdAt'] = createdAt; + data['unreadCount'] = unreadCount; return data; } } @@ -54,12 +59,12 @@ class Participants { } Map toJson() { - final Map data = new Map(); - data['userId'] = this.userId; - data['userName'] = this.userName; - data['employeeNumber'] = this.employeeNumber; - data['role'] = this.role; - data['userStatus'] = this.userStatus; + final Map data = {}; + data['userId'] = userId; + data['userName'] = userName; + data['employeeNumber'] = employeeNumber; + data['role'] = role; + data['userStatus'] = userStatus; return data; } } diff --git a/lib/new_views/common_widgets/custom_badge.dart b/lib/new_views/common_widgets/custom_badge.dart index 654304ec..a048877f 100644 --- a/lib/new_views/common_widgets/custom_badge.dart +++ b/lib/new_views/common_widgets/custom_badge.dart @@ -5,12 +5,20 @@ import 'package:test_sa/new_views/app_style/app_color.dart'; class CustomBadge extends StatelessWidget { final Widget child; // The widget that the badge will be overlaid on. final int value; // The value or text to be displayed in the badge. - final Color color; // The background color of the badge. + final Color color; + final double? positionR; + final double? positionT; + final double? height; + final double? width; - const CustomBadge({ + CustomBadge({ Key? key, required this.child, required this.value, + this.height, + this.width, + this.positionR, + this.positionT, this.color = AppColor.red30, // Default color is red }) : super(key: key); @@ -22,13 +30,14 @@ class CustomBadge extends StatelessWidget { child, // The main widget // if (value > 0) Positioned( - right: -6, - top: -6, + right: positionR ?? -6, + top: positionT ?? -6, + //Need to check why opacity he used here this is not correct need to make this total dynamic and responsive child: Opacity( opacity: value > 0 ? 1 : 0, child: Container( - height: 23, - constraints: const BoxConstraints(minWidth: 23), + height: height ?? 23, + constraints: BoxConstraints(minWidth: width ?? 23), padding: const EdgeInsets.all(3), alignment: Alignment.center, decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(30)), @@ -43,3 +52,139 @@ class CustomBadge extends StatelessWidget { ); } } + +//Need to use this badge every where.. +class CustomBadge2 extends StatelessWidget { + final Widget child; + final int value; + final double top; + final double right; + final double left; + final double bottom; + final double minSize; + final Color backgroundColor; + final Color textColor; + final TextStyle? textStyle; + final int maxValue; + final Duration animationDuration; + + const CustomBadge2({ + super.key, + required this.child, + required this.value, + this.top = -6, + this.right = -6, + this.left = double.nan, + this.bottom = double.nan, + this.minSize = 18, + this.backgroundColor = AppColor.red30, + this.textColor = AppColor.white10, + this.textStyle, + this.maxValue = 99, + this.animationDuration = const Duration(milliseconds: 200), + }); + + bool get _isVisible => value > 0; + + @override + Widget build(BuildContext context) { + return Stack( + clipBehavior: Clip.none, + children: [ + child, + if (_isVisible) + Positioned( + top: top.isNaN ? null : top, + right: right.isNaN ? null : right, + left: left.isNaN ? null : left, + bottom: bottom.isNaN ? null : bottom, + child: AnimatedScale( + scale: _isVisible ? 1 : 0, + duration: animationDuration, + curve: Curves.easeOutBack, + child: AnimatedOpacity( + opacity: _isVisible ? 1 : 0, + duration: animationDuration, + child: _Badge( + value: value, + maxValue: maxValue, + minSize: minSize, + backgroundColor: backgroundColor, + textColor: textColor, + textStyle: textStyle ?? + Theme.of(context).textTheme.labelSmall?.copyWith( + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ), + ], + ); + } +} + +class _Badge extends StatelessWidget { + final int value; + final int maxValue; + final double minSize; + final Color backgroundColor; + final Color textColor; + final TextStyle? textStyle; + + const _Badge({ + required this.value, + required this.maxValue, + required this.minSize, + required this.backgroundColor, + required this.textColor, + this.textStyle, + }); + + bool get _isOverflow => value > maxValue; + + @override + Widget build(BuildContext context) { + final TextStyle baseStyle = (textStyle ?? Theme.of(context).textTheme.labelMedium)!.copyWith( + color: textColor, + fontWeight: FontWeight.w700, + ); + + final double badgeSize = minSize; + + return IgnorePointer( + child: SizedBox( + width: badgeSize, + height: badgeSize, + child: DecoratedBox( + decoration: const BoxDecoration( + shape: BoxShape.circle, + ), + child: DecoratedBox( + decoration: BoxDecoration( + color: backgroundColor, + shape: BoxShape.circle, + ), + child: Center( + child: _isOverflow + ? FittedBox( + child: Text( + '$maxValue+', + style: baseStyle.copyWith( + fontSize: badgeSize * 0.45, + ), + ), + ) + : CounterAnimatedText( + value: value, + style: baseStyle.copyWith( + fontSize: badgeSize * 0.6, + ), + ), + ), + ), + ), + ), + ); + } +} From 077095502e7d5f92bb09d2a06f9b984638a7ea9b Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Mon, 29 Dec 2025 08:56:20 +0300 Subject: [PATCH 05/15] chat count implemented --- .../notification/firebase_notification_manger.dart | 4 +++- .../cm_module/views/service_request_detail_main_view.dart | 2 +- lib/modules/cx_module/chat/helper/chat_file_viewer.dart | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/controllers/notification/firebase_notification_manger.dart b/lib/controllers/notification/firebase_notification_manger.dart index cc32e159..7b6dd5cd 100644 --- a/lib/controllers/notification/firebase_notification_manger.dart +++ b/lib/controllers/notification/firebase_notification_manger.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:firebase_messaging/firebase_messaging.dart'; @@ -84,6 +85,7 @@ class FirebaseNotificationManger { } static void handleMessage(context, Map messageData) { + log('message data is ${messageData}'); if (messageData["requestType"] != null && messageData["requestNumber"] != null) { Widget? serviceClass; @@ -116,7 +118,7 @@ class FirebaseNotificationManger { moduleId: moduleId, // requestDetails: RequestsDetails(nameOfType: requestData?.nameOfType, status: requestData?.statusName, priority: requestData?.priorityName, date: requestData?.transactionDate, ), ); - ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); + // ServiceRequestDetailMain(requestId: int.parse(messageData["requestNumber"].toString()), moduleId: 1); break; default: diff --git a/lib/modules/cm_module/views/service_request_detail_main_view.dart b/lib/modules/cm_module/views/service_request_detail_main_view.dart index 3e2fab9c..332a7b49 100644 --- a/lib/modules/cm_module/views/service_request_detail_main_view.dart +++ b/lib/modules/cm_module/views/service_request_detail_main_view.dart @@ -41,6 +41,7 @@ class _ServiceRequestDetailMainState extends State { @override void initState() { super.initState(); + _requestProvider = Provider.of(context, listen: false); WidgetsBinding.instance.addPostFrameCallback((_) { Provider.of(context, listen: false).reset(); getInitialData(); @@ -49,7 +50,6 @@ class _ServiceRequestDetailMainState extends State { 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); diff --git a/lib/modules/cx_module/chat/helper/chat_file_viewer.dart b/lib/modules/cx_module/chat/helper/chat_file_viewer.dart index 04fba4a1..6d9c99d9 100644 --- a/lib/modules/cx_module/chat/helper/chat_file_viewer.dart +++ b/lib/modules/cx_module/chat/helper/chat_file_viewer.dart @@ -24,6 +24,7 @@ class ChatFileViewer extends StatelessWidget { @override Widget build(BuildContext context) { + return FutureBuilder( future: checkFileInLocalStorage(), builder: (BuildContext context, AsyncSnapshot snapshot) { From 66b77c51a61567cd021dc979e6d82ce0c9545148 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Tue, 30 Dec 2025 09:22:51 +0300 Subject: [PATCH 06/15] loan site bug fixes --- .../loan_module/models/loan_form_model.dart | 8 +++++--- .../pages/create_loan_request_page.dart | 15 ++++----------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/modules/loan_module/models/loan_form_model.dart b/lib/modules/loan_module/models/loan_form_model.dart index b31df2e6..a60b93ef 100644 --- a/lib/modules/loan_module/models/loan_form_model.dart +++ b/lib/modules/loan_module/models/loan_form_model.dart @@ -1,7 +1,5 @@ import 'package:test_sa/models/lookup.dart'; -import 'package:test_sa/models/new_models/building.dart'; -import 'package:test_sa/models/new_models/department.dart'; -import 'package:test_sa/models/new_models/floor.dart'; +import 'package:test_sa/models/new_models/mapped_sites.dart'; import 'package:test_sa/models/new_models/site.dart'; import 'package:test_sa/modules/loan_module/models/medical_department_model.dart'; @@ -21,6 +19,8 @@ class LoanFormModel { Site? site; MedicalDepartmentModel? department; List? loanAttachment; + MappedSite? mappedSite; + MappedDepartment? mappedDepartment; LoanFormModel({ this.docName, @@ -38,6 +38,8 @@ class LoanFormModel { this.loanAttachment, this.site, this.department, + this.mappedSite, + this.mappedDepartment, }); //{ diff --git a/lib/modules/loan_module/pages/create_loan_request_page.dart b/lib/modules/loan_module/pages/create_loan_request_page.dart index fe76e39b..c257622f 100644 --- a/lib/modules/loan_module/pages/create_loan_request_page.dart +++ b/lib/modules/loan_module/pages/create_loan_request_page.dart @@ -1,22 +1,17 @@ import 'dart:convert'; import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:test_sa/controllers/providers/api/device_transfer_provider.dart'; import 'package:test_sa/controllers/validator/validator.dart'; -import 'package:test_sa/dashboard_latest/dashboard_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/device/asset.dart'; -import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/generic_attachment_model.dart'; import 'package:test_sa/models/lookup.dart'; -import 'package:test_sa/models/new_models/site.dart'; +import 'package:test_sa/models/new_models/mapped_sites.dart'; import 'package:test_sa/models/new_models/task_request/task_type_model.dart'; import 'package:test_sa/models/new_models/work_order_detail_model.dart'; import 'package:test_sa/modules/cm_module/cm_request_utils.dart'; @@ -25,13 +20,11 @@ import 'package:test_sa/modules/loan_module/models/loan_form_model.dart'; import 'package:test_sa/modules/loan_module/models/medical_department_model.dart'; import 'package:test_sa/modules/loan_module/provider/loan_period_provider.dart'; import 'package:test_sa/modules/loan_module/provider/loan_provider.dart'; -import 'package:test_sa/modules/loan_module/provider/loan_provider.dart'; import 'package:test_sa/modules/loan_module/provider/medical_department_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/app_text_form_field.dart'; import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; -import 'package:test_sa/providers/department_provider.dart'; import 'package:test_sa/providers/gas_request_providers/site_provider.dart'; import 'package:test_sa/providers/loading_list_notifier.dart'; import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; @@ -275,10 +268,10 @@ class _CreateLoanRequestPageState extends State with Tick return [ 'Item Details'.addTranslation.bodyText(context).custom(color: AppColor.black10), 8.height, - SingleItemDropDownMenu( + SingleItemDropDownMenu( context: context, title: context.translation.site, - initialValue: _loanFormModel.site, + initialValue: _loanFormModel.mappedSite, showShadow: false, validator: (value) { if (value == null) return "Please select a site"; @@ -287,7 +280,7 @@ class _CreateLoanRequestPageState extends State with Tick backgroundColor: AppColor.fieldBgColor(context), showAsBottomSheet: true, onSelect: (value) { - _loanFormModel.site = value; + _loanFormModel.mappedSite = value; setState(() {}); }, ), From e303b4e099485e6dc60e553f87c9c3e7d7812642 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Tue, 30 Dec 2025 13:56:00 +0300 Subject: [PATCH 07/15] chat bug fixes --- lib/controllers/api_routes/urls.dart | 1 + .../cx_module/chat/chat_api_client.dart | 44 +++++++++++++++++++ lib/modules/cx_module/chat/chat_page.dart | 6 ++- lib/modules/cx_module/chat/chat_provider.dart | 31 ++++++++++++- lib/modules/cx_module/chat/chat_widget.dart | 6 ++- .../chat/helper/chat_file_picker.dart | 1 + .../chat/model/chat_participant_model.dart | 6 +-- 7 files changed, 88 insertions(+), 7 deletions(-) diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index bd3e73e6..2897df0d 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -17,6 +17,7 @@ class URLs { static const String chatHubUrlApi = "$chatHubUrl/api"; // new V2 apis static const String chatHubUrlChat = "$chatHubUrl/hubs/chat"; // new V2 apis + static const String resetMessageCount = "$chatHubUrlApi/userChatHistory/AckMessages"; // new V2 apis static const String chatApiKey = "f53a98286f82798d588f67a7f0db19f7aebc839e"; // new V2 apis static String _host = host1; diff --git a/lib/modules/cx_module/chat/chat_api_client.dart b/lib/modules/cx_module/chat/chat_api_client.dart index 29d9239a..77770e83 100644 --- a/lib/modules/cx_module/chat/chat_api_client.dart +++ b/lib/modules/cx_module/chat/chat_api_client.dart @@ -95,6 +95,50 @@ class ChatApiClient { } } + Future resetCountApi(int moduleCode, int referenceNumber, String? employeeNumber) async { + final Map payload = { + 'moduleCode': moduleCode, + 'ReferenceNumber': referenceNumber, + }; + + // final headers = chatAckHeaders( + // apiKey: URLs.chatApiKey, + // employeeNumber: employeeNumber.toString(), + // origin: 'http://localhost:4400', + // referer: 'http://localhost:4400/', + // userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', + // ); + + final Response response = await ApiClient().postJsonForResponse( + URLs.resetMessageCount, + payload, + token: chatLoginResponse!.token, + // headers: headers, + ); + // Response response = await ApiClient().postJsonForResponse( + // "${URLs.resetMessageCount}?moduleCode=$moduleCode&ReferenceNumber=$referenceNumber", {"moduleCode": moduleCode.toString(), "ReferenceNumber": referenceNumber.toString()}, + // token: chatLoginResponse!.token); + + + return response.statusCode == 200; + } + + Map chatAckHeaders({ + required String apiKey, + required String employeeNumber, + required String origin, + required String referer, + required String userAgent, + String acceptLanguage = 'en-US,en;q=0.9', + }) { + return { + 'Accept': 'application/json, text/plain, */*', + 'Content-Type': 'application/json', + // 'X-API-Key': apiKey, + // 'X-Employee-Number': employeeNumber, + }; + } + Future> viewAllDocuments(int moduleId, int referenceId) async { Response response = await ApiClient().getJsonForResponse("${URLs.chatHubUrlApi}/attachments/conversation?referenceId=$referenceId&moduleCode=$moduleId", token: chatLoginResponse!.token); diff --git a/lib/modules/cx_module/chat/chat_page.dart b/lib/modules/cx_module/chat/chat_page.dart index f84b4e23..d4b44721 100644 --- a/lib/modules/cx_module/chat/chat_page.dart +++ b/lib/modules/cx_module/chat/chat_page.dart @@ -75,7 +75,7 @@ class _ChatPageState extends State { }); } - void loadChatHistory() { + void loadChatHistory() async { // // String assigneeEmployeeNumber = Provider.of(context, listen: false).currentWorkOrder?.data?.assignedEmployee?.employeeId ?? ""; // // String myEmployeeId = context.userProvider.user!.username!; // // @@ -99,7 +99,9 @@ class _ChatPageState extends State { ? Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderCreatedBy!.employeeId! : Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderContactPerson.first.employeeId!)) : ""); - Provider.of(context, listen: false).connectToHub(widget.moduleId, widget.requestId, myEmployeeId, receiver, widget.readOnly, isMounted: mounted); + ChatProvider chatProvider = Provider.of(context, listen: false); + chatProvider.connectToHub(widget.moduleId, widget.requestId, myEmployeeId, receiver, widget.readOnly, isMounted: mounted); + } @override diff --git a/lib/modules/cx_module/chat/chat_provider.dart b/lib/modules/cx_module/chat/chat_provider.dart index 136dd28b..84282a64 100644 --- a/lib/modules/cx_module/chat/chat_provider.dart +++ b/lib/modules/cx_module/chat/chat_provider.dart @@ -40,6 +40,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:signalr_netcore/hub_connection.dart'; import 'package:signalr_netcore/signalr_client.dart'; +import 'package:test_sa/controllers/api_routes/api_manager.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/extensions/string_extensions.dart'; import 'package:test_sa/modules/cx_module/chat/model/chat_login_response_model.dart'; @@ -370,9 +371,37 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { // } } } - // notifyListeners(); } + Future resetCount({ + required int moduleId, + required int referenceNo, + String ?userId , + }) async { + try { + return await ChatApiClient().resetCountApi(moduleId, referenceNo,userId); + } catch (e, stack) { + debugPrint('resetCount error: $e'); + rethrow; + } + } + + // Future resetCount({ required int moduleId, + // required int referenceNo}) async { + // Response response; + // try { + // response = await ApiManager.instance.post("${URLs.resetMessageCount}?moduleCode=$moduleId&ReferenceNumber=$referenceNo", body: {}); + // notifyListeners(); + // if (response.statusCode == 200) { + // return true; + // } + // return false; + // } catch (error) { + // notifyListeners(); + // return false; + // } + // } + void updateUserChatHistoryStatusAsync(List data) { try { chatHubConnection!.invoke("UpdateUserChatHistoryStatusAsync", args: [data]); diff --git a/lib/modules/cx_module/chat/chat_widget.dart b/lib/modules/cx_module/chat/chat_widget.dart index f84c7032..788f470b 100644 --- a/lib/modules/cx_module/chat/chat_widget.dart +++ b/lib/modules/cx_module/chat/chat_widget.dart @@ -86,7 +86,11 @@ class _ChatWidgetState extends State { minSize: 18, child: IconButton( icon: const Icon(Icons.chat_bubble), - onPressed: () { + onPressed: () async { + bool readStatus = await chatProvider.resetCount(moduleId: widget.moduleId, referenceNo: widget.requestId, userId: context.userProvider.user?.id); + if (readStatus) { + chatProvider.chatParticipantModel?.unreadCount = 0; + } Navigator.push( context, CupertinoPageRoute( diff --git a/lib/modules/cx_module/chat/helper/chat_file_picker.dart b/lib/modules/cx_module/chat/helper/chat_file_picker.dart index 1070e3db..f080cd20 100644 --- a/lib/modules/cx_module/chat/helper/chat_file_picker.dart +++ b/lib/modules/cx_module/chat/helper/chat_file_picker.dart @@ -46,6 +46,7 @@ class ChatFilePicker extends StatelessWidget { fromMediaPicker(BuildContext context, ImageSource imageSource) async { XFile? pickedFile = await ImagePicker().pickImage(source: imageSource, imageQuality: 70, maxWidth: 800, maxHeight: 800); if (pickedFile != null) { + //Has UI issues here top buttons not clickable smoothly CroppedFile? croppedFile = await ImageCropper().cropImage( sourcePath: pickedFile.path, // aspectRatio: CropAspectRatio(ratioX: 1, ratioY: 1), diff --git a/lib/modules/cx_module/chat/model/chat_participant_model.dart b/lib/modules/cx_module/chat/model/chat_participant_model.dart index 671bc4c4..80c6bfc4 100644 --- a/lib/modules/cx_module/chat/model/chat_participant_model.dart +++ b/lib/modules/cx_module/chat/model/chat_participant_model.dart @@ -5,7 +5,7 @@ class ChatParticipantModel { String? title; String? conversationType; List? participants; - // String? lastMessage; + dynamic lastMessage; String? createdAt; int? unreadCount; @@ -21,7 +21,7 @@ class ChatParticipantModel { participants!.add(new Participants.fromJson(v)); }); } - // lastMessage = json['lastMessage']; + lastMessage = json['lastMessage']; createdAt = json['createdAt']; unreadCount = json['unreadCount']; } @@ -34,7 +34,7 @@ class ChatParticipantModel { if (participants != null) { data['participants'] = participants!.map((v) => v.toJson()).toList(); } - // data['lastMessage'] = lastMessage; + data['lastMessage'] = lastMessage; data['createdAt'] = createdAt; data['unreadCount'] = unreadCount; return data; From d57348dd92fb3c32392f4593f542736d0c96176c Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Tue, 30 Dec 2025 14:58:21 +0300 Subject: [PATCH 08/15] bug fixes for site and department previously it was showing all sites not related to that specif user --- lib/modules/loan_module/models/loan_form_model.dart | 9 ++++++--- .../loan_module/pages/create_loan_request_page.dart | 9 +++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/modules/loan_module/models/loan_form_model.dart b/lib/modules/loan_module/models/loan_form_model.dart index a60b93ef..79d459ca 100644 --- a/lib/modules/loan_module/models/loan_form_model.dart +++ b/lib/modules/loan_module/models/loan_form_model.dart @@ -1,6 +1,7 @@ import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/new_models/mapped_sites.dart'; import 'package:test_sa/models/new_models/site.dart'; +import 'package:test_sa/models/new_models/traf_department.dart'; import 'package:test_sa/modules/loan_module/models/medical_department_model.dart'; class LoanFormModel { @@ -20,7 +21,7 @@ class LoanFormModel { MedicalDepartmentModel? department; List? loanAttachment; MappedSite? mappedSite; - MappedDepartment? mappedDepartment; + TrafDepartment? mappedDepartment; LoanFormModel({ this.docName, @@ -93,8 +94,10 @@ class LoanFormModel { "vendorNumber": vendorNumber, "vendorContact": vendorNumber, "vendorEmail": vendorEmail, - 'siteId': site?.id, - 'departmentId': department?.id, + // 'siteId': site?.id, + 'siteId': mappedSite?.id, + // 'departmentId': department?.id, + 'departmentId': mappedDepartment?.id, "loanAttachments": loanAttachment != null ? loanAttachment!.map((v) => v.toJson()).toList() : [], }; } diff --git a/lib/modules/loan_module/pages/create_loan_request_page.dart b/lib/modules/loan_module/pages/create_loan_request_page.dart index c257622f..b5107f85 100644 --- a/lib/modules/loan_module/pages/create_loan_request_page.dart +++ b/lib/modules/loan_module/pages/create_loan_request_page.dart @@ -13,11 +13,11 @@ import 'package:test_sa/models/generic_attachment_model.dart'; import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/new_models/mapped_sites.dart'; import 'package:test_sa/models/new_models/task_request/task_type_model.dart'; +import 'package:test_sa/models/new_models/traf_department.dart'; import 'package:test_sa/models/new_models/work_order_detail_model.dart'; import 'package:test_sa/modules/cm_module/cm_request_utils.dart'; import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; import 'package:test_sa/modules/loan_module/models/loan_form_model.dart'; -import 'package:test_sa/modules/loan_module/models/medical_department_model.dart'; import 'package:test_sa/modules/loan_module/provider/loan_period_provider.dart'; import 'package:test_sa/modules/loan_module/provider/loan_provider.dart'; import 'package:test_sa/modules/loan_module/provider/medical_department_provider.dart'; @@ -27,6 +27,7 @@ import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; import 'package:test_sa/providers/gas_request_providers/site_provider.dart'; import 'package:test_sa/providers/loading_list_notifier.dart'; +import 'package:test_sa/providers/lookups/department_lookup_provider.dart'; import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; import '../../../../../../new_views/common_widgets/default_app_bar.dart'; @@ -285,7 +286,7 @@ class _CreateLoanRequestPageState extends State with Tick }, ), 8.height, - SingleItemDropDownMenu( + SingleItemDropDownMenu( context: context, title: context.translation.department, showShadow: false, @@ -294,11 +295,11 @@ class _CreateLoanRequestPageState extends State with Tick return null; }, showAsBottomSheet: true, - initialValue: _loanFormModel.department, + initialValue: _loanFormModel.mappedDepartment, requestById: context.userProvider.user?.clientId, backgroundColor: AppColor.fieldBgColor(context), onSelect: (value) { - _loanFormModel.department = value; + _loanFormModel.mappedDepartment = value; setState(() {}); }, ), From af6ae868d7fe816b8732faab3bc7569e3d0970b9 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Tue, 30 Dec 2025 16:23:44 +0300 Subject: [PATCH 09/15] improvements --- .../loan_module/pages/create_loan_request_page.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/modules/loan_module/pages/create_loan_request_page.dart b/lib/modules/loan_module/pages/create_loan_request_page.dart index b5107f85..32616761 100644 --- a/lib/modules/loan_module/pages/create_loan_request_page.dart +++ b/lib/modules/loan_module/pages/create_loan_request_page.dart @@ -281,8 +281,13 @@ class _CreateLoanRequestPageState extends State with Tick backgroundColor: AppColor.fieldBgColor(context), showAsBottomSheet: true, onSelect: (value) { - _loanFormModel.mappedSite = value; - setState(() {}); + if(value!=null){ + _loanFormModel.mappedSite = value; + _loanFormModel.mappedDepartment = null; + Provider.of(context,listen:false).getData(id:_loanFormModel.mappedSite?.id); + setState(() {}); + } + }, ), 8.height, From e598f3b5ad71d5c840129a46f260ff74d62baced Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Wed, 31 Dec 2025 09:39:36 +0300 Subject: [PATCH 10/15] chat bug fixes --- .../cx_module/chat/chat_api_client.dart | 35 ++----------------- lib/modules/cx_module/chat/chat_page.dart | 14 +++++--- lib/modules/cx_module/chat/chat_provider.dart | 18 ++-------- lib/modules/cx_module/chat/chat_widget.dart | 14 ++++---- 4 files changed, 21 insertions(+), 60 deletions(-) diff --git a/lib/modules/cx_module/chat/chat_api_client.dart b/lib/modules/cx_module/chat/chat_api_client.dart index 77770e83..639849a3 100644 --- a/lib/modules/cx_module/chat/chat_api_client.dart +++ b/lib/modules/cx_module/chat/chat_api_client.dart @@ -35,6 +35,7 @@ import 'model/user_chat_history_model.dart'; // import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as user; // import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav; +//Need to refector this remove unused code. class ChatApiClient { static final ChatApiClient _instance = ChatApiClient._internal(); @@ -97,47 +98,17 @@ class ChatApiClient { Future resetCountApi(int moduleCode, int referenceNumber, String? employeeNumber) async { final Map payload = { - 'moduleCode': moduleCode, - 'ReferenceNumber': referenceNumber, + 'moduleCode': "$moduleCode", + 'ReferenceNumber': "$referenceNumber", }; - - // final headers = chatAckHeaders( - // apiKey: URLs.chatApiKey, - // employeeNumber: employeeNumber.toString(), - // origin: 'http://localhost:4400', - // referer: 'http://localhost:4400/', - // userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', - // ); - final Response response = await ApiClient().postJsonForResponse( URLs.resetMessageCount, payload, token: chatLoginResponse!.token, - // headers: headers, ); - // Response response = await ApiClient().postJsonForResponse( - // "${URLs.resetMessageCount}?moduleCode=$moduleCode&ReferenceNumber=$referenceNumber", {"moduleCode": moduleCode.toString(), "ReferenceNumber": referenceNumber.toString()}, - // token: chatLoginResponse!.token); - - return response.statusCode == 200; } - Map chatAckHeaders({ - required String apiKey, - required String employeeNumber, - required String origin, - required String referer, - required String userAgent, - String acceptLanguage = 'en-US,en;q=0.9', - }) { - return { - 'Accept': 'application/json, text/plain, */*', - 'Content-Type': 'application/json', - // 'X-API-Key': apiKey, - // 'X-Employee-Number': employeeNumber, - }; - } Future> viewAllDocuments(int moduleId, int referenceId) async { Response response = await ApiClient().getJsonForResponse("${URLs.chatHubUrlApi}/attachments/conversation?referenceId=$referenceId&moduleCode=$moduleId", token: chatLoginResponse!.token); diff --git a/lib/modules/cx_module/chat/chat_page.dart b/lib/modules/cx_module/chat/chat_page.dart index d4b44721..060bd52b 100644 --- a/lib/modules/cx_module/chat/chat_page.dart +++ b/lib/modules/cx_module/chat/chat_page.dart @@ -28,7 +28,7 @@ import 'helper/chat_file_picker.dart'; import 'helper/chat_file_viewer.dart'; import 'model/get_single_user_chat_list_model.dart'; import 'model/user_chat_history_model.dart'; - +//Need to refactor this ... enum ChatState { idle, voiceRecordingStarted, voiceRecordingCompleted } class ChatPage extends StatefulWidget { @@ -66,7 +66,10 @@ class _ChatPageState extends State { @override void initState() { super.initState(); - loadChatHistory(); + WidgetsBinding.instance.addPostFrameCallback((_) { + loadChatHistory(); + }); + playerController.addListener(() async { // if (playerController.playerState == PlayerState.playing && playerController.maxDuration == await playerController.getDuration()) { // await playerController.stopPlayer(); @@ -99,9 +102,12 @@ class _ChatPageState extends State { ? Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderCreatedBy!.employeeId! : Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderContactPerson.first.employeeId!)) : ""); - ChatProvider chatProvider = Provider.of(context, listen: false); + final chatProvider = context.read(); chatProvider.connectToHub(widget.moduleId, widget.requestId, myEmployeeId, receiver, widget.readOnly, isMounted: mounted); - + bool readStatus = await chatProvider.resetCount(moduleId: widget.moduleId, referenceNo: widget.requestId, userId: context.userProvider.user?.id); + if (readStatus) { + chatProvider.chatParticipantModel?.unreadCount = 0; + } } @override diff --git a/lib/modules/cx_module/chat/chat_provider.dart b/lib/modules/cx_module/chat/chat_provider.dart index 84282a64..46ba0d3c 100644 --- a/lib/modules/cx_module/chat/chat_provider.dart +++ b/lib/modules/cx_module/chat/chat_provider.dart @@ -55,6 +55,8 @@ import 'model/get_single_user_chat_list_model.dart'; import 'model/user_chat_history_model.dart'; // import 'get_single_user_chat_list_model.dart'; +//Need to refactor this remove unused code. + HubConnection? chatHubConnection; class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { @@ -386,22 +388,6 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { } } - // Future resetCount({ required int moduleId, - // required int referenceNo}) async { - // Response response; - // try { - // response = await ApiManager.instance.post("${URLs.resetMessageCount}?moduleCode=$moduleId&ReferenceNumber=$referenceNo", body: {}); - // notifyListeners(); - // if (response.statusCode == 200) { - // return true; - // } - // return false; - // } catch (error) { - // notifyListeners(); - // return false; - // } - // } - void updateUserChatHistoryStatusAsync(List data) { try { chatHubConnection!.invoke("UpdateUserChatHistoryStatusAsync", args: [data]); diff --git a/lib/modules/cx_module/chat/chat_widget.dart b/lib/modules/cx_module/chat/chat_widget.dart index 788f470b..43c5d5fc 100644 --- a/lib/modules/cx_module/chat/chat_widget.dart +++ b/lib/modules/cx_module/chat/chat_widget.dart @@ -1,4 +1,4 @@ -import 'dart:developer'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -43,8 +43,10 @@ class _ChatWidgetState extends State { @override void initState() { super.initState(); - Provider.of(context, listen: false).reset(); - getChatToken(); + WidgetsBinding.instance.addPostFrameCallback((_) { + Provider.of(context, listen: false).reset(); + getChatToken(); + }); } void getChatToken() { @@ -86,11 +88,7 @@ class _ChatWidgetState extends State { minSize: 18, child: IconButton( icon: const Icon(Icons.chat_bubble), - onPressed: () async { - bool readStatus = await chatProvider.resetCount(moduleId: widget.moduleId, referenceNo: widget.requestId, userId: context.userProvider.user?.id); - if (readStatus) { - chatProvider.chatParticipantModel?.unreadCount = 0; - } + onPressed: () { Navigator.push( context, CupertinoPageRoute( From 45d736a64ee098506510bfc487dabd1ff671d925 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 1 Jan 2026 09:24:39 +0300 Subject: [PATCH 11/15] app release v1.5.0+32 to stores --- lib/controllers/api_routes/urls.dart | 10 +-- .../service_request_detail_main_view.dart | 2 +- lib/modules/cx_module/chat/chat_page.dart | 10 ++- lib/modules/cx_module/chat/chat_provider.dart | 68 ++++++++++++++++++- .../tasks_wo/task_request_detail_view.dart | 2 +- .../create_request-type_bottomsheet.dart | 12 ++-- .../my_request/all_requests_filter_page.dart | 24 +++---- .../my_request/my_requests_page.dart | 15 ++-- .../device_transfer_details.dart | 4 +- .../user/gas_refill/gas_refill_details.dart | 2 +- .../widgets/equipment/asset_detail_page.dart | 2 +- 11 files changed, 112 insertions(+), 39 deletions(-) diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 2897df0d..c1b7f5ef 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -1,16 +1,16 @@ class URLs { URLs._(); - static const String appReleaseBuildNumber = "31"; + static const String appReleaseBuildNumber = "32"; - // static const host1 = "https://atomsm.hmg.com"; // production url + static const host1 = "https://atomsm.hmg.com"; // production url // static const host1 = "https://atomsmdev.hmg.com"; // local DEV url - static const host1 = "https://atomsmuat.hmg.com"; // local UAT url + // static const host1 = "https://atomsmuat.hmg.com"; // local UAT url // static final String _baseUrl = "$_host/mobile"; // host local UAT - static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis + // static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis // static final String _baseUrl = "$_host/mobile"; // host local UAT and for internal audit dev - // static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM + static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM // static final String _baseUrl = "$_host/v5/mobile"; // v5 for data segregation static const String chatHubUrl = "https://apiderichat.hmg.com/chathub"; diff --git a/lib/modules/cm_module/views/service_request_detail_main_view.dart b/lib/modules/cm_module/views/service_request_detail_main_view.dart index 332a7b49..f59b49e1 100644 --- a/lib/modules/cm_module/views/service_request_detail_main_view.dart +++ b/lib/modules/cm_module/views/service_request_detail_main_view.dart @@ -102,7 +102,7 @@ class _ServiceRequestDetailMainState extends State { (provider.currentWorkOrder?.data?.workOrderContactPerson?.isNotEmpty ?? false)) { return ChatWidget( moduleId: widget.moduleId, - isShow: (context.userProvider.isEngineer || context.userProvider.isNurse) && provider.currentWorkOrder?.data?.assignedEmployee != null, + 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!, diff --git a/lib/modules/cx_module/chat/chat_page.dart b/lib/modules/cx_module/chat/chat_page.dart index 060bd52b..0cbc72be 100644 --- a/lib/modules/cx_module/chat/chat_page.dart +++ b/lib/modules/cx_module/chat/chat_page.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:audio_waveforms/audio_waveforms.dart'; @@ -28,6 +29,7 @@ import 'helper/chat_file_picker.dart'; import 'helper/chat_file_viewer.dart'; import 'model/get_single_user_chat_list_model.dart'; import 'model/user_chat_history_model.dart'; + //Need to refactor this ... enum ChatState { idle, voiceRecordingStarted, voiceRecordingCompleted } @@ -103,7 +105,9 @@ class _ChatPageState extends State { : Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderContactPerson.first.employeeId!)) : ""); final chatProvider = context.read(); + // final chatProvider = Provider.of { } return Column(mainAxisSize: MainAxisSize.min, children: [ if (showDateHeader) dateCard(currentMessage.createdDate?.toString().chatMsgDateWithYear ?? ""), - isSender ? senderMsgCard(showSenderName, chatProvider.chatResponseList[index]) : recipientMsgCard(showSenderName, chatProvider.chatResponseList[index]) + isSender + ? senderMsgCard(showSenderName, chatProvider.chatResponseList[index], index: index) + : recipientMsgCard(showSenderName, chatProvider.chatResponseList[index]) ]); }, itemCount: chatProvider.chatResponseList.length)) @@ -615,7 +621,7 @@ class _ChatPageState extends State { .center; } - Widget senderMsgCard(bool showHeader, SingleUserChatModel? chatResponse, {bool loading = false, String msg = ""}) { + Widget senderMsgCard(bool showHeader, SingleUserChatModel? chatResponse, {bool loading = false, String msg = "", int index = 0}) { Widget senderHeader = Row( mainAxisSize: MainAxisSize.min, children: [ diff --git a/lib/modules/cx_module/chat/chat_provider.dart b/lib/modules/cx_module/chat/chat_provider.dart index 46ba0d3c..8d56bcf2 100644 --- a/lib/modules/cx_module/chat/chat_provider.dart +++ b/lib/modules/cx_module/chat/chat_provider.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'dart:typed_data'; import 'package:audio_waveforms/audio_waveforms.dart'; @@ -251,6 +252,9 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { chatHubConnection!.on("OnSubmitChatAsync", OnSubmitChatAsync); chatHubConnection!.on("OnTypingAsync", OnTypingAsync); chatHubConnection!.on("OnStopTypingAsync", OnStopTypingAsync); + //Need by Chat Backend for seen and un seen. + chatHubConnection!.on("OnSeenChatUserAsync", onSeenUserChatAsync); + chatHubConnection!.on("OnAckSeenAsync", onAckSeenAsync); //group On message @@ -378,10 +382,10 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { Future resetCount({ required int moduleId, required int referenceNo, - String ?userId , + String? userId, }) async { try { - return await ChatApiClient().resetCountApi(moduleId, referenceNo,userId); + return await ChatApiClient().resetCountApi(moduleId, referenceNo, userId); } catch (e, stack) { debugPrint('resetCount error: $e'); rethrow; @@ -468,6 +472,15 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { print(args); } + Future markMessageAsRead(int messageId) async { + final senderId = sender?.userId; + if (senderId == null) return; + chatHubConnection?.invoke( + "SendMessageReadAsync", + args: [messageId, senderId], + ); + } + void onChatSeen(List? args) { dynamic items = args!.toList(); // for (var user in searchedChats!) { @@ -574,6 +587,57 @@ class ChatProvider with ChangeNotifier, DiagnosticableTreeMixin { notifyListeners(); } + Future onSeenUserChatAsync(List? parameters) async { + try { + if (parameters == null || parameters.isEmpty) { + log('onSeenUserChatAsync: parameters are null or empty'); + return; + } + final parm = parameters.first; + if (parm is! List || parm.isEmpty) { + log('onSeenUserChatAsync: parm is not a valid list'); + return; + } + final firstItem = parm.first; + if (firstItem is! Map) { + log('onSeenUserChatAsync: firstItem is not a Map'); + return; + } + await chatHubConnection!.invoke( + "AckSeenAsync", + args: [ + firstItem['currentUserId'], + [firstItem['userChatHistoryId']], + ], + ); + } catch (e, stackTrace) { + log('onSeenUserChatAsync error: $e'); + log('StackTrace: $stackTrace'); + } + } + + + Future onAckSeenAsync(List? parameters) async { + try { + if (parameters == null || parameters.isEmpty) { + log('onAckSeenAsync: parameters are null or empty'); + return; + } + final parm = parameters.first; + log('parm onAckSeenAsync $parm'); + if (chatResponseList.isEmpty) { + log('onAckSeenAsync: chatResponseList is empty'); + return; + } + log('last list item id ${chatResponseList.first.toJson()}'); + chatResponseList.first.isSeen = true; + notifyListeners(); + } catch (e, stackTrace) { + log('onAckSeenAsync error: $e'); + log('StackTrace: $stackTrace'); + } + } + // Future OnSubmitChatAsync(List? parameters) async { // // List data = jsonDecode(parameters!.first!.toString()); // diff --git a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart index e4777fec..bf3aa96f 100644 --- a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart +++ b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart @@ -80,7 +80,7 @@ class _TaskRequestDetailsViewState extends State { (_taskReqModel.taskJobContactPersons?.isNotEmpty ?? false)) { return ChatWidget( moduleId: widget.moduleId, - isShow: _taskReqModel.taskJobStatus!.value! != 1, + isShow: context.settingProvider.isUserFlowMedical&& _taskReqModel.taskJobStatus!.value! != 1, isReadOnly: _taskReqModel.taskJobStatus!.value! != 2, requestId: widget.taskId.toInt(), assigneeEmployeeNumber: _taskReqModel.assignedEngineer?.employeeId!, diff --git a/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart b/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart index a8d9155c..77439b73 100644 --- a/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart +++ b/lib/new_views/pages/land_page/create_request-type_bottomsheet.dart @@ -110,12 +110,14 @@ class CreateRequestModel { // list.add(CreateRequestModel(module.module!.name!, "add_icon", CreateTaskView.id)); // } // }); - if (context.userProvider.isQualityUser) { + if (context.userProvider.isQualityUser && context.settingProvider.isUserFlowMedical) { list.add(CreateRequestModel("Equipment Internal Audit Checklist".addTranslation, "add_icon", CreateEquipmentInternalAuditForm.id)); list.add(CreateRequestModel("System Internal Audit Checklist".addTranslation, "add_icon", CreateSystemInternalAuditForm.id)); - } else if (context.userProvider.isAssessor) { - list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id)); - } else if (context.userProvider.isEngineer) { + } + // else if (context.userProvider.isAssessor) { + // list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id)); + // } + else if (context.userProvider.isEngineer) { if (Provider.of(context, listen: false).engineerCanCreateCM) { list.add(CreateRequestModel(context.translation.correctiveMaintenance, "add_icon", CreateNewRequest.id)); } @@ -127,7 +129,7 @@ class CreateRequestModel { list.add(CreateRequestModel(context.translation.transferAsset, "add_icon", CreateDeviceTransferRequest.id)); //TODO uncommit this to enable task. list.add(CreateRequestModel(context.translation.task, "add_icon", CreateTaskView.id)); - list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id)); + // list.add(CreateRequestModel("TRAF".addTranslation, "add_icon", CreateTRAFRequestPage.id)); } return list; } diff --git a/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart b/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart index 155babc9..ea79339f 100644 --- a/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart +++ b/lib/new_views/pages/land_page/my_request/all_requests_filter_page.dart @@ -78,8 +78,8 @@ class _AllRequestsFilterPageState extends State { if (isEngineer) { types[context.translation.recurrentWo] = 5; - types["Equipment Internal Audit".addTranslation] = 10; - types["System Internal Audit".addTranslation] = 11; + if (context.settingProvider.isUserFlowMedical) types["Equipment Internal Audit".addTranslation] = 10; + if (context.settingProvider.isUserFlowMedical) types["System Internal Audit".addTranslation] = 11; } if (context.settingProvider.isUserFlowMedical && isEngineer) { @@ -92,18 +92,18 @@ class _AllRequestsFilterPageState extends State { types[module.module!.name!] = module.module!.value!; } }); - - if (!isEngineer) { - types['TRAF'] = 9; - } - - if (context.userProvider.isAssessor) { - types = {"TRAF": 9}; - } +//Hide traf + // if (!isEngineer) { + // types['TRAF'] = 9; + // } + + // if (context.userProvider.isAssessor) { + // types = {"TRAF": 9}; + // } if (context.userProvider.isQualityUser) { if (context.settingProvider.isUserFlowMedical) types = {'Recall and Alert': 7}; - types['Equipment Internal Audit'] = 10; - types['System Internal Audit'] = 11; + if (context.settingProvider.isUserFlowMedical) types['Equipment Internal Audit'] = 10; + if (context.settingProvider.isUserFlowMedical) types['System Internal Audit'] = 11; } final statuses = { diff --git a/lib/new_views/pages/land_page/my_request/my_requests_page.dart b/lib/new_views/pages/land_page/my_request/my_requests_page.dart index a39cc687..30ba4c19 100644 --- a/lib/new_views/pages/land_page/my_request/my_requests_page.dart +++ b/lib/new_views/pages/land_page/my_request/my_requests_page.dart @@ -61,10 +61,11 @@ class _MyRequestsPageState extends State { requestsList.add(Request(module.module!.value!, module.module!.name!.addTranslation)); } }); - if (context.userProvider.user!.type == UsersTypes.normal_user) { - requestsList.add(Request(9, 'TRAF')); - } - if (context.userProvider.isEngineer) { + //Hide TRAF. + // if (context.userProvider.user!.type == UsersTypes.normal_user) { + // requestsList.add(Request(9, 'TRAF')); + // } + if (context.userProvider.isEngineer &&context.settingProvider.isUserFlowMedical) { requestsList.add(Request(10, 'Equipment Internal Audit')); requestsList.add(Request(11, 'System Internal Audit')); } @@ -72,15 +73,15 @@ class _MyRequestsPageState extends State { if (context.userProvider.isAssessor) { requestsList = [ Request(null, context.translation.allWorkOrder), - Request(9, 'TRAF'), + // Request(9, 'TRAF'), ]; } if (context.userProvider.isQualityUser) { requestsList = [ Request(null, context.translation.allWorkOrder), if (context.settingProvider.isUserFlowMedical) Request(7, 'Recall and Alert'), - Request(10, 'Equipment Internal Audit'), - Request(11, 'System Internal Audit'), + if (context.settingProvider.isUserFlowMedical) Request(10, 'Equipment Internal Audit'), + if (context.settingProvider.isUserFlowMedical) Request(11, 'System Internal Audit'), ]; } diff --git a/lib/views/pages/device_transfer/device_transfer_details.dart b/lib/views/pages/device_transfer/device_transfer_details.dart index 03b04961..e0ad05bc 100644 --- a/lib/views/pages/device_transfer/device_transfer_details.dart +++ b/lib/views/pages/device_transfer/device_transfer_details.dart @@ -223,7 +223,7 @@ class _DeviceTransferDetailsState extends State { if (context.userProvider.isEngineer || context.userProvider.isNurse) ChatWidget( moduleId: widget.moduleId, - isShow: _model!.senderMachineStatusValue! != 4, + isShow: context.settingProvider.isUserFlowMedical && _model!.senderMachineStatusValue! != 4, isReadOnly: _model!.senderMachineStatusValue! != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: _model!.senderAssignedEmployeeNumber, @@ -284,7 +284,7 @@ class _DeviceTransferDetailsState extends State { if (context.userProvider.isEngineer || context.userProvider.isNurse) ChatWidget( moduleId: widget.moduleId, - isShow: (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue!) != 0, + isShow: context.settingProvider.isUserFlowMedical && (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue!) != 0, isReadOnly: (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue) != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: isSender ? _deviceTransfer.senderAssignedEmployeeNumber! : _deviceTransfer.receiverAssignedEmployeeNumber!, diff --git a/lib/views/pages/user/gas_refill/gas_refill_details.dart b/lib/views/pages/user/gas_refill/gas_refill_details.dart index 57f81d36..74f4287e 100644 --- a/lib/views/pages/user/gas_refill/gas_refill_details.dart +++ b/lib/views/pages/user/gas_refill/gas_refill_details.dart @@ -68,7 +68,7 @@ class _GasRefillDetailsPageState extends State { return ChatWidget( moduleId: widget.moduleId, - isShow: _gasRefillModel.status!.value! != 0, + isShow: context.settingProvider.isUserFlowMedical && _gasRefillModel.status!.value! != 0, isReadOnly: _gasRefillModel.status!.value! != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: _gasRefillModel.assignedEmployee?.employeeId!, diff --git a/lib/views/widgets/equipment/asset_detail_page.dart b/lib/views/widgets/equipment/asset_detail_page.dart index f34f834c..f0b095fb 100644 --- a/lib/views/widgets/equipment/asset_detail_page.dart +++ b/lib/views/widgets/equipment/asset_detail_page.dart @@ -122,7 +122,7 @@ class _AssetDetailPageState extends State { "${context.translation.building}: ${assetModel.building?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context), "${context.translation.floor}: ${assetModel.floor?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context), "${context.translation.md}: ${assetModel.department?.departmentName?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context), - "${context.translation.room}: ${assetModel.room?.value ?? "-"}".bodyText(context), + "${context.translation.room}: ${assetModel.room?.name?.cleanupWhitespace.capitalizeFirstOfEach ?? "-"}".bodyText(context), ], ).expanded, ], From 45eaa0eeed76547d609ce4bab4dc2040e3b82bf9 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Mon, 5 Jan 2026 16:19:54 +0300 Subject: [PATCH 12/15] app release v1.5.0+32 to stores --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 4efdf850..ec7b7255 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.4.0+31 +version: 1.5.0+32 environment: sdk: ">=3.5.0 <4.0.0" From b78d10af468cc3e6e4442f2f54f6a182b1249d10 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 15 Jan 2026 18:20:38 +0300 Subject: [PATCH 13/15] current user employee id for chat. --- lib/models/user.dart | 4 ++++ .../cm_module/views/service_request_detail_main_view.dart | 2 +- lib/modules/tm_module/tasks_wo/task_request_detail_view.dart | 4 ++-- lib/views/pages/device_transfer/device_transfer_details.dart | 4 ++-- lib/views/pages/user/gas_refill/gas_refill_details.dart | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/models/user.dart b/lib/models/user.dart index 5eac8486..747f221b 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -12,6 +12,7 @@ class User { List? departmentName; String? message; String? username; + String? employeeId; String? userID; String? email; String? password; @@ -53,6 +54,7 @@ class User { this.departmentName, this.message, this.username, + this.employeeId, this.userID, this.email, this.password, @@ -148,6 +150,7 @@ class User { map['message'] = message; map['username'] = username; map['userID'] = userID; + map['employeeId'] = employeeId; map['email'] = email; map['password'] = password; map['token'] = token; @@ -209,6 +212,7 @@ class User { message = json['message']; username = json['username']; userID = json['userID']; + employeeId = json['employeeId']; email = json['email']; password = json['password']; token = json['token']; diff --git a/lib/modules/cm_module/views/service_request_detail_main_view.dart b/lib/modules/cm_module/views/service_request_detail_main_view.dart index f59b49e1..36d5e8bf 100644 --- a/lib/modules/cm_module/views/service_request_detail_main_view.dart +++ b/lib/modules/cm_module/views/service_request_detail_main_view.dart @@ -106,7 +106,7 @@ class _ServiceRequestDetailMainState extends State { isReadOnly: _requestProvider.isReadOnlyRequest, requestId: widget.requestId.toInt(), assigneeEmployeeNumber: provider.currentWorkOrder?.data?.assignedEmployee!.employeeId!, - myLoginUserID: context.userProvider.user!.username!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: provider.currentWorkOrder?.data?.workOrderContactPerson.first.employeeId, ); } diff --git a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart index bf3aa96f..514d8055 100644 --- a/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart +++ b/lib/modules/tm_module/tasks_wo/task_request_detail_view.dart @@ -80,11 +80,11 @@ class _TaskRequestDetailsViewState extends State { (_taskReqModel.taskJobContactPersons?.isNotEmpty ?? false)) { return ChatWidget( moduleId: widget.moduleId, - isShow: context.settingProvider.isUserFlowMedical&& _taskReqModel.taskJobStatus!.value! != 1, + isShow: context.settingProvider.isUserFlowMedical && _taskReqModel.taskJobStatus!.value! != 1, isReadOnly: _taskReqModel.taskJobStatus!.value! != 2, requestId: widget.taskId.toInt(), assigneeEmployeeNumber: _taskReqModel.assignedEngineer?.employeeId!, - myLoginUserID: context.userProvider.user!.username!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: _taskReqModel.taskJobContactPersons!.first.user?.employeeId, ); } else diff --git a/lib/views/pages/device_transfer/device_transfer_details.dart b/lib/views/pages/device_transfer/device_transfer_details.dart index e0ad05bc..49b58057 100644 --- a/lib/views/pages/device_transfer/device_transfer_details.dart +++ b/lib/views/pages/device_transfer/device_transfer_details.dart @@ -227,7 +227,7 @@ class _DeviceTransferDetailsState extends State { isReadOnly: _model!.senderMachineStatusValue! != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: _model!.senderAssignedEmployeeNumber, - myLoginUserID: context.userProvider.user!.username!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: _model!.assetTransferContactPersons!.first.employeeNumber, ), if ((_userProvider!.user?.type == UsersTypes.engineer)) @@ -288,7 +288,7 @@ class _DeviceTransferDetailsState extends State { isReadOnly: (isSender ? _deviceTransfer.senderMachineStatusValue! : _deviceTransfer.receiverMachineStatusValue) != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: isSender ? _deviceTransfer.senderAssignedEmployeeNumber! : _deviceTransfer.receiverAssignedEmployeeNumber!, - myLoginUserID: context.userProvider.user!.username!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: _deviceTransfer.assetTransferContactPersons!.first.employeeNumber, ), if ((_userProvider!.user?.type == UsersTypes.engineer)) diff --git a/lib/views/pages/user/gas_refill/gas_refill_details.dart b/lib/views/pages/user/gas_refill/gas_refill_details.dart index 74f4287e..45d3c128 100644 --- a/lib/views/pages/user/gas_refill/gas_refill_details.dart +++ b/lib/views/pages/user/gas_refill/gas_refill_details.dart @@ -72,7 +72,7 @@ class _GasRefillDetailsPageState extends State { isReadOnly: _gasRefillModel.status!.value! != 1, requestId: widget.model.id!.toInt(), assigneeEmployeeNumber: _gasRefillModel.assignedEmployee?.employeeId!, - myLoginUserID: context.userProvider.user!.username!, + myLoginUserID: context.userProvider.user!.employeeId ?? context.userProvider.user!.username!, contactEmployeeINumber: _gasRefillModel.gasRefillContactPerson!.first.employeeCode!, ); }) From 520e7af640694ab5e59c156ac04a4b9f0a326039 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 18 Jan 2026 15:05:47 +0300 Subject: [PATCH 14/15] fcm initialization changed to landing page --- lib/dashboard_latest/dashboard_view.dart | 16 ++++++++-------- lib/new_views/pages/land_page/land_page.dart | 12 +++++++++++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/dashboard_latest/dashboard_view.dart b/lib/dashboard_latest/dashboard_view.dart index 987e76cd..bf9b5a71 100644 --- a/lib/dashboard_latest/dashboard_view.dart +++ b/lib/dashboard_latest/dashboard_view.dart @@ -73,14 +73,14 @@ class _DashboardViewState extends State { // await requestDetailProvider.engineerRejectWorkOrder(id: '3', feedBack: 'Abcdef'); // await dashBoardProvider.getDashBoardCount(usersType: user.type!); // await getAllRequests(); - if (isFCM) { - FirebaseNotificationManger.initialized(context); - NotificationManger.initialisation((notificationDetails) { - FirebaseNotificationManger.handleMessage(context, json.decode(notificationDetails.payload!)); - }, (id, title, body, payload) async {}); - - isFCM = false; - } + // if (isFCM) { + // FirebaseNotificationManger.initialized(context); + // NotificationManger.initialisation((notificationDetails) { + // FirebaseNotificationManger.handleMessage(context, json.decode(notificationDetails.payload!)); + // }, (id, title, body, payload) async {}); + // + // isFCM = false; + // } }); } diff --git a/lib/new_views/pages/land_page/land_page.dart b/lib/new_views/pages/land_page/land_page.dart index b28b6eb2..ea07c085 100644 --- a/lib/new_views/pages/land_page/land_page.dart +++ b/lib/new_views/pages/land_page/land_page.dart @@ -1,8 +1,11 @@ +import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:test_sa/controllers/notification/firebase_notification_manger.dart'; +import 'package:test_sa/controllers/notification/notification_manger.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/controllers/providers/settings/app_settings.dart'; import 'package:test_sa/dashboard_latest/dashboard_view.dart'; @@ -42,7 +45,7 @@ class _LandPageState extends State { bool showAppbar = true; late List _pages; UserProvider? _userProvider; - + bool isFCM = true; @override void initState() { _pages = []; @@ -123,7 +126,14 @@ class _LandPageState extends State { // if (_userProvider!.user!.type != UsersTypes.engineer) const CalendarPage(), const MyAssetsPage(fromBottomBar: true), ]; + if (isFCM) { + FirebaseNotificationManger.initialized(context); + NotificationManger.initialisation((notificationDetails) { + FirebaseNotificationManger.handleMessage(context, json.decode(notificationDetails.payload!)); + }, (id, title, body, payload) async {}); + isFCM = false; + } checkLocalAuth(); } From 4bd055cfe852a4787955f56abdb2aa090903a15e Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Tue, 20 Jan 2026 11:00:43 +0300 Subject: [PATCH 15/15] merge conflict resolved. --- .../notification/firebase_notification_manger.dart | 7 +++---- lib/modules/cm_module/cm_detail_page.dart | 2 +- lib/modules/cx_module/chat/chat_widget.dart | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/controllers/notification/firebase_notification_manger.dart b/lib/controllers/notification/firebase_notification_manger.dart index b941e40e..243e8a57 100644 --- a/lib/controllers/notification/firebase_notification_manger.dart +++ b/lib/controllers/notification/firebase_notification_manger.dart @@ -13,9 +13,8 @@ import 'package:test_sa/modules/cm_module/cm_detail_page.dart'; import 'package:test_sa/modules/cx_module/survey/survey_page.dart'; import 'package:test_sa/modules/pm_module/ppm_wo/ppm_details_page.dart'; import 'package:test_sa/modules/pm_module/recurrent_wo/recurrent_work_order_view.dart'; -import 'package:test_sa/modules/tm_module/tasks_wo/task_request_detail_view.dart'; -import 'package:test_sa/views/pages/device_transfer/device_transfer_details.dart'; -import 'package:test_sa/views/pages/user/gas_refill/gas_refill_details.dart'; +import 'package:test_sa/modules/tm_module/device_transfer/device_transfer_details.dart'; +import 'package:test_sa/modules/tm_module/tasks/task_request_detail_view.dart'; import 'package:test_sa/modules/tm_module/gas_refill/gas_refill_details.dart'; import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; @@ -99,7 +98,7 @@ class FirebaseNotificationManger { switch (moduleId) { case 1: // cm - serviceClass = ServiceRequestDetailMain(requestId: requestNumber, moduleId: moduleId); + serviceClass = CMDetailPage(requestId: requestNumber, moduleId: moduleId); break; case 2: // gas refill serviceClass = GasRefillDetailsPage( diff --git a/lib/modules/cm_module/cm_detail_page.dart b/lib/modules/cm_module/cm_detail_page.dart index b9870847..15b1b503 100644 --- a/lib/modules/cm_module/cm_detail_page.dart +++ b/lib/modules/cm_module/cm_detail_page.dart @@ -42,7 +42,7 @@ class _CMDetailPageState extends State { @override void initState() { super.initState(); - _requestProvider = Provider.of(context, listen: false); + _requestProvider = Provider.of(context, listen: false); WidgetsBinding.instance.addPostFrameCallback((_) { Provider.of(context, listen: false).reset(); getInitialData(); diff --git a/lib/modules/cx_module/chat/chat_widget.dart b/lib/modules/cx_module/chat/chat_widget.dart index 43c5d5fc..8d5011e8 100644 --- a/lib/modules/cx_module/chat/chat_widget.dart +++ b/lib/modules/cx_module/chat/chat_widget.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; -import 'package:test_sa/modules/cm_module/service_request_detail_provider.dart'; +import 'package:test_sa/modules/cm_module/cm_detail_provider.dart'; import 'package:test_sa/new_views/common_widgets/custom_badge.dart'; import 'chat_page.dart'; @@ -59,13 +59,13 @@ class _ChatWidgetState extends State { ChatProvider cProvider = Provider.of(context, listen: false); if (cProvider.chatLoginResponse != null && cProvider.referenceID == widget.requestId) return; - String assigneeEmployeeNumber = widget.assigneeEmployeeNumber ?? Provider.of(context, listen: false).currentWorkOrder?.data?.assignedEmployee?.employeeId ?? ""; + String assigneeEmployeeNumber = widget.assigneeEmployeeNumber ?? Provider.of(context, listen: false).currentWorkOrder?.data?.assignedEmployee?.employeeId ?? ""; String myEmployeeId = widget.myLoginUserID ?? context.userProvider.user!.username!; String receiver = context.userProvider.isNurse ? assigneeEmployeeNumber : (context.userProvider.isEngineer - ? (widget.contactEmployeeINumber ?? Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderContactPerson.first.employeeId!) + ? (widget.contactEmployeeINumber ?? Provider.of(context, listen: false).currentWorkOrder!.data!.workOrderContactPerson.first.employeeId!) : ""); cProvider.getUserAutoLoginTokenSilent(widget.moduleId, widget.requestId, widget.title, myEmployeeId, receiver, isMounted: mounted);