diff --git a/.gitignore b/.gitignore index 034a5be..4a374d7 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,6 @@ pubspec.lock /build/ # Web related -lib/generated_plugin_registrant.dart # Symbolication related app.*.symbols @@ -46,3 +45,4 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release +/ios/ diff --git a/assets/icons/chat/call.svg b/assets/icons/chat/call.svg new file mode 100644 index 0000000..843daf4 --- /dev/null +++ b/assets/icons/chat/call.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/chat/doc.svg b/assets/icons/chat/doc.svg new file mode 100644 index 0000000..1f678df --- /dev/null +++ b/assets/icons/chat/doc.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/chat/ppt.svg b/assets/icons/chat/ppt.svg new file mode 100644 index 0000000..5134010 --- /dev/null +++ b/assets/icons/chat/ppt.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/chat/txt.svg b/assets/icons/chat/txt.svg new file mode 100644 index 0000000..bbaf693 --- /dev/null +++ b/assets/icons/chat/txt.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/chat/video_call.svg b/assets/icons/chat/video_call.svg new file mode 100644 index 0000000..2fceee6 --- /dev/null +++ b/assets/icons/chat/video_call.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/chat/xls.svg b/assets/icons/chat/xls.svg new file mode 100644 index 0000000..325f974 --- /dev/null +++ b/assets/icons/chat/xls.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/chat/zip.svg b/assets/icons/chat/zip.svg new file mode 100644 index 0000000..9aaaf6b --- /dev/null +++ b/assets/icons/chat/zip.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/monthly_attendance.svg b/assets/images/monthly_attendance.svg index c84a9a1..b77ccc3 100644 --- a/assets/images/monthly_attendance.svg +++ b/assets/images/monthly_attendance.svg @@ -7,9 +7,9 @@ - - - + + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index f72ea36..1c5851a 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -498,5 +498,7 @@ "verification": "تَحَقّق", "resend": "إعادة إرسال", "codeExpire": "انتهت صلاحية رمز التحقق", - "typeheretoreply": "اكتب هنا للرد" + "typeheretoreply": "اكتب هنا للرد", + "favorite": "مفضلتي", + "searchfromchat": "البحث من الدردشة" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 896a365..b4b06f0 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -284,7 +284,7 @@ "add": "Add", "edit": "Edit", "myProfile": "My Profile", - "mowadhafhi": "Mowadhafhi", + "mowadhafhi": "Mowadhafi", "searchAnnouncements": "Search Announcements", "announcements": "Announcements", "swipeRequest": "Swipe Request", @@ -296,7 +296,7 @@ "relatedTopic": "Related Topic", "selectTopic": "Select Topic", "supportingDocument": "Supporting Document", - "mowadhafhiRequest": "Mowadhafhi Request", + "mowadhafhiRequest": "Mowadhafi Request", "ticketReference": "Ticket Reference", "section": "Section", "topic": "Topic", @@ -498,5 +498,8 @@ "resend": "Resend", "codeExpire": "The verification code has been expired", "allQuestionsCorrect": "You have answered all questions correct", - "typeheretoreply": "Type here to reply" + "typeheretoreply": "Type here to reply", + "favorite" : "My Favorites", + "searchfromchat": "Search from chat" + } \ No newline at end of file diff --git a/assets/lottie/loading.json b/assets/lottie/loading.json index 8190459..ea7bff3 100644 --- a/assets/lottie/loading.json +++ b/assets/lottie/loading.json @@ -1 +1 @@ -{"v":"5.8.1","fr":30,"ip":0,"op":60,"w":300,"h":300,"nm":"loading_6","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":60,"s":[360]}],"ix":10},"p":{"a":0,"k":[150.00000000000003,150.00000000000003,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[30.000000000000004,30.000000000000004,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[300,300],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.12941176470588237,0.7411764705882353,0.7764705882352941,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":50,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[0]},{"t":60,"s":[99]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[1]},{"t":50,"s":[100]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":60,"s":[3]}],"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":30,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[150.00000000000003,150.00000000000003,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[30.000000000000004,30.000000000000004,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[300,300],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.7450980392156863,0.9254901960784314,0.9372549019607843,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":50,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":300,"st":0,"bm":0}],"markers":[]} \ No newline at end of file +{"v":"5.8.1","fr":30,"ip":0,"op":60,"w":100,"h":100,"nm":"loading_6","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":60,"s":[360]}],"ix":10},"p":{"a":0,"k":[50.00000000000002,50.00000000000002,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[10.000000000000004,10.000000000000004,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[300,300],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.12941176470588237,0.7411764705882353,0.7764705882352941,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":50,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[0]},{"t":60,"s":[99]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[1]},{"t":50,"s":[100]}],"ix":2},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":60,"s":[3]}],"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":30,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50.00000000000002,50.00000000000002,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[10.000000000000004,10.000000000000004,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[300,300],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.7450980392156863,0.9254901960784314,0.9372549019607843,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":50,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":300,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 2ac7a44..2797543 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -44,6 +44,8 @@ This App requires access to your location to mark your attendance. NSPhotoLibraryUsageDescription This app requires photo library access to select image as document & upload it. + NSMicrophoneUsageDescription + This app requires microphone access to for call. UIBackgroundModes remote-notification diff --git a/lib/api/chat/chat_provider_model.dart b/lib/api/chat/chat_provider_model.dart index bcb748c..7a27c1f 100644 --- a/lib/api/chat/chat_provider_model.dart +++ b/lib/api/chat/chat_provider_model.dart @@ -1,37 +1,67 @@ +import 'dart:async'; import 'dart:convert'; +import 'dart:io'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; -import 'package:fluttertoast/fluttertoast.dart'; import 'package:http/http.dart'; +import 'package:logger/logger.dart' as L; import 'package:logging/logging.dart'; import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/models/chat/get_search_user_chat_model.dart'; -import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_Model.dart'; +import 'package:mohem_flutter_app/models/chat/get_single_user_chat_list_model.dart'; import 'package:mohem_flutter_app/models/chat/get_user_login_token_model.dart' as login; -import 'package:shared_preferences/shared_preferences.dart'; +import 'package:mohem_flutter_app/models/chat/make_user_favotire_unfavorite_chat_model.dart' as fav; +import 'package:mohem_flutter_app/widgets/image_picker.dart'; import 'package:signalr_netcore/signalr_client.dart'; -import 'package:logger/logger.dart' as L; +import 'package:uuid/uuid.dart'; class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { + ScrollController scrollController = ScrollController(); + TextEditingController message = TextEditingController(); + TextEditingController search = TextEditingController(); List userChatHistory = []; List? pChatHistory, searchedChats; late HubConnection hubConnection; L.Logger logger = L.Logger(); - TextEditingController message = TextEditingController(); - ScrollController scrollController = ScrollController(); + bool hubConInitialized = false; + String chatCID = ''; bool isLoading = true; + bool isChatScreenActive = false; + late File selectedFile; + bool isFileSelected = false; + String sFileType = ""; + bool isMsgReply = false; + List repliedMsg = []; + List favUsersList = []; + int paginationVal = 0; - Future getUserAutoLoginToken() async { - String userName = AppState().memberInformationList!.eMPLOYEEEMAILADDRESS!.split("@").first.toString(); - Response response = - await ApiClient().postJsonForResponse("${ApiConsts.chatServerBaseApiUrl}user/desktopuserlogin", {"userName": userName, "password": "FxIu26rWIKoF8n6mpbOmAjDLphzFGmpG", "loginType": 2}); - login.UserAutoLoginModel userLoginResponse = login.userAutoLoginModelFromJson(response.body); - AppState().setchatUserDetails = userLoginResponse; - await buildHubConnection(); + Future getUserAutoLoginToken(BuildContext cxt) async { + Response response = await ApiClient().postJsonForResponse( + "${ApiConsts.chatServerBaseApiUrl}user/externaluserlogin", + { + "employeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER.toString(), + "password": "FxIu26rWIKoF8n6mpbOmAjDLphzFGmpG", + }, + ); + login.UserAutoLoginModel userLoginResponse = login.userAutoLoginModelFromJson( + response.body, + ); + + if (userLoginResponse.response != null) { + hubConInitialized = true; + AppState().setchatUserDetails = userLoginResponse; + await buildHubConnection(); + } else { + Utils.showToast( + userLoginResponse.errorResponses!.first.fieldName.toString() + " Erorr", + ); + return; + } } Future?> getChatMemberFromSearch(String sName, int cUserId) async { @@ -40,12 +70,13 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { token: AppState().chatDetails!.response!.token, ); return searchUserJsonModel(response.body); - logger.d(response.body); - isLoading = false; - notifyListeners(); } - List searchUserJsonModel(String str) => List.from(json.decode(str).map((x) => ChatUser.fromJson(x))); + List searchUserJsonModel(String str) => List.from( + json.decode(str).map( + (x) => ChatUser.fromJson(x), + ), + ); void getUserRecentChats() async { Response response = await ApiClient().getJsonForResponse( @@ -53,80 +84,210 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { token: AppState().chatDetails!.response!.token, ); ChatUserModel recentChat = userToList(response.body); - pChatHistory = recentChat.response; + + Response favRes = await ApiClient().getJsonForResponse( + "${ApiConsts.chatServerBaseApiUrl}${ApiConsts.chatFavoriteUsers}${AppState().chatDetails!.response!.id}", + token: AppState().chatDetails!.response!.token, + ); + ChatUserModel favUList = userToList(favRes.body); + + if (favUList.response != null && recentChat.response != null) { + favUsersList = favUList.response!; + favUsersList.sort( + (ChatUser a, ChatUser b) => a.userName!.toLowerCase().compareTo( + b.userName!.toLowerCase(), + ), + ); + for (dynamic user in recentChat.response!) { + for (dynamic favUser in favUList.response!) { + if (user.id == favUser.id) { + user.isFav = favUser.isFav; + } + } + } + } + pChatHistory = recentChat.response ?? []; + pChatHistory!.sort( + (ChatUser a, ChatUser b) => a.userName!.toLowerCase().compareTo( + b.userName!.toLowerCase(), + ), + ); + searchedChats = pChatHistory; isLoading = false; notifyListeners(); } - void getSingleUserChatHistory({required String senderUID, required int receiverUID, required String pagination}) async { + Future getUserChatHistoryNotDeliveredAsync({required int userId}) async { + await hubConnection.invoke( + "GetUserChatHistoryNotDeliveredAsync", + args: [userId], + ); + return ""; + } + + void getSingleUserChatHistory({required int senderUID, required int receiverUID, required bool loadMore, bool isNewChat = false}) async { isLoading = true; + if (isNewChat) userChatHistory = []; + if (!loadMore) paginationVal = 0; + isChatScreenActive = true; Response response = await ApiClient().getJsonForResponse( - "${ApiConsts.chatServerBaseApiUrl}${ApiConsts.chatSingleUserHistoryUrl}/$senderUID/$receiverUID/$pagination", + "${ApiConsts.chatServerBaseApiUrl}${ApiConsts.chatSingleUserHistoryUrl}/$senderUID/$receiverUID/$paginationVal", token: AppState().chatDetails!.response!.token, ); - logger.d(response.statusCode); - print(response.body); if (response.statusCode == 204) { - userChatHistory = []; + if (isNewChat) { + userChatHistory = []; + } else if (loadMore) { + // userChatHistory = []; + Utils.showToast("No More Data To Load"); + } } else { - userChatHistory = getSingleUserChatModel(response.body); + if (loadMore) { + List temp = getSingleUserChatModel( + response.body, + ).reversed.toList(); + userChatHistory.addAll( + temp, + ); + } else { + userChatHistory = getSingleUserChatModel( + response.body, + ).reversed.toList(); + } } + await getUserChatHistoryNotDeliveredAsync( + userId: senderUID, + ); isLoading = false; notifyListeners(); + markRead( + userChatHistory, + receiverUID, + ); + generateConvId(); + } + + void generateConvId() async { + Uuid uuid = const Uuid(); + chatCID = uuid.v4(); + } + + void markRead(List data, reciverID) { + for (SingleUserChatModel element in data!) { + if (!element.isSeen!) { + dynamic data = [ + { + "userChatHistoryId": element.userChatHistoryId, + "TargetUserId": element.targetUserId, + "isDelivered": true, + "isSeen": true, + } + ]; + updateUserChatHistoryStatusAsync(data); + } + } + for (ChatUser element in searchedChats!) { + if (element.id == reciverID) { + element.unreadMessageCount = 0; + notifyListeners(); + } + } + } + + void updateUserChatHistoryStatusAsync(List data) { + hubConnection.invoke( + "UpdateUserChatHistoryStatusAsync", + args: [data], + ); } - List getSingleUserChatModel(String str) => List.from(json.decode(str).map((x) => SingleUserChatModel.fromJson(x))); + List getSingleUserChatModel(String str) => List.from( + json.decode(str).map( + (x) => SingleUserChatModel.fromJson(x), + ), + ); - ChatUserModel userToList(String str) => ChatUserModel.fromJson(json.decode(str)); + ChatUserModel userToList(String str) => ChatUserModel.fromJson( + json.decode(str), + ); + + Future uploadAttachments(String userId, File file) async { + dynamic result; + dynamic request = MultipartRequest( + 'POST', + Uri.parse( + '${ApiConsts.chatServerBaseApiUrl}${ApiConsts.chatMediaImageUploadUrl}', + ), + ); + request.fields.addAll({'userId': userId, 'fileSource': '1'}); + request.files.add(await MultipartFile.fromPath('files', file.path)); + request.headers.addAll( + { + 'Authorization': 'Bearer ${AppState().chatDetails!.response!.token}', + }, + ); + try { + StreamedResponse response = await request.send(); + if (response.statusCode == 200) { + result = jsonDecode( + await response.stream.bytesToString(), + ); + } else { + result = []; + } + } catch (e) { + if (kDebugMode) { + print(e); + } + } + ; + return result; + } Future buildHubConnection() async { - HttpConnectionOptions httpOp = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true); - hubConnection = await HubConnectionBuilder() - .withUrl(ApiConsts.chatHubConnectionUrl + "?UserId=${AppState().chatDetails!.response!.id}&source=Web&access_token=${AppState().chatDetails!.response!.token}", options: httpOp) - .withAutomaticReconnect(retryDelays: [2000, 5000, 10000, 20000]) + HttpConnectionOptions httpOp = HttpConnectionOptions( + skipNegotiation: false, + logMessageContent: true, + ); + hubConnection = HubConnectionBuilder() + .withUrl( + ApiConsts.chatHubConnectionUrl + "?UserId=${AppState().chatDetails!.response!.id}&source=Web&access_token=${AppState().chatDetails!.response!.token}", + options: httpOp, + ) + .withAutomaticReconnect( + retryDelays: [2000, 5000, 10000, 20000], + ) .configureLogging( - Logger("Logs Enabled"), + Logger("Loggin"), ) .build(); hubConnection.onclose( - ({Exception? error}) { - // logger.d(error); - }, + ({Exception? error}) {}, ); hubConnection.onreconnecting( - ({Exception? error}) { - // logger.d(error); - // logger.d("Reconnecting"); - }, + ({Exception? error}) {}, ); hubConnection.onreconnected( - ({String? connectionId}) { - // logger.d("Reconnected"); - }, + ({String? connectionId}) {}, ); if (hubConnection.state != HubConnectionState.Connected) { await hubConnection.start(); + getUserChatHistoryNotDeliveredAsync( + userId: int.parse( + AppState().chatDetails!.response!.id.toString(), + ), + ); hubConnection.on("OnUpdateUserStatusAsync", changeStatus); hubConnection.on("OnDeliveredChatUserAsync", onMsgReceived); + // hubConnection.on("OnSeenChatUserAsync", onChatSeen); - hubConnection.on("OnUserTypingAsync", onUserTyping); - // hubConnection.on("OnUserCountAsync", userCountAsync); - // hubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow); - // hubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered); - hubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus); - } else { - hubConnection.on("OnUpdateUserStatusAsync", changeStatus); - hubConnection.on("OnDeliveredChatUserAsync", onMsgReceived); - - hubConnection.on("OnUserTypingAsync", onUserTyping); - // hubConnection.on("OnUserCountAsync", userCountAsync); - // hubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow); - // hubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered); + //hubConnection.on("OnUserTypingAsync", onUserTyping); + hubConnection.on("OnUserCountAsync", userCountAsync); + hubConnection.on("OnUpdateUserChatHistoryWindowsAsync", updateChatHistoryWindow); + hubConnection.on("OnGetUserChatHistoryNotDeliveredAsync", chatNotDelivered); hubConnection.on("OnUpdateUserChatHistoryStatusAsync", updateUserChatStatus); } - isLoading = false; - notifyListeners(); } void updateUserChatStatus(List? args) { @@ -134,7 +295,6 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { for (dynamic cItem in items[0]) { for (SingleUserChatModel chat in userChatHistory) { if (chat.userChatHistoryId.toString() == cItem["userChatHistoryId"].toString()) { - logger.d(jsonEncode(chat)); chat.isSeen = cItem["isSeen"]; chat.isDelivered = cItem["isDelivered"]; notifyListeners(); @@ -143,9 +303,9 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } } - void userCountAsync(List? args) { - List items = args!.toList(); - print("---------------------------------User Count Async -------------------------------------"); + void onChatSeen(List? args) { + dynamic items = args!.toList(); + logger.d("---------------------------------Chat Seen -------------------------------------"); logger.d(items); // for (var user in searchedChats!) { // if (user.id == items.first["id"]) { @@ -155,10 +315,11 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { // notifyListeners(); } - void updateChatHistoryWindow(List? args) { - List items = args!.toList(); - print("---------------------------------Update Chat History Windows Async -------------------------------------"); - logger.d(items); + void userCountAsync(List? args) { + dynamic items = args!.toList(); + // logger.d(items); + //logger.d("---------------------------------User Count Async -------------------------------------"); + //logger.d(items); // for (var user in searchedChats!) { // if (user.id == items.first["id"]) { // user.userStatus = items.first["userStatus"]; @@ -167,9 +328,9 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { // notifyListeners(); } - void chatNotDelivered(List? args) { - List items = args!.toList(); - print("--------------------------------- Chat Not Delivered Windows Async -------------------------------------"); + void updateChatHistoryWindow(List? args) { + dynamic items = args!.toList(); + print("---------------------------------Update Chat History Windows Async -------------------------------------"); logger.d(items); // for (var user in searchedChats!) { // if (user.id == items.first["id"]) { @@ -179,9 +340,33 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { // notifyListeners(); } + void chatNotDelivered(List? args) { + dynamic items = args!.toList(); + for (dynamic item in items[0]) { + searchedChats!.forEach((element) { + if (element.id == item["currentUserId"]) { + var val = element.unreadMessageCount == null ? 0 : element.unreadMessageCount; + element.unreadMessageCount = val! + 1; + } + }); + // dynamic data = [ + // { + // "userChatHistoryId": item["userChatHistoryId"], + // "TargetUserId": item["targetUserId"], + // "isDelivered": true, + // "isSeen": true, + // } + // ]; + // updateUserChatHistoryStatusAsync(data); + } + notifyListeners(); + } + void changeStatus(List? args) { - // print("================= Status Online // Offline ===================="); - List items = args!.toList(); + if (kDebugMode) { + // print("================= Status Online // Offline ===================="); + } + dynamic items = args!.toList(); // logger.d(items); for (ChatUser user in searchedChats!) { if (user.id == items.first["id"]) { @@ -217,18 +402,34 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { data.first.currentUserId = temp.first.targetUserId; data.first.currentUserName = temp.first.targetUserName; } - userChatHistory.add(data.first); + userChatHistory.insert(0, data.first); + // searchedChats!.forEach((element) { + // if (element.id == data.first.currentUserId) { + // var val = element.unreadMessageCount == null ? 0 : element.unreadMessageCount; + // element.unreadMessageCount = val! + 1; + // } + // }); + + var list = [ + { + "userChatHistoryId": data.first.userChatHistoryId, + "TargetUserId": data.first.targetUserId, + "isDelivered": true, + "isSeen": isChatScreenActive ? true : false, + } + ]; + updateUserChatHistoryStatusAsync(list); + notifyListeners(); - scrollDown(); + // if (isChatScreenActive) scrollToBottom(); } void onUserTyping(List? parameters) { - print("==================== Typing Active =================="); - logger.d(parameters); + // print("==================== Typing Active =================="); + // logger.d(parameters); for (ChatUser user in searchedChats!) { if (user.id == parameters![1] && parameters[0] == true) { user.isTyping = parameters[0] as bool?; - Future.delayed( const Duration(seconds: 2), () { @@ -241,12 +442,115 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { notifyListeners(); } - void sendChatMessage(String chatMessage, int targetUserId, String targetUserName) async { - if (chatMessage == null || chatMessage.isEmpty) { - return; + int getFileType(String value) { + switch (value) { + case ".pdf": + return 1; + case ".png": + return 3; + case ".txt": + return 5; + case ".jpg": + return 12; + case ".jpeg": + return 4; + case ".xls": + return 7; + case ".xlsx": + return 7; + case ".doc": + return 6; + case ".docx": + return 6; + case ".ppt": + return 8; + case ".pptx": + return 8; + case ".zip": + return 2; + case ".rar": + return 2; + default: + return 0; } + } - var contain = searchedChats!.where((ChatUser element) => element.id == targetUserId); + String getFileTypeDescription(String value) { + switch (value) { + case ".pdf": + return "application/pdf"; + case ".png": + return "image/png"; + case ".txt": + return "text/plain"; + case ".jpg": + return "image/jpg"; + case ".jpeg": + return "image/jpeg"; + case ".ppt": + return "application/vnd.openxmlformats-officedocument.presentationml.presentation"; + case ".pptx": + return "application/vnd.openxmlformats-officedocument.presentationml.presentation"; + case ".doc": + return "application/vnd.openxmlformats-officedocument.wordprocessingm"; + case ".docx": + return "application/vnd.openxmlformats-officedocument.wordprocessingm"; + case ".xls": + return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + case ".xlsx": + return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + case ".zip": + return "application/octet-stream"; + case ".rar": + return "application/octet-stream"; + default: + return ""; + } + } + + Future sendChatToServer( + {required int chatEventId, required fileTypeId, required int targetUserId, required String targetUserName, required chatReplyId, required bool isAttachment, required bool isReply}) async { + Uuid uuid = const Uuid(); + + var msg = message.text; + SingleUserChatModel data = SingleUserChatModel( + chatEventId: chatEventId, + chatSource: 1, + contant: msg, + contantNo: uuid.v4(), + conversationId: chatCID, + createdDate: DateTime.now(), + currentUserId: AppState().chatDetails!.response!.id, + currentUserName: AppState().chatDetails!.response!.userName, + targetUserId: targetUserId, + targetUserName: targetUserName, + isReplied: false, + fileTypeId: fileTypeId, + userChatReplyResponse: isReply ? UserChatReplyResponse.fromJson(repliedMsg.first.toJson()) : null, + fileTypeResponse: isAttachment + ? FileTypeResponse( + fileTypeId: fileTypeId, + fileTypeName: getFileType(getFileExtension(selectedFile.path).toString()), + fileKind: getFileExtension(selectedFile.path), + fileName: selectedFile.path.split("/").last, + fileTypeDescription: getFileTypeDescription(getFileExtension(selectedFile.path).toString()), + ) + : null, + ); + userChatHistory.insert(0, data); + isFileSelected = false; + isMsgReply = false; + sFileType = ""; + message.clear(); + notifyListeners(); + + String chatData = + '{"contant":"$msg","contantNo":"${uuid.v4()}","chatEventId":$chatEventId,"fileTypeId": $fileTypeId,"currentUserId":${AppState().chatDetails!.response!.id},"chatSource":1,"userChatHistoryLineRequestList":[{"isSeen":false,"isDelivered":false,"targetUserId":$targetUserId,"targetUserStatus":1}],"chatReplyId":$chatReplyId,"conversationId":"$chatCID"}'; + await hubConnection.invoke("AddChatUserAsync", args: [json.decode(chatData)]); + } + + void sendChatMessage(int targetUserId, String targetUserName, BuildContext context) async { + dynamic contain = searchedChats!.where((ChatUser element) => element.id == targetUserId); if (contain.isEmpty) { searchedChats!.add( ChatUser( @@ -254,51 +558,219 @@ class ChatProviderModel with ChangeNotifier, DiagnosticableTreeMixin { userName: targetUserName, ), ); + notifyListeners(); } - - String chatData = - '{"contant":"$chatMessage","contantNo":"8a129295-36d7-7185-5d34-cc4eec7bcba4","chatEventId":1,"fileTypeId":null,"currentUserId":${AppState().chatDetails!.response!.id},"chatSource":1,"userChatHistoryLineRequestList":[{"isSeen":false,"isDelivered":false,"targetUserId":$targetUserId,"targetUserStatus":1}],"conversationId":"715f8b13-96ee-cd36-cb07-5a982a219982"}'; - await hubConnection.invoke("AddChatUserAsync", args: [json.decode(chatData)]); - userChatHistory.add( - SingleUserChatModel( - chatEventId: 1, - chatSource: 1, - contant: chatMessage, - contantNo: "8a129295-36d7-7185-5d34-cc4eec7bcba4", - conversationId: "715f8b13-96ee-cd36-cb07-5a982a219982", - createdDate: DateTime.now(), - currentUserId: AppState().chatDetails!.response!.id, - currentUserName: AppState().chatDetails!.response!.userName, + if (!isFileSelected && !isMsgReply) { + logger.d("Normal Text Message"); + if (message.text == null || message.text.isEmpty) { + return; + } + sendChatToServer(chatEventId: 1, fileTypeId: null, targetUserId: targetUserId, targetUserName: targetUserName, isAttachment: false, chatReplyId: null, isReply: false); + } + if (isFileSelected && !isMsgReply) { + Utils.showLoading(context); + //logger.d("Normal Attachment Message"); + dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), selectedFile); + String? ext = getFileExtension(selectedFile.path); + Utils.hideLoading(context); + sendChatToServer(chatEventId: 2, fileTypeId: getFileType(ext.toString()), targetUserId: targetUserId, targetUserName: targetUserName, isAttachment: true, chatReplyId: null, isReply: false); + } + if (!isFileSelected && isMsgReply) { + // logger.d("Normal Text Message With Reply"); + if (message.text == null || message.text.isEmpty) { + return; + } + sendChatToServer( + chatEventId: 1, fileTypeId: null, targetUserId: targetUserId, targetUserName: targetUserName, chatReplyId: repliedMsg.first.userChatHistoryId, isAttachment: false, isReply: true); + } + if (isFileSelected && isMsgReply) { + // logger.d("Attachment Message With Reply"); + Utils.showLoading(context); + dynamic value = await uploadAttachments(AppState().chatDetails!.response!.id.toString(), selectedFile); + String? ext = getFileExtension(selectedFile.path); + Utils.hideLoading(context); + sendChatToServer( + chatEventId: 2, + fileTypeId: getFileType(ext.toString()), targetUserId: targetUserId, targetUserName: targetUserName, - ), - ); - message.clear(); + isAttachment: true, + chatReplyId: repliedMsg.first.userChatHistoryId, + isReply: true, + ); + } + } + + void selectImageToUpload(BuildContext context) { + ImageOptions.showImageOptionsNew(context, true, (String image, File file) async { + if (checkFileSize(file.path)) { + selectedFile = file; + isFileSelected = true; + sFileType = getFileExtension(file.path)!; + message.text = file.path.split("/").last; + Navigator.of(context).pop(); + } else { + Utils.showToast("Max 1 mb size is allowed to upload"); + } + notifyListeners(); + }); + } + + void removeAttachment() { + isFileSelected = false; + sFileType = ""; + message.text = ''; notifyListeners(); - scrollDown(); } - void scrollDown() { - scrollController.animateTo( - scrollController.position.maxScrollExtent + 100, - curve: Curves.easeOut, - duration: const Duration(milliseconds: 300), - ); + String? getFileExtension(String fileName) { + try { + return "." + fileName.split('.').last; + } catch (e) { + return null; + } + } + + bool checkFileSize(String path) { + int fileSizeLimit = 1024; + File f = File(path); + double fileSizeInKB = f.lengthSync() / 1024; + double fileSizeInMB = fileSizeInKB / 1024; + if (fileSizeInKB > fileSizeLimit) { + return false; + } else { + return true; + } + } + + String getType(String type) { + switch (type) { + case ".pdf": + return "assets/images/pdf.svg"; + case ".png": + return "assets/images/png.svg"; + case ".txt": + return "assets/icons/chat/txt.svg"; + case ".jpg": + return "assets/images/jpg.svg"; + case ".jpeg": + return "assets/images/jpg.svg"; + case ".xls": + return "assets/icons/chat/xls.svg"; + case ".xlsx": + return "assets/icons/chat/xls.svg"; + case ".doc": + return "assets/icons/chat/doc.svg"; + case ".docx": + return "assets/icons/chat/doc.svg"; + case ".ppt": + return "assets/icons/chat/ppt.svg"; + case ".pptx": + return "assets/icons/chat/ppt.svg"; + case ".zip": + return "assets/icons/chat/zip.svg"; + case ".rar": + return "assets/icons/chat/zip.svg"; + default: + return "assets/images/thumb.svg"; + } + } + + void chatReply(SingleUserChatModel data) { + repliedMsg = []; + data.isReplied = true; + isMsgReply = true; + repliedMsg.add(data); notifyListeners(); } -// void _scrollListener() { -// if (scrollController.position.extentAfter.toInt() <= 0 && canCallApi) { -// if (userChatHistory.length < _ayatTangheemTypeMapped.totalItemsCount) { -// currentPageNo++; -// if (widget.tangheemQuery == null) { -// getTangheemData(); -// } else { -// getTangheemDataByKeyword(); -// } -// } -// canCallApi = false; -// } -// } + void closeMe() { + repliedMsg = []; + isMsgReply = false; + notifyListeners(); + } + String dateFormte(DateTime data) { + DateFormat f = DateFormat('hh:mm a dd MMM yyyy'); + f.format(data); + return f.format(data); + } + + Future favoriteUser({required int userID, required int targetUserID}) async { + Response response = + await ApiClient().postJsonForResponse("${ApiConsts.chatServerBaseApiUrl}FavUser/addFavUser", {"targetUserId": targetUserID, "userId": userID}, token: AppState().chatDetails!.response!.token); + fav.FavoriteChatUser favoriteChatUser = fav.FavoriteChatUser.fromRawJson(response.body); + if (favoriteChatUser.response != null) { + for (ChatUser user in searchedChats!) { + if (user.id == favoriteChatUser.response!.targetUserId!) { + user.isFav = favoriteChatUser.response!.isFav; + favUsersList.add(user); + } + } + } + notifyListeners(); + } + + Future unFavoriteUser({required int userID, required int targetUserID}) async { + Response response = await ApiClient() + .postJsonForResponse("${ApiConsts.chatServerBaseApiUrl}FavUser/deleteFavUser", {"targetUserId": targetUserID, "userId": userID}, token: AppState().chatDetails!.response!.token); + fav.FavoriteChatUser favoriteChatUser = fav.FavoriteChatUser.fromRawJson(response.body); + if (favoriteChatUser.response != null) { + for (var user in searchedChats!) { + if (user.id == favoriteChatUser.response!.targetUserId!) { + user.isFav = favoriteChatUser.response!.isFav; + } + } + favUsersList.removeWhere((ChatUser element) => element.id == targetUserID); + } + notifyListeners(); + } + + void clearSelections() { + searchedChats = pChatHistory; + search.clear(); + isChatScreenActive = false; + paginationVal = 0; + message.text = ''; + isFileSelected = false; + repliedMsg = []; + sFileType = ""; + notifyListeners(); + } + + void clearAll() { + searchedChats = pChatHistory; + search.clear(); + isChatScreenActive = false; + paginationVal = 0; + message.text = ''; + isFileSelected = false; + repliedMsg = []; + sFileType = ""; + } + + // void scrollListener() { + // _firstAutoscrollExecuted = true; + // if (scrollController.hasClients && scrollController.position.pixels == scrollController.position.maxScrollExtent) { + // _shouldAutoscroll = true; + // } else { + // _shouldAutoscroll = false; + // } + // } + // + // void scrollToBottom() { + // scrollController.animateTo( + // scrollController.position.maxScrollExtent + 100, + // duration: const Duration(milliseconds: 500), + // curve: Curves.easeIn, + // ); + // } + + void msgScroll() { + scrollController.animateTo( + scrollController.position.minScrollExtent - 100, + duration: const Duration(milliseconds: 500), + curve: Curves.easeIn, + ); + } } diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index cce2207..9747e5c 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -1,10 +1,12 @@ import 'dart:async'; import 'dart:convert'; +import 'package:http/http.dart'; import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/date_uitl.dart'; +import 'package:mohem_flutter_app/models/chat/chat_count_conversation_model.dart'; import 'package:mohem_flutter_app/models/dashboard/get_accrual_balances_list_model.dart'; import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart'; import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; @@ -15,7 +17,6 @@ import 'package:mohem_flutter_app/models/itg/itg_response_model.dart'; import 'package:uuid/uuid.dart'; - class DashboardApiClient { static final DashboardApiClient _instance = DashboardApiClient._internal(); @@ -178,4 +179,11 @@ class DashboardApiClient { return responseData; }, url, postParams); } + + Future getChatCount() async { + Response response = await ApiClient().getJsonForResponse( + "${ApiConsts.chatServerBaseApiUrl}user/unreadconversationcount/${AppState().getUserName}", + ); + return chatUnreadCovnCountModelFromJson(response.body); + } } diff --git a/lib/api/my_attendance_api_client.dart b/lib/api/my_attendance_api_client.dart index eb66a8b..c11885c 100644 --- a/lib/api/my_attendance_api_client.dart +++ b/lib/api/my_attendance_api_client.dart @@ -9,6 +9,7 @@ import 'package:mohem_flutter_app/models/get_eit_dff_structure_list_model.dart'; import 'package:mohem_flutter_app/models/get_eit_transaction_list_model.dart'; import 'package:mohem_flutter_app/models/start_eit_approval_process_model.dart'; import 'package:mohem_flutter_app/models/submit_eit_transaction_list_model.dart'; +import 'package:mohem_flutter_app/models/worklist/resubmit_eit_response_model.dart'; class MyAttendanceApiClient { static final MyAttendanceApiClient _instance = MyAttendanceApiClient._internal(); @@ -17,47 +18,59 @@ class MyAttendanceApiClient { factory MyAttendanceApiClient() => _instance; - Future?> getEitTransaction(String pFunctionName) async { + Future?> getEitTransaction(String pFunctionName, String? empID) async { String url = "${ApiConsts.erpRest}GET_EIT_TRANSACTIONS"; Map postParams = {"P_PAGE_LIMIT": 50, "P_PAGE_NUM": 1, "P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E", "P_FUNCTION_NAME": pFunctionName}; postParams.addAll(AppState().postParamsJson); + // postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + if('P_SELECTED_EMPLOYEE_NUMBER'.isNotEmpty){ + postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + // AppState().postParamsJson['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + print(empID); + } return await ApiClient().postJsonForObject((json) { GenericResponseModel? responseData = GenericResponseModel.fromJson(json); return responseData.getEITTransactionList ?? []; }, url, postParams); } - Future getEitDffStructure(String pFunctionName) async { + Future getEitDffStructure(String pFunctionName, String? empID) async { String url = "${ApiConsts.erpRest}GET_EIT_DFF_STRUCTURE"; Map postParams = {"P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E", "P_FUNCTION_NAME": pFunctionName}; postParams.addAll(AppState().postParamsJson); + if('P_SELECTED_EMPLOYEE_NUMBER'.isNotEmpty) { + postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + } return await ApiClient().postJsonForObject((json) { GenericResponseModel? responseData = GenericResponseModel.fromJson(json); return responseData; }, url, postParams); } - Future> getValueSetValues(String pSegmentName, String pDescFlexContextCode, String pDescFlexName, List> list) async { + Future> getValueSetValues(String pSegmentName, String pDescFlexContextCode, String pDescFlexName, List> list,{ String? empID, String? parentValue}) async { String url = "${ApiConsts.erpRest}GET_VALUE_SET_VALUES"; Map postParams = { "P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E", "P_PAGE_LIMIT": 1000, "P_PAGE_NUM": 1, - "P_PARENT_VALUE": null, + "P_PARENT_VALUE": empID!.isNotEmpty? parentValue : null, "P_SEGMENT_NAME": pSegmentName, "P_DESC_FLEX_CONTEXT_CODE": pDescFlexContextCode, "P_DESC_FLEX_NAME": pDescFlexName, "GetValueSetValuesTBL": list, }; postParams.addAll(AppState().postParamsJson); + if('P_SELECTED_EMPLOYEE_NUMBER'.isNotEmpty) { + postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + } return await ApiClient().postJsonForObject((json) { GenericResponseModel? responseData = GenericResponseModel.fromJson(json); return responseData.getValueSetValuesList ?? []; }, url, postParams); } - Future getDefaultValue(String pSegmentName, String pDescFlexContextCode, String pDescFlexName, List> list) async { + Future getDefaultValue(String pSegmentName, String pDescFlexContextCode, String pDescFlexName, List> list, String? empID) async { String url = "${ApiConsts.erpRest}GET_DEFAULT_VALUE"; Map postParams = { "P_SELECTED_RESP_ID": -999, @@ -69,13 +82,16 @@ class MyAttendanceApiClient { "GetValueSetValuesTBL": list, }; postParams.addAll(AppState().postParamsJson); + if('P_SELECTED_EMPLOYEE_NUMBER'.isNotEmpty) { + postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + } return await ApiClient().postJsonForObject((json) { GenericResponseModel? responseData = GenericResponseModel.fromJson(json); return ESERVICESDV.fromJson(responseData.getDefaultValueList!.toJson()); }, url, postParams); } - Future validateEitTransaction(String pDescFlexContextCode, String pFunctionName, List> list) async { + Future validateEitTransaction(String pDescFlexContextCode, String pFunctionName, List> list, { String? empID}) async { String url = "${ApiConsts.erpRest}VALIDATE_EIT_TRANSACTION"; Map postParams = { "P_SELECTED_RESP_ID": -999, @@ -85,6 +101,9 @@ class MyAttendanceApiClient { "EITTransactionTBL": list, }; postParams.addAll(AppState().postParamsJson); + if('P_SELECTED_EMPLOYEE_NUMBER'.isNotEmpty) { + postParams['P_SELECTED_EMPLOYEE_NUMBER'] = empID; + } return await ApiClient().postJsonForObject((json) { GenericResponseModel? responseData = GenericResponseModel.fromJson(json); return responseData; //ESERVICESDV.fromJson(responseData.getDefaultValueList!.toJson()); @@ -108,6 +127,21 @@ class MyAttendanceApiClient { }, url, postParams); } + Future reSubmitEitTransaction(String itemKey, var notifID, List> list) async { + String url = "${ApiConsts.erpRest}RESUBMIT_EIT_TRANSACTION"; + Map postParams = { + "P_NOTIFICATION_ID": notifID, + "P_ITEM_KEY": itemKey, + "P_EMAIL_ADDRESS": AppState().memberInformationList!.eMPLOYEEEMAILADDRESS, + "EITTransactionTBL": list + }; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + return responseData.resubmitEITTransactionList!; //ESERVICESDV.fromJson(responseData.getDefaultValueList!.toJson()); + }, url, postParams); + } + Future> getApproversList(String pAmeTransactionType, int pTransactionId) async { String url = "${ApiConsts.erpRest}GET_APPROVERS_LIST"; Map postParams = { diff --git a/lib/api/profile_api_client.dart b/lib/api/profile_api_client.dart index d7ceba0..210d179 100644 --- a/lib/api/profile_api_client.dart +++ b/lib/api/profile_api_client.dart @@ -293,7 +293,7 @@ class ProfileApiClient { "P_COUNTRY_CODE": countryCode, //"SA", "P_DESC_FLEX_CONTEXT_CODE": "SA", "P_FUNCTION_NAME": pFunctionName, - "P_ACTION": correctOrNew == 1 ? 'CORRECT' : 'NEW', + "P_ACTION": correctOrNew == 1 ? 'CORRECT' : 'CHANGE', "P_EFFECTIVE_DATE": effectiveDate, //"EITTransactionTBL": list, diff --git a/lib/api/worklist/worklist_api_client.dart b/lib/api/worklist/worklist_api_client.dart index 1b00383..75992e7 100644 --- a/lib/api/worklist/worklist_api_client.dart +++ b/lib/api/worklist/worklist_api_client.dart @@ -23,6 +23,7 @@ import 'package:mohem_flutter_app/models/itg_forms_models/itg_request_model.dart import 'package:mohem_flutter_app/models/member_information_list_model.dart'; import 'package:mohem_flutter_app/models/notification_get_respond_attributes_list_model.dart'; import 'package:mohem_flutter_app/models/update_user_item_type_list.dart'; +import 'package:mohem_flutter_app/models/worklist/GetRFCEmployeeList.dart'; import 'package:mohem_flutter_app/models/worklist/get_favorite_replacements_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/eit_otification_body_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/get_basic_det_ntf_body_list_model.dart'; @@ -88,6 +89,16 @@ class WorkListApiClient { }, url, postParams); } + Future> getRFCEmployeeeList(int pNotificationID) async { + String url = "${ApiConsts.erpRest}GET_RFC_EMPLOYEE_LIST"; + Map postParams = {"P_NOTIFICATION_ID": pNotificationID, "P_PAGE_NUM": 1, "P_PAGE_LIMIT": 10}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + return responseData.getRFCEmployeeListList ?? []; + }, url, postParams); + } + Future> getActionHistory(int pNotificationID) async { String url = "${ApiConsts.erpRest}GET_ACTION_HISTORY"; Map postParams = { @@ -357,7 +368,7 @@ class WorkListApiClient { }, url, postParams); } - Future delegateITGRequest(String requestType, int taskId, int itemId, String employeeNumber,String newUserEMPId, String comments) async { + Future delegateITGRequest(String requestType, int taskId, int itemId, String employeeNumber, String newUserEMPId, String comments) async { String url = "${ApiConsts.cocRest}ITGDelegateRequest"; Map postParams = { "RequestType": requestType, @@ -373,7 +384,44 @@ class WorkListApiClient { return responseData.itgRequest; }, url, postParams); } - Future informationITGRequest(String requestType, int taskId, int itemId, String employeeNumber,String newUserEMPId, String comments) async { + + // ANSWER = 'Services/COCWS.svc/REST/ITGAnswer'; + + Future answerITGRequest(String requestType, int taskId, int itemId, String employeeNumber, String newUserEMPId, String comments) async { + String url = "${ApiConsts.cocRest}ITGAnswer"; + Map postParams = { + "RequestType": requestType, + "TaskID": taskId, + "ItemID": itemId, + "EmployeeNumber": employeeNumber, + "Comments": comments, + }; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + ItgFormsModel responseData = ItgFormsModel.fromJson(json); + return responseData.itgRequest; + }, url, postParams); + } + + Future grantITGRequest(String requestType, int taskId, int itemId, String employeeNumber, String newUserEMPId, String comments) async { + String url = "${ApiConsts.cocRest}ITGGrantAccess"; + Map postParams = { + "RequestType": requestType, + "TaskID": taskId, + "ItemID": itemId, + "EmployeeNumber": employeeNumber, + "Comments": "", + "AdditionalFields": null, + "NewUserEMPId":newUserEMPId + }; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + ItgFormsModel responseData = ItgFormsModel.fromJson(json); + return responseData.itgRequest; + }, url, postParams); + } + + Future informationITGRequest(String requestType, int taskId, int itemId, String employeeNumber, String newUserEMPId, String comments) async { String url = "${ApiConsts.cocRest}ITGRequestInformation"; Map postParams = { "RequestType": requestType, @@ -481,6 +529,16 @@ class WorkListApiClient { }, url, postParams); } + Future?> getFavoriteReplacementWithImageNew(String selectedFavLetter) async { + String url = "${ApiConsts.erpRest}Mohemm_GetFavoriteReplacements_Images"; + Map postParams = {"ItgFilter": selectedFavLetter}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + return responseData.mohemmGetFavoriteReplacementsList; + }, url, postParams); + } + Future changeFavoriteReplacements({required String email, required String employeName, required String image, required String userName, bool isFav = false}) async { String url = "${ApiConsts.erpRest}Mohemm_ChangeFavoriteReplacements"; Map postParamsObj = { @@ -516,7 +574,7 @@ class WorkListApiClient { }, url, postParams); } - Future submitComment({String? comment, String? email, String? userId, int? notificationId, required String apiMode}) async { + Future submitComment({String? comment, String? email, String? userId, int? notificationId, required String apiMode, int? approverIndex = null}) async { String url = "${ApiConsts.erpRest}NOTIFICATION_ACTIONS"; Map postParams = { "P_COMMENTS": comment, @@ -524,7 +582,7 @@ class WorkListApiClient { "P_EMAIL_ADDRESS": email, "P_FORWARD_TO_USER_NAME": userId, "P_NOTIFICATION_ID": notificationId, - "P_APPROVER_INDEX": null, + "P_APPROVER_INDEX": approverIndex, "RespondAttributeList": [] }; postParams.addAll(AppState().postParamsJson); diff --git a/lib/app_state/app_state.dart b/lib/app_state/app_state.dart index d0a8699..b0620ad 100644 --- a/lib/app_state/app_state.dart +++ b/lib/app_state/app_state.dart @@ -132,9 +132,19 @@ class AppState { set setItgWorkListIndex(int? _itgWorkListIndex) => itgWorkListIndex = _itgWorkListIndex; + String? itgRequestType; + + set setItgRequestType(String? _itgRequestType) => itgRequestType = _itgRequestType; + + String? get getItgRequestType => itgRequestType; + UserAutoLoginModel? chatDetails; set setchatUserDetails(UserAutoLoginModel details) => chatDetails = details; UserAutoLoginModel? get getchatUserDetails => chatDetails; + + String? _base64ImageEmp = "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"; + + String? get getBase64ImageEmp => _base64ImageEmp; } diff --git a/lib/classes/colors.dart b/lib/classes/colors.dart index b77e7b1..f99cc31 100644 --- a/lib/classes/colors.dart +++ b/lib/classes/colors.dart @@ -60,4 +60,5 @@ class MyColors { static const Color grey35Color = Color(0xff535353); static const Color grey9DColor = Color(0xff9D9D9D); static const Color darkDigitColor = Color(0xff2D2F39); + static const Color grey71Color = Color(0xff717171); } diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index bcf5add..6c24049 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -1,7 +1,7 @@ class ApiConsts { //static String baseUrl = "http://10.200.204.20:2801/"; // Local server - static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server - // static String baseUrl = "https://hmgwebservices.com"; // Live server + //static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server + static String baseUrl = "https://hmgwebservices.com"; // Live server static String baseUrlServices = baseUrl + "/Services/"; // server // static String baseUrlServices = "https://api.cssynapses.com/tangheem/"; // Live server static String utilitiesRest = baseUrlServices + "Utilities.svc/REST/"; @@ -10,12 +10,15 @@ class ApiConsts { static String user = baseUrlServices + "api/User/"; static String cocRest = baseUrlServices + "COCWS.svc/REST/"; + //Chat static String chatServerBaseUrl = "https://apiderichat.hmg.com"; static String chatServerBaseApiUrl = "https://apiderichat.hmg.com/api/"; static String chatHubConnectionUrl = chatServerBaseUrl + "/ConnectionChatHub"; static String chatSearchMember = "user/getUserWithStatusAndFavAsync/"; static String chatRecentUrl = "UserChatHistory/getchathistorybyuserid"; //For a Mem static String chatSingleUserHistoryUrl = "UserChatHistory/GetUserChatHistory"; + static String chatMediaImageUploadUrl = "shared/upload"; + static String chatFavoriteUsers = "FavUser/getFavUserById/"; // 42062 is CurrentUserID and 36745 is targetUserID and 0 is For Pagination // static String chatSearchMember = "https://apiderichat.hmg.com/api/user/getUserWithStatusAndFavAsync/aamir.muhammad/36239"; diff --git a/lib/classes/date_uitl.dart b/lib/classes/date_uitl.dart index 44b85c3..a1e52d9 100644 --- a/lib/classes/date_uitl.dart +++ b/lib/classes/date_uitl.dart @@ -402,6 +402,30 @@ class DateUtil { return ""; } + static String formatDuration(Duration d) { + var seconds = d.inSeconds; + var days = seconds ~/ Duration.secondsPerDay; + seconds -= days * Duration.secondsPerDay; + var hours = seconds ~/ Duration.secondsPerHour; + seconds -= hours * Duration.secondsPerHour; + var minutes = seconds ~/ Duration.secondsPerMinute; + seconds -= minutes * Duration.secondsPerMinute; + + List tokens = []; + if (days != 0) { + tokens.add('$days days'); + } + if (tokens.isNotEmpty || hours != 0) { + tokens.add('$hours hours'); + } + if (tokens.isNotEmpty || minutes != 0) { + tokens.add('$minutes mins'); + } + tokens.add('$seconds secs'); + + return tokens.join(' '); + } + /// get data formatted like 26/4/2020 /// [dateTime] convert DateTime to data formatted according to language static String getDayMonthYearDateFormattedLang(DateTime dateTime, bool isArabic) { diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 1fcbf2e..ab3fb9a 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -5,6 +5,7 @@ import 'package:mohem_flutter_app/ui/attendance/vacation_rule_screen.dart'; import 'package:mohem_flutter_app/ui/bottom_sheets/attendence_details_bottom_sheet.dart'; import 'package:mohem_flutter_app/ui/chat/chat_detailed_screen.dart'; import 'package:mohem_flutter_app/ui/chat/chat_home.dart'; +import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/ui/landing/itg/survey_screen.dart'; import 'package:mohem_flutter_app/ui/landing/today_attendance_screen.dart'; @@ -177,6 +178,7 @@ class AppRoutes { //Chat static const String chat = "/chat"; static const String chatDetailed = "/chatDetailed"; + static const String chatFavoriteUsers = "/chatFavoriteUsers"; //Marathon static const String marathonIntroScreen = "/marathonIntroScreen"; @@ -287,8 +289,9 @@ class AppRoutes { changePassword: (BuildContext context) => ChangePasswordScreen(), //Chat - chat: (BuildContext context) => ChatHomeScreen(), + chat: (BuildContext context) => ChatHome(), chatDetailed: (BuildContext context) => ChatDetailScreen(), + chatFavoriteUsers: (BuildContext context) => ChatFavoriteUsersScreen(), // Marathon marathonIntroScreen: (BuildContext context) => MarathonIntroScreen(), diff --git a/lib/dialogs/otp_dialog.dart b/lib/dialogs/otp_dialog.dart index 3952e70..9158b94 100644 --- a/lib/dialogs/otp_dialog.dart +++ b/lib/dialogs/otp_dialog.dart @@ -11,10 +11,12 @@ import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/otp_widget.dart'; import 'package:sizer/sizer.dart'; +final ValueNotifier otpFieldClear = ValueNotifier(""); + class OtpDialog { final int type; final int? mobileNo; - final Function(String) onSuccess; + final Function(String, TextEditingController _pinPutController) onSuccess; final Function onFailure; final BuildContext context; final Function onResendCode; @@ -96,58 +98,70 @@ class OtpDialog { 6.height, (LocaleKeys.pleaseEnterTheVerificationCodeSentTo.tr() + ' xxxxxxxx' + mobileNo.toString().substring(mobileNo.toString().length - 3)).toText16(), 18.height, - Directionality( - textDirection: TextDirection.ltr, - child: Center( - child: OTPWidget( - autoFocus: true, - controller: _pinPutController, - defaultBorderColor: const Color(0xffD8D8D8), - maxLength: 4, - onTextChanged: (text) {}, - pinBoxColor: Colors.white, - onDone: (code) => _onOtpCallBack(code, null), - textBorderColor: const Color(0xffD8D8D8), - pinBoxWidth: 60, - pinBoxHeight: 60, - pinTextStyle: const TextStyle(fontSize: 24.0, color: MyColors.darkTextColor), - pinTextAnimatedSwitcherTransition: ProvidedPinBoxTextAnimation.scalingTransition, - pinTextAnimatedSwitcherDuration: const Duration(milliseconds: 300), - pinBoxRadius: 10, - keyboardType: TextInputType.number, - ), - ), - ), - 10.height, - stopTimer - ? Row( - children: [ - Expanded( - child: LocaleKeys.codeExpire.tr().toText16( - color: MyColors.redColor, - ), - ), - 12.width, - Image.asset( - "assets/icons/ic_alarm.png", - width: 20, - height: 20, - color: MyColors.redColor, - ), - ], - ) - : RichText( - text: TextSpan( - text: LocaleKeys.theVerificationCodeWillExpireIn.tr() + '\n', - style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.darkTextColor, letterSpacing: -0.48), - children: [ - TextSpan( - text: displayTime, - style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.textMixColor, letterSpacing: -0.48), + ValueListenableBuilder( + builder: (BuildContext context, String value, Widget? child) { + // This builder will only get called when the _counter + // is updated. + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Directionality( + textDirection: TextDirection.ltr, + child: Center( + child: OTPWidget( + autoFocus: true, + controller: _pinPutController, + defaultBorderColor: const Color(0xffD8D8D8), + maxLength: 4, + onTextChanged: (text) {}, + pinBoxColor: Colors.white, + onDone: (code) => _onOtpCallBack(code, null), + textBorderColor: const Color(0xffD8D8D8), + pinBoxWidth: 60, + pinBoxHeight: 60, + pinTextStyle: const TextStyle(fontSize: 24.0, color: MyColors.darkTextColor), + pinTextAnimatedSwitcherTransition: ProvidedPinBoxTextAnimation.scalingTransition, + pinTextAnimatedSwitcherDuration: const Duration(milliseconds: 300), + pinBoxRadius: 10, + keyboardType: TextInputType.number, ), - ], + ), ), - ), + 10.height, + stopTimer + ? Row( + children: [ + Expanded( + child: LocaleKeys.codeExpire.tr().toText16( + color: MyColors.redColor, + ), + ), + 12.width, + Image.asset( + "assets/icons/ic_alarm.png", + width: 20, + height: 20, + color: MyColors.redColor, + ), + ], + ) + : RichText( + text: TextSpan( + text: LocaleKeys.theVerificationCodeWillExpireIn.tr() + '\n', + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.darkTextColor, letterSpacing: -0.48), + children: [ + TextSpan( + text: displayTime, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.textMixColor, letterSpacing: -0.48), + ), + ], + ), + ), + ], + ); + }, + valueListenable: otpFieldClear, + ), 18.height, DefaultButton( stopTimer ? LocaleKeys.resend.tr() : LocaleKeys.cancel.tr(), @@ -238,14 +252,15 @@ class OtpDialog { }); } - static void hideSMSBox(context) { - Navigator.pop(context); + void hideSMSBox(context) { + onFailure(); } void _onOtpCallBack(String otpCode, bool? isAutofill) { if (otpCode.length == 4) { - stopTimer = true; - onSuccess(otpCode); + // stopTimer = true; + otpFieldClear.value = otpCode; + onSuccess(otpCode, _pinPutController); } } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 37f41ae..8bc15e7 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -18,12 +18,13 @@ extension EmailValidator on String { Widget toText10({Color? color, bool isBold = false, int? maxlines, FontStyle? fontStyle}) => Text( this, - //maxLines: maxlines, + maxLines: maxlines, style: TextStyle(fontSize: 10, fontStyle: fontStyle ?? FontStyle.normal, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4), ); - Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isBold = false}) => Text( + Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isBold = false, int maxLine = 0}) => Text( this, + maxLines: (maxLine > 0) ? maxLine : null, style: TextStyle( fontSize: 11, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600), diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 83910f7..a54ff37 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -514,7 +514,9 @@ class CodegenLoader extends AssetLoader{ "verification": "تَحَقّق", "resend": "إعادة إرسال", "codeExpire": "انتهت صلاحية رمز التحقق", - "typeheretoreply": "اكتب هنا للرد" + "typeheretoreply": "اكتب هنا للرد", + "favorite": "مفضلتي", + "searchfromchat": "البحث من الدردشة" }; static const Map en_US = { "mohemm": "Mohemm", @@ -802,7 +804,7 @@ static const Map en_US = { "add": "Add", "edit": "Edit", "myProfile": "My Profile", - "mowadhafhi": "Mowadhafhi", + "mowadhafhi": "Mowadhafi", "searchAnnouncements": "Search Announcements", "announcements": "Announcements", "swipeRequest": "Swipe Request", @@ -814,7 +816,7 @@ static const Map en_US = { "relatedTopic": "Related Topic", "selectTopic": "Select Topic", "supportingDocument": "Supporting Document", - "mowadhafhiRequest": "Mowadhafhi Request", + "mowadhafhiRequest": "Mowadhafi Request", "ticketReference": "Ticket Reference", "section": "Section", "topic": "Topic", @@ -1016,7 +1018,9 @@ static const Map en_US = { "resend": "Resend", "codeExpire": "The verification code has been expired", "allQuestionsCorrect": "You have answered all questions correct", - "typeheretoreply": "Type here to reply" + "typeheretoreply": "Type here to reply", + "favorite": "My Favorites", + "searchfromchat": "Search from chat" }; static const Map> mapLocales = {"ar_SA": ar_SA, "en_US": en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index f52bb89..f5d4960 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -485,5 +485,7 @@ abstract class LocaleKeys { static const resend = 'resend'; static const codeExpire = 'codeExpire'; static const typeheretoreply = 'typeheretoreply'; + static const favorite = 'favorite'; + static const searchfromchat = 'searchfromchat'; } diff --git a/lib/generated_plugin_registrant.dart b/lib/generated_plugin_registrant.dart new file mode 100644 index 0000000..288ba42 --- /dev/null +++ b/lib/generated_plugin_registrant.dart @@ -0,0 +1,37 @@ +// +// Generated file. Do not edit. +// + +// ignore_for_file: directives_ordering +// ignore_for_file: lines_longer_than_80_chars +// ignore_for_file: depend_on_referenced_packages + +import 'package:file_picker/_internal/file_picker_web.dart'; +import 'package:firebase_core_web/firebase_core_web.dart'; +import 'package:firebase_messaging_web/firebase_messaging_web.dart'; +import 'package:fluttertoast/fluttertoast_web.dart'; +import 'package:geolocator_web/geolocator_web.dart'; +import 'package:google_maps_flutter_web/google_maps_flutter_web.dart'; +import 'package:image_picker_for_web/image_picker_for_web.dart'; +import 'package:shared_preferences_web/shared_preferences_web.dart'; +import 'package:url_launcher_web/url_launcher_web.dart'; +import 'package:video_player_web/video_player_web.dart'; +import 'package:wakelock_web/wakelock_web.dart'; + +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +// ignore: public_member_api_docs +void registerPlugins(Registrar registrar) { + FilePickerWeb.registerWith(registrar); + FirebaseCoreWeb.registerWith(registrar); + FirebaseMessagingWeb.registerWith(registrar); + FluttertoastWebPlugin.registerWith(registrar); + GeolocatorPlugin.registerWith(registrar); + GoogleMapsPlugin.registerWith(registrar); + ImagePickerPlugin.registerWith(registrar); + SharedPreferencesPlugin.registerWith(registrar); + UrlLauncherPlugin.registerWith(registrar); + VideoPlayerPlugin.registerWith(registrar); + WakelockWeb.registerWith(registrar); + registrar.registerMessageHandler(); +} diff --git a/lib/main.dart b/lib/main.dart index adca4aa..a2ae0ae 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -92,6 +92,12 @@ class MyApp extends StatelessWidget { MonthYearPickerLocalizations.delegate, ); return MaterialApp( + builder: (BuildContext context, Widget? child) { + return MediaQuery( + data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), + child: child!, + ); + }, theme: AppTheme.getTheme( EasyLocalization.of(context)?.locale.languageCode == "ar", ), diff --git a/lib/models/chat/call.dart b/lib/models/chat/call.dart new file mode 100644 index 0000000..20f6a79 --- /dev/null +++ b/lib/models/chat/call.dart @@ -0,0 +1,133 @@ +// To parse this JSON data, do +// +// final callDataModel = callDataModelFromJson(jsonString); + +import 'dart:convert'; + +class CallDataModel { + CallDataModel({ + this.callerId, + this.callReciverId, + this.notificationForeground, + this.message, + this.title, + this.type, + this.identity, + this.name, + this.isCall, + this.isWebrtc, + this.contant, + this.contantNo, + this.chatEventId, + this.fileTypeId, + this.currentUserId, + this.chatSource, + this.userChatHistoryLineRequestList, + this.server, + }); + + String? callerId; + String? callReciverId; + String? notificationForeground; + String? message; + String? title; + String? type; + String? identity; + String? name; + String? isCall; + String? isWebrtc; + String? contant; + String? contantNo; + String? chatEventId; + dynamic? fileTypeId; + String? currentUserId; + String? chatSource; + List? userChatHistoryLineRequestList; + String? server; + + factory CallDataModel.fromRawJson(String str) => CallDataModel.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory CallDataModel.fromJson(Map json) => CallDataModel( + callerId: json["callerID"] == null ? null : json["callerID"], + callReciverId: json["callReciverID"] == null ? null : json["callReciverID"], + notificationForeground: json["notification_foreground"] == null ? null : json["notification_foreground"], + message: json["message"] == null ? null : json["message"], + title: json["title"] == null ? null : json["title"], + type: json["type"] == null ? null : json["type"], + identity: json["identity"] == null ? null : json["identity"], + name: json["name"] == null ? null : json["name"], + isCall: json["is_call"] == null ? null : json["is_call"], + isWebrtc: json["is_webrtc"] == null ? null : json["is_webrtc"], + contant: json["contant"] == null ? null : json["contant"], + contantNo: json["contantNo"] == null ? null : json["contantNo"], + chatEventId: json["chatEventId"] == null ? null : json["chatEventId"], + fileTypeId: json["fileTypeId"], + currentUserId: json["currentUserId"] == null ? null : json["currentUserId"], + chatSource: json["chatSource"] == null ? null : json["chatSource"], + userChatHistoryLineRequestList: json["userChatHistoryLineRequestList"] == null + ? null + : List.from( + json["userChatHistoryLineRequestList"].map( + (x) => UserChatHistoryLineRequestList.fromJson(x), + ), + ), + server: json["server"] == null ? null : json["server"], + ); + + Map toJson() => { + "callerID": callerId == null ? null : callerId, + "callReciverID": callReciverId == null ? null : callReciverId, + "notification_foreground": notificationForeground == null ? null : notificationForeground, + "message": message == null ? null : message, + "title": title == null ? null : title, + "type": type == null ? null : type, + "identity": identity == null ? null : identity, + "name": name == null ? null : name, + "is_call": isCall == null ? null : isCall, + "is_webrtc": isWebrtc == null ? null : isWebrtc, + "contant": contant == null ? null : contant, + "contantNo": contantNo == null ? null : contantNo, + "chatEventId": chatEventId == null ? null : chatEventId, + "fileTypeId": fileTypeId, + "currentUserId": currentUserId == null ? null : currentUserId, + "chatSource": chatSource == null ? null : chatSource, + "userChatHistoryLineRequestList": userChatHistoryLineRequestList == null + ? null + : List.from( + userChatHistoryLineRequestList!.map( + (x) => x.toJson(), + ), + ), + "server": server == null ? null : server, + }; +} + +class UserChatHistoryLineRequestList { + UserChatHistoryLineRequestList({ + this.isSeen, + this.isDelivered, + this.targetUserId, + this.targetUserStatus, + }); + + bool? isSeen; + bool? isDelivered; + int? targetUserId; + int? targetUserStatus; + + factory UserChatHistoryLineRequestList.fromJson(Map json) => UserChatHistoryLineRequestList( + isSeen: json["isSeen"] == null ? null : json["isSeen"], + isDelivered: json["isDelivered"] == null ? null : json["isDelivered"], + targetUserId: json["targetUserId"] == null ? null : json["targetUserId"], + targetUserStatus: json["targetUserStatus"] == null ? null : json["targetUserStatus"], + ); + + Map toJson() => { + "isSeen": isSeen == null ? null : isSeen, + "isDelivered": isDelivered == null ? null : isDelivered, + "targetUserId": targetUserId == null ? null : targetUserId, + "targetUserStatus": targetUserStatus == null ? null : targetUserStatus, + }; +} diff --git a/lib/models/chat/chat_count_conversation_model.dart b/lib/models/chat/chat_count_conversation_model.dart new file mode 100644 index 0000000..e584d32 --- /dev/null +++ b/lib/models/chat/chat_count_conversation_model.dart @@ -0,0 +1,25 @@ +import 'dart:convert'; + +ChatUnreadCovnCountModel chatUnreadCovnCountModelFromJson(String str) => ChatUnreadCovnCountModel.fromJson(json.decode(str)); + +String chatUnreadCovnCountModelToJson(ChatUnreadCovnCountModel data) => json.encode(data.toJson()); + +class ChatUnreadCovnCountModel { + ChatUnreadCovnCountModel({ + this.singleChatCount, + this.groupChatCount, + }); + + int? singleChatCount; + int? groupChatCount; + + factory ChatUnreadCovnCountModel.fromJson(Map json) => ChatUnreadCovnCountModel( + singleChatCount: json["singleChatCount"] == null ? null : json["singleChatCount"], + groupChatCount: json["groupChatCount"] == null ? null : json["groupChatCount"], + ); + + Map toJson() => { + "singleChatCount": singleChatCount == null ? null : singleChatCount, + "groupChatCount": groupChatCount == null ? null : groupChatCount, + }; +} diff --git a/lib/models/chat/get_single_user_chat_list_Model.dart b/lib/models/chat/get_single_user_chat_list_model.dart similarity index 58% rename from lib/models/chat/get_single_user_chat_list_Model.dart rename to lib/models/chat/get_single_user_chat_list_model.dart index 0e3cb22..6a35f0e 100644 --- a/lib/models/chat/get_single_user_chat_list_Model.dart +++ b/lib/models/chat/get_single_user_chat_list_model.dart @@ -1,3 +1,9 @@ +import 'dart:convert'; + +List singleUserChatModelFromJson(String str) => List.from(json.decode(str).map((x) => SingleUserChatModel.fromJson(x))); + +String singleUserChatModelToJson(List data) => json.encode(List.from(data.map((x) => x.toJson()))); + class SingleUserChatModel { SingleUserChatModel({ this.userChatHistoryId, @@ -19,6 +25,7 @@ class SingleUserChatModel { this.conversationId, this.fileTypeResponse, this.userChatReplyResponse, + this.isReplied, }); int? userChatHistoryId; @@ -29,17 +36,18 @@ class SingleUserChatModel { String? currentUserName; int? targetUserId; String? targetUserName; - dynamic encryptedTargetUserId; - dynamic encryptedTargetUserName; + String? encryptedTargetUserId; + String? encryptedTargetUserName; int? chatEventId; - dynamic fileTypeId; + dynamic? fileTypeId; bool? isSeen; bool? isDelivered; DateTime? createdDate; int? chatSource; String? conversationId; FileTypeResponse? fileTypeResponse; - dynamic userChatReplyResponse; + UserChatReplyResponse? userChatReplyResponse; + bool? isReplied; factory SingleUserChatModel.fromJson(Map json) => SingleUserChatModel( userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"], @@ -50,8 +58,8 @@ class SingleUserChatModel { currentUserName: json["currentUserName"] == null ? null : json["currentUserName"], targetUserId: json["targetUserId"] == null ? null : json["targetUserId"], targetUserName: json["targetUserName"] == null ? null : json["targetUserName"], - encryptedTargetUserId: json["encryptedTargetUserId"], - encryptedTargetUserName: json["encryptedTargetUserName"], + encryptedTargetUserId: json["encryptedTargetUserId"] == null ? null : json["encryptedTargetUserId"], + encryptedTargetUserName: json["encryptedTargetUserName"] == null ? null : json["encryptedTargetUserName"], chatEventId: json["chatEventId"] == null ? null : json["chatEventId"], fileTypeId: json["fileTypeId"], isSeen: json["isSeen"] == null ? null : json["isSeen"], @@ -60,7 +68,8 @@ class SingleUserChatModel { chatSource: json["chatSource"] == null ? null : json["chatSource"], conversationId: json["conversationId"] == null ? null : json["conversationId"], fileTypeResponse: json["fileTypeResponse"] == null ? null : FileTypeResponse.fromJson(json["fileTypeResponse"]), - userChatReplyResponse: json["userChatReplyResponse"], + userChatReplyResponse: json["userChatReplyResponse"] == null ? null : UserChatReplyResponse.fromJson(json["userChatReplyResponse"]), + isReplied: false, ); Map toJson() => { @@ -72,8 +81,8 @@ class SingleUserChatModel { "currentUserName": currentUserName == null ? null : currentUserName, "targetUserId": targetUserId == null ? null : targetUserId, "targetUserName": targetUserName == null ? null : targetUserName, - "encryptedTargetUserId": encryptedTargetUserId, - "encryptedTargetUserName": encryptedTargetUserName, + "encryptedTargetUserId": encryptedTargetUserId == null ? null : encryptedTargetUserId, + "encryptedTargetUserName": encryptedTargetUserName == null ? null : encryptedTargetUserName, "chatEventId": chatEventId == null ? null : chatEventId, "fileTypeId": fileTypeId, "isSeen": isSeen == null ? null : isSeen, @@ -82,7 +91,7 @@ class SingleUserChatModel { "chatSource": chatSource == null ? null : chatSource, "conversationId": conversationId == null ? null : conversationId, "fileTypeResponse": fileTypeResponse == null ? null : fileTypeResponse!.toJson(), - "userChatReplyResponse": userChatReplyResponse, + "userChatReplyResponse": userChatReplyResponse == null ? null : userChatReplyResponse!.toJson(), }; } @@ -117,3 +126,51 @@ class FileTypeResponse { "fileName": fileName, }; } + +class UserChatReplyResponse { + UserChatReplyResponse({ + this.userChatHistoryId, + this.chatEventId, + this.contant, + this.contantNo, + this.fileTypeId, + this.createdDate, + this.targetUserId, + this.targetUserName, + this.fileTypeResponse, + }); + + int? userChatHistoryId; + int? chatEventId; + String? contant; + String? contantNo; + dynamic? fileTypeId; + DateTime? createdDate; + int? targetUserId; + String? targetUserName; + FileTypeResponse? fileTypeResponse; + + factory UserChatReplyResponse.fromJson(Map json) => UserChatReplyResponse( + userChatHistoryId: json["userChatHistoryId"] == null ? null : json["userChatHistoryId"], + chatEventId: json["chatEventId"] == null ? null : json["chatEventId"], + contant: json["contant"] == null ? null : json["contant"], + contantNo: json["contantNo"] == null ? null : json["contantNo"], + fileTypeId: json["fileTypeId"], + createdDate: json["createdDate"] == null ? null : DateTime.parse(json["createdDate"]), + targetUserId: json["targetUserId"] == null ? null : json["targetUserId"], + targetUserName: json["targetUserName"] == null ? null : json["targetUserName"], + fileTypeResponse: json["fileTypeResponse"] == null ? null : FileTypeResponse.fromJson(json["fileTypeResponse"]), + ); + + Map toJson() => { + "userChatHistoryId": userChatHistoryId == null ? null : userChatHistoryId, + "chatEventId": chatEventId == null ? null : chatEventId, + "contant": contant == null ? null : contant, + "contantNo": contantNo == null ? null : contantNo, + "fileTypeId": fileTypeId, + "createdDate": createdDate == null ? null : createdDate!.toIso8601String(), + "targetUserId": targetUserId == null ? null : targetUserId, + "targetUserName": targetUserName == null ? null : targetUserName, + "fileTypeResponse": fileTypeResponse == null ? null : fileTypeResponse!.toJson(), + }; +} diff --git a/lib/models/chat/get_user_login_token_model.dart b/lib/models/chat/get_user_login_token_model.dart index 8afd3a9..8d55461 100644 --- a/lib/models/chat/get_user_login_token_model.dart +++ b/lib/models/chat/get_user_login_token_model.dart @@ -1,4 +1,3 @@ - import 'dart:convert'; UserAutoLoginModel userAutoLoginModelFromJson(String str) => UserAutoLoginModel.fromJson(json.decode(str)); @@ -7,22 +6,22 @@ String userAutoLoginModelToJson(UserAutoLoginModel data) => json.encode(data.toJ class UserAutoLoginModel { UserAutoLoginModel({ - this.response, + this.response, this.errorResponses, }); Response? response; - dynamic? errorResponses; + List? errorResponses; factory UserAutoLoginModel.fromJson(Map json) => UserAutoLoginModel( - response: json["response"] == null ? null : Response.fromJson(json["response"]), - errorResponses: json["errorResponses"], - ); + response: json["response"] == null ? null : Response.fromJson(json["response"]), + errorResponses: json["errorResponses"] == null ? null : List.from(json["errorResponses"].map((x) => ErrorResponse.fromJson(x))), + ); Map toJson() => { - "response": response == null ? null : response!.toJson(), - "errorResponses": errorResponses, - }; + "response": response == null ? null : response!.toJson(), + "errorResponses": errorResponses == null ? null : List.from(errorResponses!.map((x) => x.toJson())), + }; } class Response { @@ -51,28 +50,48 @@ class Response { String? encryptedUserName; factory Response.fromJson(Map json) => Response( - id: json["id"] == null ? null : json["id"], - userName: json["userName"] == null ? null : json["userName"], - email: json["email"] == null ? null : json["email"], - phone: json["phone"] == null ? null : json["phone"], - title: json["title"] == null ? null : json["title"], - token: json["token"] == null ? null : json["token"], - isDomainUser: json["isDomainUser"] == null ? null : json["isDomainUser"], - isActiveCode: json["isActiveCode"] == null ? null : json["isActiveCode"], - encryptedUserId: json["encryptedUserId"] == null ? null : json["encryptedUserId"], - encryptedUserName: json["encryptedUserName"] == null ? null : json["encryptedUserName"], - ); + id: json["id"] == null ? null : json["id"], + userName: json["userName"] == null ? null : json["userName"], + email: json["email"] == null ? null : json["email"], + phone: json["phone"] == null ? null : json["phone"], + title: json["title"] == null ? null : json["title"], + token: json["token"] == null ? null : json["token"], + isDomainUser: json["isDomainUser"] == null ? null : json["isDomainUser"], + isActiveCode: json["isActiveCode"] == null ? null : json["isActiveCode"], + encryptedUserId: json["encryptedUserId"] == null ? null : json["encryptedUserId"], + encryptedUserName: json["encryptedUserName"] == null ? null : json["encryptedUserName"], + ); + + Map toJson() => { + "id": id == null ? null : id, + "userName": userName == null ? null : userName, + "email": email == null ? null : email, + "phone": phone == null ? null : phone, + "title": title == null ? null : title, + "token": token == null ? null : token, + "isDomainUser": isDomainUser == null ? null : isDomainUser, + "isActiveCode": isActiveCode == null ? null : isActiveCode, + "encryptedUserId": encryptedUserId == null ? null : encryptedUserId, + "encryptedUserName": encryptedUserName == null ? null : encryptedUserName, + }; +} + +class ErrorResponse { + ErrorResponse({ + this.fieldName, + this.message, + }); + + String? fieldName; + String? message; + + factory ErrorResponse.fromJson(Map json) => ErrorResponse( + fieldName: json["fieldName"] == null ? null : json["fieldName"], + message: json["message"] == null ? null : json["message"], + ); Map toJson() => { - "id": id == null ? null : id, - "userName": userName == null ? null : userName, - "email": email == null ? null : email, - "phone": phone == null ? null : phone, - "title": title == null ? null : title, - "token": token == null ? null : token, - "isDomainUser": isDomainUser == null ? null : isDomainUser, - "isActiveCode": isActiveCode == null ? null : isActiveCode, - "encryptedUserId": encryptedUserId == null ? null : encryptedUserId, - "encryptedUserName": encryptedUserName == null ? null : encryptedUserName, - }; + "fieldName": fieldName == null ? null : fieldName, + "message": message == null ? null : message, + }; } diff --git a/lib/models/chat/make_user_favotire_unfavorite_chat_model.dart b/lib/models/chat/make_user_favotire_unfavorite_chat_model.dart new file mode 100644 index 0000000..64eb6b1 --- /dev/null +++ b/lib/models/chat/make_user_favotire_unfavorite_chat_model.dart @@ -0,0 +1,53 @@ +// To parse this JSON data, do +// +// final favoriteChatUser = favoriteChatUserFromJson(jsonString); + +import 'dart:convert'; + +class FavoriteChatUser { + FavoriteChatUser({ + this.response, + this.errorResponses, + }); + + Response? response; + dynamic? errorResponses; + + factory FavoriteChatUser.fromRawJson(String str) => FavoriteChatUser.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory FavoriteChatUser.fromJson(Map json) => FavoriteChatUser( + response: json["response"] == null ? null : Response.fromJson(json["response"]), + errorResponses: json["errorResponses"], + ); + + Map toJson() => { + "response": response == null ? null : response!.toJson(), + "errorResponses": errorResponses, + }; +} + +class Response { + Response({ + this.targetUserId, + this.isFav, + }); + + int? targetUserId; + bool? isFav; + + factory Response.fromRawJson(String str) => Response.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory Response.fromJson(Map json) => Response( + targetUserId: json["targetUserId"] == null ? null : json["targetUserId"], + isFav: json["isFav"] == null ? null : json["isFav"], + ); + + Map toJson() => { + "targetUserId": targetUserId == null ? null : targetUserId, + "isFav": isFav == null ? null : isFav, + }; +} diff --git a/lib/models/generic_response_model.dart b/lib/models/generic_response_model.dart index 753d2c2..cd7043c 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -99,12 +99,14 @@ import 'package:mohem_flutter_app/models/vacation_rule/respond_attributes_list_m import 'package:mohem_flutter_app/models/vacation_rule/vr_item_types_list_model.dart'; import 'package:mohem_flutter_app/models/vacation_rule/wf_look_up_list_model.dart'; import 'package:mohem_flutter_app/models/validate_eit_transaction_list_model.dart'; +import 'package:mohem_flutter_app/models/worklist/GetRFCEmployeeList.dart'; import 'package:mohem_flutter_app/models/worklist/get_favorite_replacements_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/eit_otification_body_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/get_basic_det_ntf_body_list_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/get_contact_notification_body_list_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/get_phones_notification_body_list_model.dart'; import 'package:mohem_flutter_app/models/worklist/replacement_list_model.dart'; +import 'package:mohem_flutter_app/models/worklist/resubmit_eit_response_model.dart'; import 'package:mohem_flutter_app/models/worklist_response_model.dart'; import 'package:mohem_flutter_app/start_term_approval_process_list_model.dart'; @@ -224,7 +226,7 @@ class GenericResponseModel { GetPoNotificationBodyList? getPoNotificationBodyList; GetPrNotificationBodyList? getPrNotificationBodyList; List? getQuotationAnalysisList; - List? getRFCEmployeeListList; + List? getRFCEmployeeListList; List? getRespondAttributeValueList; List? getSITCollectionNotificationBodyList; List? getSITDFFStructureList; @@ -326,13 +328,13 @@ class GenericResponseModel { List? respondAttributesList; List? respondRolesList; String? resubmitAbsenceTransactionList; - String? resubmitEITTransactionList; + ResubmitEITRequestResponse? resubmitEITTransactionList; String? resubmitHrTransactionList; String? sFHGetPoNotificationBodyList; String? sFHGetPrNotificationBodyList; StartAbsenceApprovalProccess? startAbsenceApprovalProccess; StartAddressApprovalProcess? startAddressApprovalProcessList; - String? startBasicDetApprProcessList; + StartAddressApprovalProcess? startBasicDetApprProcessList; String? startCeiApprovalProcess; String? startContactApprovalProcessList; StartEitApprovalProcess? startEitApprovalProcess; @@ -975,7 +977,7 @@ class GenericResponseModel { getQuotationAnalysisList!.add(GetQuotationAnalysisList.fromJson(v)); }); } - getRFCEmployeeListList = json['GetRFCEmployeeListList']; + getRespondAttributeValueList = json['GetRespondAttributeValueList']; getSITCollectionNotificationBodyList = json['GetSITCollectionNotificationBodyList']; getSITDFFStructureList = json['GetSITDFFStructureList']; @@ -986,6 +988,14 @@ class GenericResponseModel { getScheduleShiftsDetailsList!.add(GetScheduleShiftsDetailsList.fromJson(v)); }); } + + if (json['GetRFCEmployeeListList'] != null) { + getRFCEmployeeListList = []; + json['GetRFCEmployeeListList'].forEach((v) { + getRFCEmployeeListList!.add(GetRFCEmployeeList.fromJson(v)); + }); + } + getShiftTypesList = json['GetShiftTypesList']; if (json['GetStampMsNotificationBodyList'] != null) { @@ -1273,25 +1283,25 @@ class GenericResponseModel { if (json['RespondRolesList'] != null) { respondRolesList = []; json['RespondRolesList'].forEach((v) { - respondRolesList!.add(v); + // respondRolesList!.add(v); }); } resubmitAbsenceTransactionList = json['ResubmitAbsenceTransactionList']; - resubmitEITTransactionList = json['ResubmitEITTransactionList']; + resubmitEITTransactionList = json['ResubmitEITTransactionList'] != null ? ResubmitEITRequestResponse.fromJson(json['ResubmitEITTransactionList']) : null; resubmitHrTransactionList = json['ResubmitHrTransactionList']; sFHGetPoNotificationBodyList = json['SFH_GetPoNotificationBodyList']; sFHGetPrNotificationBodyList = json['SFH_GetPrNotificationBodyList']; startAbsenceApprovalProccess = json['StartAbsenceApprovalProccess'] != null ? StartAbsenceApprovalProccess.fromJson(json['StartAbsenceApprovalProccess']) : null; startAddressApprovalProcessList = json['StartAddressApprovalProcessList'] != null ? StartAddressApprovalProcess.fromJson(json['StartAddressApprovalProcessList']) : null; - startBasicDetApprProcessList = json['StartBasicDetApprProcessList']; + startBasicDetApprProcessList = json['StartAddressApprovalProcessList'] != null ? StartAddressApprovalProcess.fromJson(json['StartAddressApprovalProcessList']) : null; startCeiApprovalProcess = json['StartCeiApprovalProcess']; startContactApprovalProcessList = json['StartContactApprovalProcessList']; startEitApprovalProcess = json['StartEitApprovalProcess'] != null ? StartEitApprovalProcess.fromJson(json['StartEitApprovalProcess']) : null; startHrApprovalProcessList = json['StartHrApprovalProcessList']; - startPhonesApprovalProcessList = json['StartPhonesApprovalProcessList'] != null ? StartPhoneApprovalProcess.fromJson(json['startPhonesApprovalProcessList']) : null; + startPhonesApprovalProcessList = json['StartPhonesApprovalProcessList'] != null ? StartPhoneApprovalProcess.fromJson(json['StartPhonesApprovalProcessList']) : null; startSitApprovalProcess = json['StartSitApprovalProcess']; startTermApprovalProcessList = json['StartTermApprovalProcessList'] != null ? StartTermApprovalProcessList.fromJson(json['StartTermApprovalProcessList']) : null; diff --git a/lib/models/get_employee_address_model.dart b/lib/models/get_employee_address_model.dart index 8531ced..71c5885 100644 --- a/lib/models/get_employee_address_model.dart +++ b/lib/models/get_employee_address_model.dart @@ -5,7 +5,7 @@ String? dATATYPE; String? dATEVALUE; String? dISPLAYFLAG; - Null? nUMBERVALUE; + dynamic? nUMBERVALUE; String? sEGMENTPROMPT; int? sEGMENTSEQNUM; String? sEGMENTVALUEDSP; diff --git a/lib/models/profile/get_address_dff_structure_list.dart b/lib/models/profile/get_address_dff_structure_list.dart index 786e683..61f6b5e 100644 --- a/lib/models/profile/get_address_dff_structure_list.dart +++ b/lib/models/profile/get_address_dff_structure_list.dart @@ -5,7 +5,7 @@ class GetAddressDffStructureList { String? aLPHANUMERICALLOWEDFLAG; String? aPPLICATIONCOLUMNNAME; String? cHILDSEGMENTSVS; - Null? cHILDSEGMENTSVSSplited; + dynamic? cHILDSEGMENTSVSSplited; String? dEFAULTTYPE; String? dEFAULTVALUE; String? dESCFLEXCONTEXTCODE; @@ -27,9 +27,9 @@ class GetAddressDffStructureList { String? nUMBERPRECISION; String? nUMERICMODEENABLEDFLAG; String? pARENTSEGMENTSDV; - List? pARENTSEGMENTSDVSplited; + List? pARENTSEGMENTSDVSplited; String? pARENTSEGMENTSVS; - List? pARENTSEGMENTSVSSplitedVS; + List? pARENTSEGMENTSVSSplitedVS; String? rEADONLY; String? rEQUIREDFLAG; String? sEGMENTNAME; diff --git a/lib/models/worklist/GetRFCEmployeeList.dart b/lib/models/worklist/GetRFCEmployeeList.dart new file mode 100644 index 0000000..33881c7 --- /dev/null +++ b/lib/models/worklist/GetRFCEmployeeList.dart @@ -0,0 +1,48 @@ +class GetRFCEmployeeList { + String? aCTION; + String? eMPLOYEEDISPLAYNAME; + String? eMPLOYEEIMAGE; + int? fROMROWNUM; + int? nOOFROWS; + int? rOWNUM; + int? sEQ; + int? tOROWNUM; + String? uSERNAME; + + GetRFCEmployeeList( + {this.aCTION, + this.eMPLOYEEDISPLAYNAME, + this.eMPLOYEEIMAGE, + this.fROMROWNUM, + this.nOOFROWS, + this.rOWNUM, + this.sEQ, + this.tOROWNUM, + this.uSERNAME}); + + GetRFCEmployeeList.fromJson(Map json) { + aCTION = json['ACTION']; + eMPLOYEEDISPLAYNAME = json['EMPLOYEE_DISPLAY_NAME']; + eMPLOYEEIMAGE = json['EMPLOYEE_IMAGE']; + fROMROWNUM = json['FROM_ROW_NUM']; + nOOFROWS = json['NO_OF_ROWS']; + rOWNUM = json['ROW_NUM']; + sEQ = json['SEQ']; + tOROWNUM = json['TO_ROW_NUM']; + uSERNAME = json['USER_NAME']; + } + + Map toJson() { + Map data = new Map(); + data['ACTION'] = this.aCTION; + data['EMPLOYEE_DISPLAY_NAME'] = this.eMPLOYEEDISPLAYNAME; + data['EMPLOYEE_IMAGE'] = this.eMPLOYEEIMAGE; + data['FROM_ROW_NUM'] = this.fROMROWNUM; + data['NO_OF_ROWS'] = this.nOOFROWS; + data['ROW_NUM'] = this.rOWNUM; + data['SEQ'] = this.sEQ; + data['TO_ROW_NUM'] = this.tOROWNUM; + data['USER_NAME'] = this.uSERNAME; + return data; + } +} diff --git a/lib/models/worklist/get_favorite_replacements_model.dart b/lib/models/worklist/get_favorite_replacements_model.dart index b5cefad..feee917 100644 --- a/lib/models/worklist/get_favorite_replacements_model.dart +++ b/lib/models/worklist/get_favorite_replacements_model.dart @@ -16,10 +16,10 @@ class GetFavoriteReplacements { this.employeeImage, }); - final String? userName; - final String? employeeDisplayName; - final String? emailAddress; - final String? employeeImage; + String? userName; + String? employeeDisplayName; + String? emailAddress; + String? employeeImage; factory GetFavoriteReplacements.fromJson(Map json) => GetFavoriteReplacements( userName: json["USER_NAME"] == null ? null : json["USER_NAME"], diff --git a/lib/models/worklist/replacement_list_model.dart b/lib/models/worklist/replacement_list_model.dart index f77b991..864cbe1 100644 --- a/lib/models/worklist/replacement_list_model.dart +++ b/lib/models/worklist/replacement_list_model.dart @@ -23,7 +23,7 @@ class ReplacementList { final String? emailAddress; final String? employeeDisplayName; - final String? employeeImage; + String? employeeImage; final int? fromRowNum; bool? isFavorite; final int? noOfRows; diff --git a/lib/models/worklist/resubmit_eit_response_model.dart b/lib/models/worklist/resubmit_eit_response_model.dart new file mode 100644 index 0000000..1da90ba --- /dev/null +++ b/lib/models/worklist/resubmit_eit_response_model.dart @@ -0,0 +1,22 @@ +class ResubmitEITRequestResponse { + String? pRETURNMSG; + String? pRETURNSTATUS; + int? pTRANSACTIONID; + + ResubmitEITRequestResponse( + {this.pRETURNMSG, this.pRETURNSTATUS, this.pTRANSACTIONID}); + + ResubmitEITRequestResponse.fromJson(Map json) { + pRETURNMSG = json['P_RETURN_MSG']; + pRETURNSTATUS = json['P_RETURN_STATUS']; + pTRANSACTIONID = json['P_TRANSACTION_ID']; + } + + Map toJson() { + Map data = new Map(); + data['P_RETURN_MSG'] = this.pRETURNMSG; + data['P_RETURN_STATUS'] = this.pRETURNSTATUS; + data['P_TRANSACTION_ID'] = this.pTRANSACTIONID; + return data; + } +} diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index 5b9f8f1..948e5b4 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -7,6 +7,7 @@ import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/main.dart'; +import 'package:mohem_flutter_app/models/chat/chat_count_conversation_model.dart'; import 'package:mohem_flutter_app/models/dashboard/drawer_menu_item_model.dart'; import 'package:mohem_flutter_app/models/dashboard/get_accrual_balances_list_model.dart'; import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart'; @@ -34,6 +35,10 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { bool isWorkListLoading = true; int workListCounter = 0; + //Chat + bool isChatCounterLoding = true; + int chatUConvCounter = 0; + //Misssing Swipe bool isMissingSwipeLoading = true; int missingSwipeCounter = 0; @@ -91,6 +96,9 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { accrualList = null; leaveBalanceAccrual = null; + isChatCounterLoding = true; + chatUConvCounter = 0; + ticketBalance = 0; isServicesMenusLoading = true; homeMenus = null; @@ -266,7 +274,18 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { return res; } - + void fetchChatCounts() async { + try { + ChatUnreadCovnCountModel response = await DashboardApiClient().getChatCount(); + chatUConvCounter = response.singleChatCount!; + isChatCounterLoding = false; + notifyListeners(); + } catch (ex) { + logger.wtf(ex); + notifyListeners(); + Utils.handleException(ex, null, null); + } + } void notify() { notifyListeners(); diff --git a/lib/ui/attendance/add_vacation_rule_screen.dart b/lib/ui/attendance/add_vacation_rule_screen.dart index a698adc..caa1ee3 100644 --- a/lib/ui/attendance/add_vacation_rule_screen.dart +++ b/lib/ui/attendance/add_vacation_rule_screen.dart @@ -98,7 +98,7 @@ class _AddVacationRuleScreenState extends State { } void callCombineApis() async { - try { + try { Utils.showLoading(context); List results = await Future.wait([ VacationRuleApiClient().getNotificationReassignMode(), diff --git a/lib/ui/attendance/monthly_attendance_screen.dart b/lib/ui/attendance/monthly_attendance_screen.dart index 439a5f4..b6407a8 100644 --- a/lib/ui/attendance/monthly_attendance_screen.dart +++ b/lib/ui/attendance/monthly_attendance_screen.dart @@ -286,8 +286,7 @@ class _MonthlyAttendanceScreenState extends State { showWeekNumber: false, cellBorderColor: Colors.white, selectionDecoration: BoxDecoration( - border: Border.all(color: MyColors.white, width: 10), - borderRadius: const BorderRadius.all(Radius.circular(100)), + border: Border.all(color: MyColors.white, width: 1), shape: BoxShape.circle, ), dataSource: MeetingDataSource(_getDataSource()), @@ -432,7 +431,7 @@ class _MonthlyAttendanceScreenState extends State { expand: false, builder: (_, controller) { dynamic dmyString = getScheduleShiftsDetailsList!.sCHEDULEDATE; - DateTime dateTime1 = DateFormat("MM/dd/yyyy hh:mm:ss a").parse(dmyString); + DateTime dateTime1 = DateFormat("MM/dd/yyyy hh:mm:ss").parse(dmyString); return Column( children: [ Container( diff --git a/lib/ui/chat/call/chat_incoming_call_screen.dart b/lib/ui/chat/call/chat_incoming_call_screen.dart new file mode 100644 index 0000000..a0b8247 --- /dev/null +++ b/lib/ui/chat/call/chat_incoming_call_screen.dart @@ -0,0 +1,379 @@ +import 'dart:ui'; + +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/models/chat/call.dart'; + +class IncomingCall extends StatefulWidget { + CallDataModel incomingCallData; + bool? isVideoCall; + + IncomingCall({Key? key, required this.incomingCallData, this.isVideoCall}) : super(key: key); + + @override + _IncomingCallState createState() => _IncomingCallState(); +} + +class _IncomingCallState extends State with SingleTickerProviderStateMixin { + AnimationController? _animationController; + CameraController? _controller; + Future? _initializeControllerFuture; + bool isCameraReady = false; + + @override + void initState() { + _animationController = AnimationController( + vsync: this, + duration: const Duration( + milliseconds: 500, + ), + ); + //_runAnimation(); + // connectSignaling(); + WidgetsBinding.instance.addPostFrameCallback( + (_) => _runAnimation(), + ); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: FutureBuilder( + future: _initializeControllerFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return Stack( + alignment: FractionalOffset.center, + children: [ + if (widget.isVideoCall!) + Positioned.fill( + child: AspectRatio( + aspectRatio: _controller!.value.aspectRatio, + child: CameraPreview( + _controller!, + ), + ), + ), + Positioned.fill( + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Container( + decoration: BoxDecoration( + color: MyColors.grey57Color.withOpacity( + 0.7, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Container( + margin: const EdgeInsets.all(21.0), + child: Row( + children: [ + Image.asset( + "assets/images/logos/main_mohemm_logo.png", + height: 70, + width: 70, + ), + Container( + margin: const EdgeInsets.only( + left: 10.0, + right: 10.0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: const [ + Text( + "Aamir Saleem Ahmad", + style: TextStyle( + fontSize: 21, + fontWeight: FontWeight.bold, + color: MyColors.white, + letterSpacing: -1.26, + height: 23 / 12, + ), + ), + Text( + "Calling...", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Color( + 0xffC6C6C6, + ), + letterSpacing: -0.48, + height: 23 / 24, + ), + ), + SizedBox( + height: 2, + ), + ], + ), + ), + ], + ), + ), + // Container( + // margin: const EdgeInsets.all(21.0), + // width: MediaQuery.of(context).size.width, + // decoration: cardRadius(15.0, color: MyColors.black, elevation: null), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Container( + // padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 6.0), + // child: Text( + // "TranslationBase.of(context).appoInfo", + // style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.white, letterSpacing: -0.64, height: 23 / 12), + // ), + // ), + // Container( + // padding: const EdgeInsets.only(left: 16.0, right: 16.0), + // child: Text( + // "widget.incomingCallData.appointmentdate + widget.incomingCallData.appointmenttime", + // style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600), + // ), + // ), + // Container( + // padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 21.0), + // child: Text( + // "widget.incomingCallData.clinicname", + // style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600), + // ), + // ), + // ], + // ), + // ), + const Spacer(), + Container( + margin: const EdgeInsets.only( + bottom: 70.0, + left: 49, + right: 49, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + RotationTransition( + turns: Tween( + begin: 0.0, + end: -.1, + ) + .chain( + CurveTween( + curve: Curves.elasticIn, + ), + ) + .animate( + _animationController!, + ), + child: RawMaterialButton( + onPressed: () { + _submit(); + }, + elevation: 2.0, + fillColor: MyColors.green2DColor, + padding: const EdgeInsets.all( + 15.0, + ), + shape: const CircleBorder(), + child: const Icon( + Icons.call, + color: MyColors.white, + size: 35.0, + ), + ), + ), + RawMaterialButton( + onPressed: () { + backToHome(); + }, + elevation: 2.0, + fillColor: MyColors.redA3Color, + padding: const EdgeInsets.all( + 15.0, + ), + shape: const CircleBorder(), + child: const Icon( + Icons.call_end, + color: MyColors.white, + size: 35.0, + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ], + ); + } else { + return const Center( + child: CircularProgressIndicator(), + ); + } + }, + ), + ); + } + + void _runAnimation() async { + List cameras = await availableCameras(); + CameraDescription firstCamera = cameras[1]; + _controller = CameraController( + firstCamera, + ResolutionPreset.medium, + ); + _initializeControllerFuture = _controller!.initialize(); + setState(() {}); + // setAudioFile(); + for (int i = 0; i < 100; i++) { + await _animationController!.forward(); + await _animationController!.reverse(); + } + } + + Future _submit() async { + try { + // backToHome(); + // final roomModel = RoomModel(name: widget.incomingCallData.name, token: widget.incomingCallData.sessionId, identity: widget.incomingCallData.identity); + await _controller?.dispose(); + + // changeCallStatusAPI(4); + + // if (_session != null && _signaling != null) { + // await Navigator.of(context).pushReplacement( + // MaterialPageRoute( + // // fullscreenDialog: true, + // builder: (BuildContext context) { + // // if (widget.incomingCallData.isWebRTC == "true") { + // return StartVideoCall(signaling: _signaling, session: _session); + // + // // else { + // // return OpenTokConnectCallPage(apiKey: OPENTOK_API_KEY, sessionId: widget.incomingCallData.sessionId, token: widget.incomingCallData.token); + // // } + // + // // return VideoCallWebPage(receiverId: widget.incomingCallData.receiverID, callerId: widget.incomingCallData.callerID); // Web WebRTC VideoCall + // + // // return CallHomePage(receiverId: widget.incomingCallData.receiverID, callerId: widget.incomingCallData.callerID); // App WebRTC VideoCall + // }, + // ), + // ); + // } else { + // // Invalid Params/Data + // Utils.showToast("Failed to establish connection with server"); + // } + } catch (err) { + print(err); + // await PlatformExceptionAlertDialog( + // exception: err, + // ).show(context); + + Utils.showToast(err.toString()); + } + } + + // void changeCallStatusAPI(int sessionStatus) { + // LiveCareService service = new LiveCareService(); + // service.endCallAPI(widget.incomingCallData.sessionId, sessionStatus, context).then((res) {}).catchError((err) { + // print(err); + // }); + // } + + void backToHome() async { + // final connected = await signaling.declineCall(widget.incomingCallData.callerID, widget.incomingCallData.receiverID); + // LandingPage.isOpenCallPage = false; + // _signaling + _animationController!.dispose(); + // player.stop(); + // changeCallStatusAPI(3); + // _signaling.bye(_session, callRejected: true); + // _signaling.callDisconnected(_session, callRejected: true); + Navigator.of(context).pop(); + } + + // + // void disposeAudioResources() async { + // await player.dispose(); + // } + // + // void setAudioFile() async { + // player.stop(); + // await player.setVolume(1.0); // full volume + // try { + // await player.setAsset('assets/sounds/ring_60Sec.mp3').then((value) { + // player.setLoopMode(LoopMode.one); // loop ring sound + // player.play(); + // }).catchError((err) { + // print("Error: $err"); + // }); + // } catch (e) { + // print("Error: $e"); + // } + // } + // + // void connectSignaling({@required bool iAmCaller = false}) async { + // print("----------------- + Signaling Connection Started ---------------------------"); + // var caller = widget.incomingCallData.callerID; + // var receiver = widget.incomingCallData.receiverID; + // var host = widget.incomingCallData.server; + // + // var selfRole = iAmCaller ? "Caller" : "Receiver"; + // var selfId = iAmCaller ? caller : receiver; + // var selfUser = SocketUser(id: selfId, name: "$selfRole-$selfId", userAgent: DeviceInfo.userAgent, moreInfo: {}); + // + // var remoteRole = !iAmCaller ? "Caller" : "Receiver"; + // var remoteId = !iAmCaller ? caller : receiver; + // var remoteUser = SocketUser(id: remoteId, name: "$remoteRole-$remoteId", userAgent: DeviceInfo.userAgent, moreInfo: {}); + // + // var sessionId = "$caller-$receiver"; + // _session = SessionOneToOne(id: sessionId, local_user: selfUser, remote_user: remoteUser); + // + // _signaling = Signaling(host, session: _session); + // await _signaling.connect(); + // + // if (_signaling.state == SignalingState.Open) { + // return; + // } + // } + + BoxDecoration cardRadius(double radius, {required Color color, double? elevation}) { + return BoxDecoration( + shape: BoxShape.rectangle, + color: color ?? Colors.white, + borderRadius: BorderRadius.all( + Radius.circular(radius), + ), + boxShadow: [ + BoxShadow( + color: const Color( + 0xff000000, + ).withOpacity( + .05, + ), + //spreadRadius: 5, + blurRadius: elevation ?? 27, + offset: const Offset( + -2, + 3, + ), + ), + ], + ); + } +} diff --git a/lib/ui/chat/call/chat_outgoing_call_screen.dart b/lib/ui/chat/call/chat_outgoing_call_screen.dart new file mode 100644 index 0000000..627c24a --- /dev/null +++ b/lib/ui/chat/call/chat_outgoing_call_screen.dart @@ -0,0 +1,431 @@ +import 'dart:convert'; +import 'dart:ui'; + +import 'package:camera/camera.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/main.dart'; +import 'package:mohem_flutter_app/models/chat/call.dart'; + +class OutGoingCall extends StatefulWidget { + CallDataModel OutGoingCallData; + bool? isVideoCall; + + OutGoingCall({Key? key, required this.OutGoingCallData, this.isVideoCall}) : super(key: key); + + @override + _OutGoingCallState createState() => _OutGoingCallState(); +} + +class _OutGoingCallState extends State with SingleTickerProviderStateMixin { + AnimationController? _animationController; + CameraController? _controller; + Future? _initializeControllerFuture; + bool isCameraReady = false; + bool isMicOff = false; + bool isLoudSpeaker = false; + bool isCamOff = false; + + @override + void initState() { + _animationController = AnimationController( + vsync: this, + duration: const Duration( + milliseconds: 500, + ), + ); + logger.d(jsonEncode(widget.OutGoingCallData)); + //_runAnimation(); + // connectSignaling(); + WidgetsBinding.instance.addPostFrameCallback( + (_) => _runAnimation(), + ); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: FutureBuilder( + future: _initializeControllerFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return Stack( + alignment: FractionalOffset.center, + children: [ + if (widget.isVideoCall!) + Positioned.fill( + child: AspectRatio( + aspectRatio: _controller!.value.aspectRatio, + child: CameraPreview( + _controller!, + ), + ), + ), + Positioned.fill( + child: ClipRect( + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Container( + decoration: BoxDecoration( + color: MyColors.grey57Color.withOpacity( + 0.7, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + 40.height, + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + margin: const EdgeInsets.all(21.0), + child: Container( + margin: const EdgeInsets.only( + left: 10.0, + right: 10.0, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + SvgPicture.asset( + "assets/images/user.svg", + height: 70, + width: 70, + fit: BoxFit.cover, + ), + 10.height, + const Text( + "Aamir Saleem Ahmad", + style: TextStyle( + fontSize: 21, + fontWeight: FontWeight.bold, + color: MyColors.white, + letterSpacing: -1.26, + height: 23 / 12, + ), + ), + const Text( + "Ringing...", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Color( + 0xffC6C6C6, + ), + letterSpacing: -0.48, + height: 23 / 24, + ), + ), + const SizedBox( + height: 2, + ), + ], + ), + ), + ), + ], + ), + // Container( + // margin: const EdgeInsets.all(21.0), + // width: MediaQuery.of(context).size.width, + // decoration: cardRadius(15.0, color: MyColors.black, elevation: null), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Container( + // padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 6.0), + // child: Text( + // "TranslationBase.of(context).appoInfo", + // style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: MyColors.white, letterSpacing: -0.64, height: 23 / 12), + // ), + // ), + // Container( + // padding: const EdgeInsets.only(left: 16.0, right: 16.0), + // child: Text( + // "widget.OutGoingCallData.appointmentdate + widget.OutGoingCallData.appointmenttime", + // style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600), + // ), + // ), + // Container( + // padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 21.0), + // child: Text( + // "widget.OutGoingCallData.clinicname", + // style: TextStyle(fontSize: 12.0, letterSpacing: -0.48, color: Color(0xff8E8E8E), fontWeight: FontWeight.w600), + // ), + // ), + // ], + // ), + // ), + const Spacer(), + Container( + margin: const EdgeInsets.only( + bottom: 70.0, + left: 49, + right: 49, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (widget.isVideoCall!) + RawMaterialButton( + onPressed: () { + _camOff(); + }, + elevation: 2.0, + fillColor: isCamOff ? MyColors.green2DColor : Colors.grey, + padding: const EdgeInsets.all( + 15.0, + ), + shape: const CircleBorder(), + child: Icon( + isCamOff ? Icons.videocam_off : Icons.videocam, + color: MyColors.white, + size: 35.0, + ), + ) + else + RawMaterialButton( + onPressed: () { + _loudOn(); + }, + elevation: 2.0, + fillColor: isLoudSpeaker ? MyColors.green2DColor : Colors.grey, + padding: const EdgeInsets.all( + 15.0, + ), + shape: const CircleBorder(), + child: const Icon( + Icons.volume_up, + color: MyColors.white, + size: 35.0, + ), + ), + RawMaterialButton( + onPressed: () { + _micOff(); + }, + elevation: 2.0, + fillColor: isMicOff ? MyColors.green2DColor : Colors.grey, + padding: const EdgeInsets.all( + 15.0, + ), + shape: const CircleBorder(), + child: Icon( + isMicOff ? Icons.mic_off : Icons.mic, + color: MyColors.white, + size: 35.0, + ), + ), + RawMaterialButton( + onPressed: () { + backToHome(); + }, + elevation: 2.0, + fillColor: MyColors.redA3Color, + padding: const EdgeInsets.all( + 15.0, + ), + shape: const CircleBorder(), + child: const Icon( + Icons.call_end, + color: MyColors.white, + size: 35.0, + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ], + ); + } else { + return const Center( + child: CircularProgressIndicator(), + ); + } + }, + ), + ); + } + + void _runAnimation() async { + List cameras = await availableCameras(); + CameraDescription firstCamera = cameras[1]; + _controller = CameraController( + firstCamera, + ResolutionPreset.medium, + ); + _initializeControllerFuture = _controller!.initialize(); + setState(() {}); + // setAudioFile(); + for (int i = 0; i < 100; i++) { + await _animationController!.forward(); + await _animationController!.reverse(); + } + } + + void _micOff() { + setState(() { + isMicOff = !isMicOff; + }); + } + + void _camOff() { + setState(() { + isCamOff = !isCamOff; + }); + } + + void _loudOn() { + setState(() { + isLoudSpeaker = !isLoudSpeaker; + }); + } + + Future _submit() async { + try { + // backToHome(); + // final roomModel = RoomModel(name: widget.OutGoingCallData.name, token: widget.OutGoingCallData.sessionId, identity: widget.OutGoingCallData.identity); + await _controller?.dispose(); + + // changeCallStatusAPI(4); + + // if (_session != null && _signaling != null) { + // await Navigator.of(context).pushReplacement( + // MaterialPageRoute( + // // fullscreenDialog: true, + // builder: (BuildContext context) { + // // if (widget.OutGoingCallData.isWebRTC == "true") { + // return StartVideoCall(signaling: _signaling, session: _session); + // + // // else { + // // return OpenTokConnectCallPage(apiKey: OPENTOK_API_KEY, sessionId: widget.OutGoingCallData.sessionId, token: widget.OutGoingCallData.token); + // // } + // + // // return VideoCallWebPage(receiverId: widget.OutGoingCallData.receiverID, callerId: widget.OutGoingCallData.callerID); // Web WebRTC VideoCall + // + // // return CallHomePage(receiverId: widget.OutGoingCallData.receiverID, callerId: widget.OutGoingCallData.callerID); // App WebRTC VideoCall + // }, + // ), + // ); + // } else { + // // Invalid Params/Data + // Utils.showToast("Failed to establish connection with server"); + // } + } catch (err) { + print(err); + // await PlatformExceptionAlertDialog( + // exception: err, + // ).show(context); + + Utils.showToast(err.toString()); + } + } + + // void changeCallStatusAPI(int sessionStatus) { + // LiveCareService service = new LiveCareService(); + // service.endCallAPI(widget.OutGoingCallData.sessionId, sessionStatus, context).then((res) {}).catchError((err) { + // print(err); + // }); + // } + + void backToHome() async { + // final connected = await signaling.declineCall(widget.OutGoingCallData.callerID, widget.OutGoingCallData.receiverID); + // LandingPage.isOpenCallPage = false; + // _signaling + _animationController!.dispose(); + // player.stop(); + // changeCallStatusAPI(3); + // _signaling.bye(_session, callRejected: true); + // _signaling.callDisconnected(_session, callRejected: true); + Navigator.of(context).pop(); + } + + // + // void disposeAudioResources() async { + // await player.dispose(); + // } + // + // void setAudioFile() async { + // player.stop(); + // await player.setVolume(1.0); // full volume + // try { + // await player.setAsset('assets/sounds/ring_60Sec.mp3').then((value) { + // player.setLoopMode(LoopMode.one); // loop ring sound + // player.play(); + // }).catchError((err) { + // print("Error: $err"); + // }); + // } catch (e) { + // print("Error: $e"); + // } + // } + // + // void connectSignaling({@required bool iAmCaller = false}) async { + // print("----------------- + Signaling Connection Started ---------------------------"); + // var caller = widget.OutGoingCallData.callerID; + // var receiver = widget.OutGoingCallData.receiverID; + // var host = widget.OutGoingCallData.server; + // + // var selfRole = iAmCaller ? "Caller" : "Receiver"; + // var selfId = iAmCaller ? caller : receiver; + // var selfUser = SocketUser(id: selfId, name: "$selfRole-$selfId", userAgent: DeviceInfo.userAgent, moreInfo: {}); + // + // var remoteRole = !iAmCaller ? "Caller" : "Receiver"; + // var remoteId = !iAmCaller ? caller : receiver; + // var remoteUser = SocketUser(id: remoteId, name: "$remoteRole-$remoteId", userAgent: DeviceInfo.userAgent, moreInfo: {}); + // + // var sessionId = "$caller-$receiver"; + // _session = SessionOneToOne(id: sessionId, local_user: selfUser, remote_user: remoteUser); + // + // _signaling = Signaling(host, session: _session); + // await _signaling.connect(); + // + // if (_signaling.state == SignalingState.Open) { + // return; + // } + // } + + BoxDecoration cardRadius(double radius, {required Color color, double? elevation}) { + return BoxDecoration( + shape: BoxShape.rectangle, + color: color ?? Colors.white, + borderRadius: BorderRadius.all( + Radius.circular(radius), + ), + boxShadow: [ + BoxShadow( + color: const Color( + 0xff000000, + ).withOpacity( + .05, + ), + //spreadRadius: 5, + blurRadius: elevation ?? 27, + offset: const Offset( + -2, + 3, + ), + ), + ], + ); + } +} diff --git a/lib/ui/chat/chat_bubble.dart b/lib/ui/chat/chat_bubble.dart index 74d2e2b..1696a49 100644 --- a/lib/ui/chat/chat_bubble.dart +++ b/lib/ui/chat/chat_bubble.dart @@ -2,80 +2,142 @@ import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:sizer/sizer.dart'; class ChatBubble extends StatelessWidget { const ChatBubble( {Key? key, required this.text, + required this.replyText, required this.isCurrentUser, required this.isSeen, required this.isDelivered, - required this.dateTime}) + required this.dateTime, + required this.isReplied, + required this.userName}) : super(key: key); final String text; + final String replyText; final bool isCurrentUser; final bool isSeen; final bool isDelivered; final String dateTime; + final bool isReplied; + final String userName; @override Widget build(BuildContext context) { return Padding( - // asymmetric padding - padding: EdgeInsets.fromLTRB( - isCurrentUser ? 64.0 : 16.0, - 4, - isCurrentUser ? 16.0 : 64.0, - 4, + // padding: EdgeInsets.zero, + padding: EdgeInsets.only( + left: isCurrentUser ? 110 : 20, + right: isCurrentUser ? 20 : 110, + bottom: 9, ), + child: Align( - // align the child within the container alignment: isCurrentUser ? Alignment.centerRight : Alignment.centerLeft, child: DecoratedBox( - // chat bubble decoration decoration: BoxDecoration( - color: Colors.white, + color: MyColors.white, gradient: isCurrentUser ? null - : LinearGradient( - transform: GradientRotation(.46), + : const LinearGradient( + transform: GradientRotation( + .46, + ), begin: Alignment.topRight, end: Alignment.bottomLeft, - colors: [ - MyColors.gradiantEndColor, - MyColors.gradiantStartColor, - ]), - borderRadius: BorderRadius.circular(10), + colors: [ + MyColors.gradiantEndColor, + MyColors.gradiantStartColor, + ], + ), + borderRadius: BorderRadius.circular( + 10, + ), ), child: Padding( - padding: const EdgeInsets.all(12), + padding: EdgeInsets.only( + top: isReplied ? 8 : 5, + right: 8, + left: 8, + bottom: 5, + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ + if (isReplied) + ClipRRect( + borderRadius: BorderRadius.circular( + 5.0, + ), + child: Container( + decoration: BoxDecoration( + border: Border( + left: BorderSide( + width: 6, + color: isCurrentUser ? MyColors.gradiantStartColor : MyColors.white, + ), + ), + color: isCurrentUser ? MyColors.black.withOpacity(0.10) : MyColors.black.withOpacity(0.30), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (userName) + .toText12( + color: MyColors.gradiantStartColor, + isBold: false, + ) + .paddingOnly( + right: 5, + top: 5, + bottom: 0, + left: 5, + ), + replyText + .toText10( + color: isCurrentUser ? MyColors.grey71Color : MyColors.white.withOpacity(0.5), + isBold: false, + maxlines: 4, + ) + .paddingOnly( + right: 5, + top: 5, + bottom: 8, + left: 5, + ), + ], + ), + ), + ], + ), + ), + ), + if (isReplied) 8.height, text.toText12( - color: - isCurrentUser ? MyColors.grey57Color : MyColors.white), - 8.height, + color: isCurrentUser ? MyColors.grey57Color : MyColors.white, + ), Row( crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end, - children: [ + children: [ dateTime.toText12( - color: isCurrentUser - ? MyColors.grey41Color.withOpacity(.5) - : Colors.white.withOpacity(0.7)), + color: isCurrentUser ? MyColors.grey41Color.withOpacity(.5) : MyColors.white.withOpacity(0.7), + ), if (isCurrentUser) 5.width, - if (isCurrentUser) - Icon( - isDelivered - ? Icons.done_all - : Icons.done_all, - color: isSeen - ? MyColors.textMixColor - : MyColors.grey9DColor, - size: 14, - ) + // if (isCurrentUser) + // Icon( + // isDelivered ? Icons.done_all : Icons.done_all, + // color: isSeen ? MyColors.textMixColor : MyColors.grey9DColor, + // size: 14, + // ), ], ), ], diff --git a/lib/ui/chat/chat_detailed_screen.dart b/lib/ui/chat/chat_detailed_screen.dart index 5f2fa97..4a8e70c 100644 --- a/lib/ui/chat/chat_detailed_screen.dart +++ b/lib/ui/chat/chat_detailed_screen.dart @@ -1,109 +1,396 @@ import 'dart:async'; +import 'dart:convert'; + import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:mohem_flutter_app/api/chat/chat_provider_model.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/main.dart'; +import 'package:mohem_flutter_app/models/chat/call.dart'; +import 'package:mohem_flutter_app/ui/chat/call/chat_outgoing_call_screen.dart'; import 'package:mohem_flutter_app/ui/chat/chat_bubble.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:provider/provider.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:signalr_netcore/signalr_client.dart'; +import 'package:swipe_to/swipe_to.dart'; + +class ChatDetailScreen extends StatefulWidget { + // ignore: prefer_const_constructors_in_immutables + ChatDetailScreen({Key? key}) : super(key: key); -class ChatDetailScreen extends StatelessWidget { + @override + State createState() => _ChatDetailScreenState(); +} + +class _ChatDetailScreenState extends State { dynamic userDetails; late ChatProviderModel data; + final RefreshController _rc = RefreshController(initialRefresh: false); - ChatDetailScreen({Key? key}) : super(key: key); + void getMoreChat() async { + if (userDetails != null) { + data.paginationVal = data.paginationVal + 10; + if (userDetails != null) + data.getSingleUserChatHistory( + senderUID: AppState().chatDetails!.response!.id!.toInt(), + receiverUID: userDetails["targetUser"].id, + loadMore: true, + isNewChat: false, + ); + } + await Future.delayed( + const Duration( + milliseconds: 1000, + ), + ); + _rc.loadComplete(); + } @override Widget build(BuildContext context) { - userDetails = ModalRoute - .of(context)! - .settings - .arguments; + userDetails = ModalRoute.of(context)!.settings.arguments; data = Provider.of(context, listen: false); - data.getSingleUserChatHistory(senderUID: AppState().chatDetails!.response!.id.toString(), receiverUID: userDetails["targetUser"].id, pagination: "0"); - Timer(const Duration(seconds: 1), () => data.scrollDown()); + if (userDetails != null) { + data.getSingleUserChatHistory( + senderUID: AppState().chatDetails!.response!.id!.toInt(), + receiverUID: userDetails["targetUser"].id, + loadMore: false, + isNewChat: userDetails["isNewChat"], + ); + } + return Scaffold( backgroundColor: const Color(0xFFF8F8F8), - appBar: AppBarWidget(context, title: userDetails["targetUser"].userName, showHomeButton: false, image: userDetails["targetUser"].image), + appBar: AppBarWidget(context, + title: userDetails["targetUser"].userName.toString().replaceAll(".", " ").capitalizeFirstofEach, + showHomeButton: false, + image: userDetails["targetUser"].image, + actions: [ + IconButton( + onPressed: () { + // makeCall( + // callType: "AUDIO", + // con: data.hubConnection, + // ); + }, + icon: SvgPicture.asset( + "assets/icons/chat/call.svg", + width: 22, + height: 22, + ), + ), + IconButton( + onPressed: () { + // makeCall( + // callType: "VIDEO", + // con: data.hubConnection, + // ); + }, + icon: SvgPicture.asset( + "assets/icons/chat/video_call.svg", + width: 20, + height: 20, + ), + ), + 10.width, + ]), body: Consumer( builder: (BuildContext context, ChatProviderModel m, Widget? child) { return (m.isLoading ? ChatHomeShimmer() : Column( - children: [ - Expanded( - child: ListView.builder( - controller: m.scrollController, - shrinkWrap: true, - itemCount: m.userChatHistory.length, - padding: const EdgeInsets.symmetric(vertical: 10), - itemBuilder: (BuildContext context, int i) { - i == 0 ? m.logger.d(m.userChatHistory.length) : ""; - return ChatBubble( - text: m.userChatHistory[i].contant.toString(), - isSeen: m.userChatHistory[i].isSeen == true ? true : false, - isCurrentUser: m.userChatHistory[i].currentUserId == AppState().chatDetails!.response!.id ? true : false, - isDelivered: m.userChatHistory[i].currentUserId == AppState().chatDetails!.response!.id && m.userChatHistory[i].isDelivered == true ? true : false, - dateTime: m.userChatHistory[i].createdDate.toString(), - ); - }, - ), - ), - Card( - margin: EdgeInsets.zero, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: TextField( - controller: m.message, - decoration: InputDecoration( - hintText: LocaleKeys.typeheretoreply.tr(), - hintStyle: const TextStyle(color: MyColors.grey98Color), - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: InputBorder.none, - errorBorder: InputBorder.none, - disabledBorder: InputBorder.none, - contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15), - suffixIcon: SizedBox( - width: 100, - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - IconButton( - icon: const Icon( - Icons.attach_file_rounded, - size: 27, - color: MyColors.lightGreenColor, + children: [ + Expanded( + flex: 2, + child: SmartRefresher( + enablePullDown: false, + enablePullUp: true, + onLoading: () { + getMoreChat(); + }, + header: const MaterialClassicHeader( + color: MyColors.gradiantEndColor, + ), + controller: _rc, + reverse: true, + child: ListView.builder( + controller: m.scrollController, + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + reverse: true, + itemCount: m.userChatHistory.length, + padding: const EdgeInsets.only(top: 20), + itemBuilder: (BuildContext context, int i) { + return SwipeTo( + iconColor: MyColors.lightGreenColor, + child: ChatBubble( + text: m.userChatHistory[i].contant.toString(), + replyText: m.userChatHistory[i].userChatReplyResponse != null ? m.userChatHistory[i].userChatReplyResponse!.contant.toString() : "", + isSeen: m.userChatHistory[i].isSeen == true ? true : false, + isCurrentUser: m.userChatHistory[i].currentUserId == AppState().chatDetails!.response!.id ? true : false, + isDelivered: m.userChatHistory[i].currentUserId == AppState().chatDetails!.response!.id && m.userChatHistory[i].isDelivered == true ? true : false, + dateTime: m.dateFormte(m.userChatHistory[i].createdDate!), + isReplied: m.userChatHistory[i].userChatReplyResponse != null ? true : false, + userName: AppState().chatDetails!.response!.userName == m.userChatHistory[i].currentUserName.toString() ? "You" : m.userChatHistory[i].currentUserName.toString(), ), - onPressed: () {}, + onRightSwipe: () { + m.chatReply( + m.userChatHistory[i], + ); + }, + ); + }, + ), + ), + ), + if (m.isMsgReply) + Row( + children: [ + Container( + height: 80, + color: MyColors.textMixColor, + width: 6, + ), + Expanded( + child: Container( + height: 80, + color: MyColors.black.withOpacity(0.10), + child: ListTile( + title: (AppState().chatDetails!.response!.userName == m.repliedMsg.first.currentUserName.toString() + ? "You" + : m.repliedMsg.first.currentUserName.toString().replaceAll(".", " ")) + .toText14(color: MyColors.lightGreenColor), + subtitle: (m.repliedMsg.isNotEmpty ? m.repliedMsg.first.contant! : "").toText12( + color: MyColors.white, + maxLine: 2, + ), + trailing: GestureDetector( + onTap: m.closeMe, + child: Container( + decoration: BoxDecoration( + color: MyColors.white.withOpacity(0.5), + borderRadius: const BorderRadius.all( + Radius.circular(20), + ), + ), + child: const Icon( + Icons.close, + size: 23, + color: MyColors.white, + ), + ), + ), + ), + ), + ), + ], + ), + if (m.isFileSelected && m.sFileType == ".png" || m.sFileType == ".jpeg" || m.sFileType == ".jpg") + Card( + margin: EdgeInsets.zero, + elevation: 0, + child: Padding( + padding: const EdgeInsets.only( + left: 20, + right: 20, + top: 20, + bottom: 0, + ), + child: Card( + margin: EdgeInsets.zero, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(0), ), - IconButton( - icon: SvgPicture.asset( - "assets/icons/chat/chat_send_icon.svg", - height: 26, - width: 35, + elevation: 0, + child: Container( + height: 200, + decoration: BoxDecoration( + image: DecorationImage( + image: FileImage( + m.selectedFile, + ), + fit: BoxFit.cover, + ), + borderRadius: const BorderRadius.all( + Radius.circular(0), + ), ), - onPressed: () { - m.sendChatMessage(m.message.text, userDetails["targetUser"].id, userDetails["targetUser"].userName); - }, + child: const SizedBox( + width: double.infinity, + height: 200, + ), + ), + ), + ), + ), + Card( + margin: EdgeInsets.zero, + child: TextField( + controller: m.message, + decoration: InputDecoration( + hintText: m.isFileSelected ? m.selectedFile.path.split("/").last : LocaleKeys.typeheretoreply.tr(), + hintStyle: TextStyle( + color: m.isFileSelected ? MyColors.darkTextColor : MyColors.grey98Color, + fontSize: 14, + ), + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + disabledBorder: InputBorder.none, + contentPadding: EdgeInsets.only( + left: m.sFileType.isNotEmpty ? 10 : 20, + right: m.sFileType.isNotEmpty ? 0 : 5, + top: 20, + bottom: 20, + ), + prefixIcon: m.sFileType.isNotEmpty + ? Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SvgPicture.asset( + m.getType(m.sFileType), + height: 30, + width: 25, + alignment: Alignment.center, + fit: BoxFit.cover, + ).paddingOnly(left: 20), + ], + ) + : null, + suffixIcon: SizedBox( + width: 96, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, // added line + children: [ + if (m.sFileType.isNotEmpty) + IconButton( + padding: EdgeInsets.zero, + alignment: Alignment.centerRight, + icon: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.max, + children: [ + Container( + decoration: const BoxDecoration( + color: MyColors.redA3Color, + borderRadius: BorderRadius.all( + Radius.circular(20), + ), + ), + child: const Icon( + Icons.close, + size: 15, + color: MyColors.white, + ), + ), + ("Clear") + .toText11( + color: MyColors.redA3Color, + ) + .paddingOnly( + left: 4, + ), + ], + ), + onPressed: () async { + m.removeAttachment(); + }, + ), + if (m.sFileType.isEmpty) + RotationTransition( + turns: const AlwaysStoppedAnimation(45 / 360), + child: IconButton( + padding: EdgeInsets.zero, + alignment: Alignment.topRight, + icon: const Icon( + Icons.attach_file_rounded, + size: 26, + color: MyColors.grey3AColor, + ), + onPressed: () async { + m.selectImageToUpload(context); + }, + ), + ), + IconButton( + alignment: Alignment.centerRight, + padding: EdgeInsets.zero, + icon: SvgPicture.asset( + "assets/icons/chat/chat_send_icon.svg", + height: 26, + width: 26, + ), + onPressed: () { + m.sendChatMessage( + userDetails["targetUser"].id, + userDetails["targetUser"].userName, + context, + ); + }, + ) + ], ), - ], + ).paddingOnly( + right: 20, + ), ), ), ), - ), - ), - ), - ], - )); + ], + )); }, ), ); } + + void makeCall({required String callType, required HubConnection con}) async { + print("================== Make call Triggered ============================"); + logger.d(jsonEncode(AppState().chatDetails!.response)); + Map json = { + "callerID": AppState().chatDetails!.response!.id!.toString(), + "callReciverID": userDetails["targetUser"].id.toString(), + "notification_foreground": "true", + "message": "Aamir is calling ", + "title": "Video Call", + "type": callType == "VIDEO" ? "Video" : "Audio", + "identity": "Aamir.Muhammad", + "name": "Aamir Saleem Ahmad", + "is_call": "true", + "is_webrtc": "true", + "contant": "Start video Call Aamir.Muhammad", + "contantNo": "775d1f11-62d9-6fcc-91f6-21f8c14559fb", + "chatEventId": "3", + "fileTypeId": null, + "currentUserId": "266642", + "chatSource": "1", + "userChatHistoryLineRequestList": [ + {"isSeen": false, "isDelivered": false, "targetUserId": 341682, "targetUserStatus": 4} + ], + // "server": "https://192.168.8.163:8086", + "server": "https://livecareturn.hmg.com:8086", + }; + + CallDataModel incomingCallData = CallDataModel.fromJson(json); + await Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => OutGoingCall( + isVideoCall: callType == "VIDEO" ? true : false, + OutGoingCallData: incomingCallData, + ), + ), + ); + } } diff --git a/lib/ui/chat/chat_home.dart b/lib/ui/chat/chat_home.dart index f6f8e24..b2db98a 100644 --- a/lib/ui/chat/chat_home.dart +++ b/lib/ui/chat/chat_home.dart @@ -1,37 +1,40 @@ import 'dart:convert'; import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:mohem_flutter_app/api/chat/chat_provider_model.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/config/routes.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/ui/chat/chat_home_screen.dart'; +import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.dart'; +import 'package:mohem_flutter_app/ui/screens/items_for_sale/fragments/items_for_sale.dart'; +import 'package:mohem_flutter_app/ui/screens/items_for_sale/fragments/my_posted_ads_fragment.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; -import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; -import 'package:mohem_flutter_app/widgets/bottom_sheets/search_employee_bottom_sheet.dart'; -import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:provider/provider.dart'; -class ChatHomeScreen extends StatefulWidget { - const ChatHomeScreen({Key? key}) : super(key: key); +class ChatHome extends StatefulWidget { + const ChatHome({Key? key}) : super(key: key); @override - State createState() => _ChatHomeScreenState(); + State createState() => _ChatHomeState(); } -class _ChatHomeScreenState extends State { - TextEditingController search = new TextEditingController(); +class _ChatHomeState extends State { + int tabIndex = 0; + PageController controller = PageController(); late ChatProviderModel data; @override void initState() { + // TODO: implement initState super.initState(); data = Provider.of(context, listen: false); - data.getUserAutoLoginToken().whenComplete(() { + data.getUserAutoLoginToken(context).whenComplete(() { data.getUserRecentChats(); }); } @@ -39,141 +42,91 @@ class _ChatHomeScreenState extends State { @override void dispose() { super.dispose(); + data.clearAll(); + if (data.hubConInitialized) { + data.hubConnection.stop(); + } } @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.white, - appBar: AppBarWidget(context, title: LocaleKeys.mychats.tr(), showHomeButton: false), - body: Consumer(builder: (BuildContext context, ChatProviderModel m, Widget? child) { - return m.isLoading - ? ChatHomeShimmer() - : ListView( - shrinkWrap: true, - physics: const AlwaysScrollableScrollPhysics(), - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 20), - child: TextField( - onChanged: (String val) { - m.filter(val); - }, - decoration: InputDecoration( - border: InputBorder.none, - focusedBorder: InputBorder.none, - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(5), - borderSide: const BorderSide( - color: Color(0xFFE5E5E5), - ), - ), - errorBorder: InputBorder.none, - disabledBorder: InputBorder.none, - contentPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), - hintText: "Search from chat", - hintStyle: const TextStyle(color: MyColors.lightTextColor, fontStyle: FontStyle.italic), - filled: true, - fillColor: const Color(0xFFF7F7F7), - ), - ), - ), - if (m.searchedChats != null) - ListView.separated( - itemCount: m.searchedChats!.length, - padding: const EdgeInsets.only(top: 0), - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (BuildContext context, int index) { - return ListTile( - leading: Stack( - children: [ - SvgPicture.asset( - "assets/images/user.svg", - height: 48, - width: 48, - ), - Positioned( - right: 5, - bottom: 1, - child: Container( - width: 10, - height: 10, - decoration: BoxDecoration( - color: m.searchedChats![index].userStatus == 1 ? MyColors.green2DColor : Colors.red, - borderRadius: const BorderRadius.all( - Radius.circular(10), - ), - ), - ), - ) - ], - ), - title: (m.searchedChats![index].userName ?? "").toText14(color: MyColors.darkTextColor), - subtitle: (m.searchedChats![index].isTyping == true ? "Typing ..." : "").toText11(color: MyColors.normalTextColor), - trailing: ("Today").toText10(color: MyColors.lightTextColor), - minVerticalPadding: 0, - onTap: () { - Navigator.pushNamed( - context, - AppRoutes.chatDetailed, - arguments: {"targetUser": m.searchedChats![index]}, - ); - }, - ); - }, - separatorBuilder: (BuildContext context, int index) => const Padding( - padding: EdgeInsets.only(right: 10, left: 70), - child: Divider( - color: Color(0xFFE5E5E5), - ), - ), - ), - // if (searchedUsersList == null) Utils.getNoChatWidget(context), + backgroundColor: MyColors.white, + appBar: AppBarWidget( + context, + title: LocaleKeys.chat.tr(), + showHomeButton: true, + ), + body: Column( + children: [ + Container( + padding: const EdgeInsets.only(left: 21, right: 21, top: 16, bottom: 16), + decoration: const BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + ), + gradient: LinearGradient( + transform: GradientRotation(.83), + begin: Alignment.topRight, + end: Alignment.bottomLeft, + colors: [ + MyColors.gradiantEndColor, + MyColors.gradiantStartColor, ], - ); - }), - floatingActionButton: FloatingActionButton( - child: Container( - width: 60, - height: 60, - decoration: const BoxDecoration( - shape: BoxShape.circle, - gradient: LinearGradient( - transform: GradientRotation(.46), - begin: Alignment.topRight, - end: Alignment.bottomLeft, - colors: [ - MyColors.gradiantEndColor, - MyColors.gradiantStartColor, + ), + ), + child: Row( + children: [ + myTab(LocaleKeys.mychats.tr(), 0), + myTab(LocaleKeys.favorite.tr(), 1), ], ), ), - child: const Icon( - Icons.add, - size: 30, - color: MyColors.white, - ), - ), - onPressed: () async { - // var userData = await ChatApiClient() - // .getChatMemberFromSearch("aamir.muhammad", 36239); - showMyBottomSheet( - context, - callBackFunc: (){}, - child: SearchEmployeeBottomSheet( - title: LocaleKeys.searchForEmployee.tr(), - apiMode: LocaleKeys.delegate.tr(), - fromChat: true, - onSelectEmployee: (_selectedEmployee) { - // Navigator.pop(context); - // selectedReplacementEmployee = _selectedEmployee; - setState(() {}); - }, - ), - ); - }, + PageView( + controller: controller, + physics: const NeverScrollableScrollPhysics(), + onPageChanged: (int pageIndex) { + setState(() { + tabIndex = pageIndex; + }); + }, + children: [ + ChatHomeScreen(), + ChatFavoriteUsersScreen(), + ], + ).expanded, + ], ), ); } + + Widget myTab(String title, int index) { + bool isSelected = (index == tabIndex); + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + title.toText12( + color: isSelected ? MyColors.white : MyColors.white.withOpacity(.74), + isCenter: true, + ), + 4.height, + Container( + height: 8, + width: 8, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: isSelected ? MyColors.white : Colors.transparent, + ), + ).onPress(() { + setState(() { + // showFabOptions = true; + }); + }) + ], + ).onPress(() { + controller.jumpToPage(index); + }).expanded; + } } diff --git a/lib/ui/chat/chat_home_screen.dart b/lib/ui/chat/chat_home_screen.dart new file mode 100644 index 0000000..816aaec --- /dev/null +++ b/lib/ui/chat/chat_home_screen.dart @@ -0,0 +1,270 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:mohem_flutter_app/api/chat/chat_provider_model.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/config/routes.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; +import 'package:mohem_flutter_app/widgets/bottom_sheets/search_employee_bottom_sheet.dart'; +import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; +import 'package:provider/provider.dart'; + +class ChatHomeScreen extends StatefulWidget { + @override + State createState() => _ChatHomeScreenState(); +} + +class _ChatHomeScreenState extends State { + TextEditingController search = TextEditingController(); + + @override + void dispose() { + super.dispose(); + search.clear(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: MyColors.white, + body: Consumer( + builder: (BuildContext context, ChatProviderModel m, Widget? child) { + return m.isLoading + ? ChatHomeShimmer() + : ListView( + shrinkWrap: true, + physics: const AlwaysScrollableScrollPhysics(), + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 20, + horizontal: 20, + ), + child: TextField( + controller: m.search, + onChanged: (String val) { + m.filter(val); + }, + decoration: InputDecoration( + border: fieldBorder( + radius: 5, + color: 0xFFE5E5E5, + ), + focusedBorder: fieldBorder( + radius: 5, + color: 0xFFE5E5E5, + ), + enabledBorder: fieldBorder( + radius: 5, + color: 0xFFE5E5E5, + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 15, + vertical: 10, + ), + hintText: LocaleKeys.searchfromchat.tr(), + hintStyle: const TextStyle( + color: MyColors.lightTextColor, + fontStyle: FontStyle.italic, + ), + filled: true, + fillColor: const Color( + 0xFFF7F7F7, + ), + suffixIcon: m.search.text.isNotEmpty + ? IconButton( + onPressed: () { + m.clearSelections(); + }, + icon: const Icon( + Icons.clear, + size: 22, + ), + color: MyColors.redA3Color, + ) + : null, + ), + ), + ), + if (m.searchedChats != null) + ListView.separated( + itemCount: m.searchedChats!.length, + padding: const EdgeInsets.only( + bottom: 80, + ), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + return SizedBox( + height: 55, + child: ListTile( + leading: Stack( + children: [ + SvgPicture.asset( + "assets/images/user.svg", + height: 48, + width: 48, + ), + Positioned( + right: 5, + bottom: 1, + child: Container( + width: 10, + height: 10, + decoration: BoxDecoration( + color: m.searchedChats![index].userStatus == 1 ? MyColors.green2DColor : Colors.red, + borderRadius: const BorderRadius.all( + Radius.circular(10), + ), + ), + ), + ) + ], + ), + title: (m.searchedChats![index].userName!.replaceFirst(".", " ").capitalizeFirstofEach ?? "").toText14(color: MyColors.darkTextColor), + // subtitle: (m.searchedChats![index].isTyping == true ? "Typing ..." : "").toText11(color: MyColors.normalTextColor), + trailing: SizedBox( + width: 60, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.max, + children: [ + if (m.searchedChats![index].unreadMessageCount! > 0) + Flexible( + child: Container( + padding: EdgeInsets.zero, + alignment: Alignment.centerRight, + width: 18, + height: 18, + decoration: const BoxDecoration( + color: MyColors.redColor, + borderRadius: BorderRadius.all( + Radius.circular(20), + ), + ), + child: (m.searchedChats![index].unreadMessageCount!.toString()) + .toText10( + color: MyColors.white, + ) + .center, + ), + ), + Flexible( + child: IconButton( + alignment: Alignment.centerRight, + padding: EdgeInsets.zero, + icon: Icon( + m.searchedChats![index].isFav != null && m.searchedChats![index].isFav == false ? Icons.star_sharp : Icons.star_sharp, + ), + color: m.searchedChats![index].isFav != null && m.searchedChats![index].isFav == true ? MyColors.yellowColor : MyColors.grey35Color, + onPressed: () { + if (m.searchedChats![index].isFav == null || m.searchedChats![index].isFav == false) { + m.favoriteUser( + userID: AppState().chatDetails!.response!.id!, + targetUserID: m.searchedChats![index].id!, + ); + } else if (m.searchedChats![index].isFav == true) { + m.unFavoriteUser( + userID: AppState().chatDetails!.response!.id!, + targetUserID: m.searchedChats![index].id!, + ); + } else { + m.favoriteUser( + userID: AppState().chatDetails!.response!.id!, + targetUserID: m.searchedChats![index].id!, + ); + } + }, + ), + ) + ], + ), + ), + minVerticalPadding: 0, + onTap: () { + Navigator.pushNamed( + context, + AppRoutes.chatDetailed, + arguments: {"targetUser": m.searchedChats![index], "isNewChat": false}, + ).then((Object? value) { + // m.GetUserChatHistoryNotDeliveredAsync(userId: int.parse(AppState().chatDetails!.response!.id.toString())); + m.clearSelections(); + m.notifyListeners(); + }); + }, + ), + ); + }, + separatorBuilder: (BuildContext context, int index) => const Padding( + padding: EdgeInsets.only( + right: 10, + left: 70, + ), + child: Divider( + color: Color( + 0xFFE5E5E5, + ), + ), + ), + ), + ], + ); + }, + ), + floatingActionButton: FloatingActionButton( + child: Container( + width: 60, + height: 60, + decoration: const BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + transform: GradientRotation(.46), + begin: Alignment.topRight, + end: Alignment.bottomLeft, + colors: [ + MyColors.gradiantEndColor, + MyColors.gradiantStartColor, + ], + ), + ), + child: const Icon( + Icons.add, + size: 30, + color: MyColors.white, + ), + ), + onPressed: () async { + showMyBottomSheet( + context, + callBackFunc: () {}, + child: SearchEmployeeBottomSheet( + title: LocaleKeys.searchForEmployee.tr(), + apiMode: LocaleKeys.delegate.tr(), + fromChat: true, + onSelectEmployee: (_selectedEmployee) {}, + ), + ); + }, + ), + ); + } + + OutlineInputBorder fieldBorder({required double radius, required int color}) { + return OutlineInputBorder( + borderRadius: BorderRadius.circular( + radius, + ), + borderSide: BorderSide( + color: Color( + color, + ), + ), + ); + } +} diff --git a/lib/ui/chat/favorite_users_screen.dart b/lib/ui/chat/favorite_users_screen.dart new file mode 100644 index 0000000..7ef0f84 --- /dev/null +++ b/lib/ui/chat/favorite_users_screen.dart @@ -0,0 +1,114 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:mohem_flutter_app/api/chat/chat_provider_model.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/config/routes.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; +import 'package:provider/provider.dart'; + +class ChatFavoriteUsersScreen extends StatelessWidget { + const ChatFavoriteUsersScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: MyColors.white, + body: Consumer( + builder: (BuildContext context, ChatProviderModel m, Widget? child) { + if (m.isLoading) { + return ChatHomeShimmer(); + } else { + return m.favUsersList != null && m.favUsersList.isNotEmpty + ? ListView.separated( + itemCount: m.favUsersList!.length, + padding: const EdgeInsets.only(top: 20), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + return SizedBox( + height: 55, + child: ListTile( + leading: Stack( + children: [ + SvgPicture.asset( + "assets/images/user.svg", + height: 48, + width: 48, + ), + Positioned( + right: 5, + bottom: 1, + child: Container( + width: 10, + height: 10, + decoration: BoxDecoration( + color: m.favUsersList![index].userStatus == 1 ? MyColors.green2DColor : Colors.red, + borderRadius: const BorderRadius.all( + Radius.circular(10), + ), + ), + ), + ) + ], + ), + title: (m.favUsersList![index].userName!.replaceFirst(".", " ").capitalizeFirstofEach ?? "").toText14( + color: MyColors.darkTextColor, + ), + trailing: IconButton( + alignment: Alignment.centerRight, + padding: EdgeInsets.zero, + icon: Icon( + m.favUsersList![index].isFav! ? Icons.star : Icons.star_border, + ), + color: m.favUsersList![index].isFav! ? MyColors.yellowColor : MyColors.grey35Color, + onPressed: () { + if (m.favUsersList![index].isFav!) + m.unFavoriteUser( + userID: AppState().chatDetails!.response!.id!, + targetUserID: m.favUsersList![index].id!, + ); + }, + ), + minVerticalPadding: 0, + onTap: () { + Navigator.pushNamed( + context, + AppRoutes.chatDetailed, + arguments: {"targetUser": m.favUsersList![index], "isNewChat": false}, + ).then( + (Object? value) { + m.clearSelections(); + }, + ); + }, + ), + ); + }, + separatorBuilder: (BuildContext context, int index) => const Padding( + padding: EdgeInsets.only( + right: 10, + left: 70, + ), + child: Divider( + color: Color( + 0xFFE5E5E5, + ), + ), + ), + ) + : Column( + children: [ + Utils.getNoDataWidget(context).expanded, + ], + ); + } + }, + ), + ); + } +} diff --git a/lib/ui/dialogs/success_dialog.dart b/lib/ui/dialogs/success_dialog.dart index 5150110..a5df64f 100644 --- a/lib/ui/dialogs/success_dialog.dart +++ b/lib/ui/dialogs/success_dialog.dart @@ -53,8 +53,8 @@ class _SuccessDialogState extends State with TickerProviderStateM repeat: false, reverse: false, controller: _controller, + frameRate: FrameRate(60.0), onLoaded: (LottieComposition v) async { - print("calling_lottie " + v.seconds.toString()); await playSuccessSound(); _controller ..duration = v.duration diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index c0e82f4..71b6d7b 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -13,6 +13,7 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/ui/landing/widget/app_drawer.dart'; @@ -75,6 +76,7 @@ class _DashboardScreenState extends State { data.fetchMenuEntries(); data.getCategoryOffersListAPI(context); marathonProvider.getMarathonDetailsFromApi(); + data.fetchChatCounts(); _refreshController.refreshCompleted(); } @@ -260,7 +262,7 @@ class _DashboardScreenState extends State { margin: EdgeInsets.only(top: AppState().isArabic(context) ? 6 : 0), width: 45, height: 45, - padding: const EdgeInsets.only(left: 14, right: 14), + padding: const EdgeInsets.only(left: 10, right: 10), decoration: BoxDecoration( color: Color(0xff259EA4), borderRadius: BorderRadius.only( @@ -473,10 +475,29 @@ class _DashboardScreenState extends State { label: LocaleKeys.itemsForSale.tr(), ), BottomNavigationBarItem( - icon: SvgPicture.asset( - "assets/icons/chat/chat.svg", - color: currentIndex == 4 ? MyColors.grey3AColor : MyColors.grey98Color, - ).paddingAll(4), + icon: Stack( + alignment: Alignment.centerLeft, + children: [ + SvgPicture.asset( + "assets/icons/chat/chat.svg", + color: currentIndex == 4 ? MyColors.grey3AColor : MyColors.grey98Color, + ).paddingAll(4), + Consumer( + builder: (BuildContext cxt, DashboardProviderModel data, Widget? child) { + return Positioned( + right: 0, + top: 0, + child: Container( + padding: const EdgeInsets.only(left: 4, right: 4), + alignment: Alignment.center, + decoration: BoxDecoration(color: MyColors.redColor, borderRadius: BorderRadius.circular(17)), + child: data.chatUConvCounter.toString().toText10(color: Colors.white), + ), + ); + }, + ), + ], + ), label: LocaleKeys.chat.tr(), ), ], @@ -496,7 +517,9 @@ class _DashboardScreenState extends State { } else if (index == 3) { Navigator.pushNamed(context, AppRoutes.itemsForSale); } else if (index == 4) { - Navigator.pushNamed(context, AppRoutes.chat); + Navigator.pushNamed(context, AppRoutes.chat).then((Object? value) { + data.fetchChatCounts(); + }); } }, ), diff --git a/lib/ui/landing/today_attendance_screen2.dart b/lib/ui/landing/today_attendance_screen2.dart index 17bb03a..ada652c 100644 --- a/lib/ui/landing/today_attendance_screen2.dart +++ b/lib/ui/landing/today_attendance_screen2.dart @@ -86,7 +86,7 @@ class _TodayAttendanceScreenState extends State { child: CircularStepProgressBar( totalSteps: 16 * 4, currentStep: (model.progress * 100).toInt(), - selectedColor: MyColors.gradiantEndColor, + selectedColor: MyColors.gradiantStartColor, unselectedColor: MyColors.grey70Color, child: Center( child: Padding( diff --git a/lib/ui/landing/widget/missing_swipe.dart b/lib/ui/landing/widget/missing_swipe.dart index ebd011a..c5bc4fb 100644 --- a/lib/ui/landing/widget/missing_swipe.dart +++ b/lib/ui/landing/widget/missing_swipe.dart @@ -1,20 +1,12 @@ -import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; -import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; -import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; -import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; -import 'package:mohem_flutter_app/models/dashboard/menus.dart'; import 'package:mohem_flutter_app/models/eit/get_eit_transaction_model.dart'; -import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/provider/eit_provider_model.dart'; -import 'package:mohem_flutter_app/ui/work_list/work_list_screen.dart'; import 'package:mohem_flutter_app/widgets/loading_dialog.dart'; -import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:provider/provider.dart'; class MissingSwipe extends StatelessWidget { diff --git a/lib/ui/landing/widget/services_widget.dart b/lib/ui/landing/widget/services_widget.dart index d822920..2333c18 100644 --- a/lib/ui/landing/widget/services_widget.dart +++ b/lib/ui/landing/widget/services_widget.dart @@ -22,8 +22,8 @@ class ServicesWidget extends StatelessWidget { "assets/images/monthly_attendance.svg", "assets/images/monthly_attendance.svg", "assets/images/ticket_request.svg", - "assets/images/ticket_request.svg", - "assets/images/ticket_request.svg", + "assets/images/ticket_bal.svg", + "assets/images/vacation_rule.svg", "assets/images/ticket_request.svg", "assets/images/ticket_request.svg", "assets/images/ticket_request.svg" @@ -39,7 +39,7 @@ class ServicesWidget extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, - children: [ + children: [ data.homeMenus![parentIndex].menuEntry.prompt!.toSectionHeading().paddingOnly(left: 21, right: 21), SizedBox( height: 105 + 26, @@ -69,12 +69,12 @@ class ServicesWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SvgPicture.asset(iconT[index]), + SvgPicture.asset(AppState().isArabic(context) ? getMenuIconAr(data.homeMenus![parentIndex].menuEntiesList[index].prompt!) : getMenuIconEn(data.homeMenus![parentIndex].menuEntiesList[index].prompt!)), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Expanded( - child: data.homeMenus![parentIndex].menuEntiesList[index].prompt!.toText10(isBold: true), + child: data.homeMenus![parentIndex].menuEntiesList[index].prompt!.toText11(isBold: true), ), RotatedBox(quarterTurns: AppState().isArabic(context) ? 2 : 4, child: SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4)), ], @@ -102,6 +102,92 @@ class ServicesWidget extends StatelessWidget { ); } + String getMenuIconAr(String name) { + String returnImage = ""; + switch (name) { + case "الحضور الشهري": + returnImage = "assets/images/services_icons/monthly_attendance.svg"; + break; + case "كشف الراتب": + returnImage = "assets/images/services_icons/payslips.svg"; + break; + case "تغيير معلومات البنك": + returnImage = "assets/images/services_icons/change_bank_details.svg"; + break; + case "طلب بدل السكن مقدما": + returnImage = "assets/images/services_icons/housing_allowance.svg"; + break; + case "شهادات تعريف الموظف": + returnImage = "assets/images/services_icons/employee_certificates.svg"; + break; + case "البيانات الشخصية": + returnImage = "assets/images/personal-info.svg"; + break; + case "الحضور": + returnImage = "assets/images/services_icons/my_attendance.svg"; + break; + case "طلبات أخرى": + returnImage = "assets/images/services_icons/other_requests.svg"; + break; + case "الإجازات": + returnImage = "assets/images/services_icons/my_leaves.svg"; + break; + case "طلب تذكرة": + returnImage = "assets/images/services_icons/ticket_bal.svg"; + break; + case "قاعدة الاجازات": + returnImage = "assets/images/services_icons/vacation_rule.svg"; + break; + default: + returnImage = "assets/images/monthly_attendance.svg"; + break; + } + return returnImage; + } + + String getMenuIconEn(String name) { + String returnImage = ""; + switch (name) { + case "Monthly Attendance": + returnImage = "assets/images/services_icons/monthly_attendance.svg"; + break; + case "Payslip": + returnImage = "assets/images/services_icons/payslips.svg"; + break; + case "Change Bank Details": + returnImage = "assets/images/services_icons/change_bank_details.svg"; + break; + case "Housing Advance": + returnImage = "assets/images/services_icons/housing_allowance.svg"; + break; + case "Employee Certificate": + returnImage = "assets/images/services_icons/employee_certificates.svg"; + break; + case "Personal Information": + returnImage = "assets/images/personal-info.svg"; + break; + case "My Attendance": + returnImage = "assets/images/services_icons/my_attendance.svg"; + break; + case "Other Requests": + returnImage = "assets/images/services_icons/other_requests.svg"; + break; + case "My Leave": + returnImage = "assets/images/services_icons/my_leaves.svg"; + break; + case "Ticket Request": + returnImage = "assets/images/services_icons/ticket_bal.svg"; + break; + case "Vacation Rule": + returnImage = "assets/images/services_icons/vacation_rule.svg"; + break; + default: + returnImage = "assets/images/monthly_attendance.svg"; + break; + } + return returnImage; + } + void handleOnPress(context, GetMenuEntriesList menuEntry) { var pro = Provider.of(context, listen: false); if (menuEntry.requestType == "MONTHLY_ATTENDANCE") { diff --git a/lib/ui/leave_balance/add_leave_balance_screen.dart b/lib/ui/leave_balance/add_leave_balance_screen.dart index 864a507..85337aa 100644 --- a/lib/ui/leave_balance/add_leave_balance_screen.dart +++ b/lib/ui/leave_balance/add_leave_balance_screen.dart @@ -92,7 +92,7 @@ class _AddLeaveBalanceScreenState extends State { } void validateAbsenceTransaction() async { - // try { + try { Utils.showLoading(context); Map dffDataMap = {}; for (int i = 1; i <= 20; i++) { @@ -100,7 +100,8 @@ class _AddLeaveBalanceScreenState extends State { for (int dffIndex = 0; dffIndex < getabsenceDffStructureList.length; dffIndex++) { if ("ATTRIBUTE$i" == getabsenceDffStructureList[dffIndex].aPPLICATIONCOLUMNNAME) { if (getabsenceDffStructureList[dffIndex].fORMATTYPE == "X") { - dffDataMap["P_ATTRIBUTE$i"] = getabsenceDffStructureList[dffIndex].eSERVICESDV!.pIDCOLUMNNAME != null ? Utils.formatDate(getabsenceDffStructureList[dffIndex].eSERVICESDV!.pIDCOLUMNNAME!) : ""; + dffDataMap["P_ATTRIBUTE$i"] = + getabsenceDffStructureList[dffIndex].eSERVICESDV!.pIDCOLUMNNAME != null ? Utils.formatDate(getabsenceDffStructureList[dffIndex].eSERVICESDV!.pIDCOLUMNNAME!) : ""; } else { dffDataMap["P_ATTRIBUTE$i"] = getabsenceDffStructureList[dffIndex].eSERVICESDV?.pIDCOLUMNNAME; } @@ -108,9 +109,17 @@ class _AddLeaveBalanceScreenState extends State { } } } - await LeaveBalanceApiClient().validateAbsenceTransaction(selectedAbsenceType!.dESCFLEXCONTEXTCODE!, "HR_LOA_SS", selectedAbsenceType!.aBSENCEATTENDANCETYPEID!, - selectedReplacementEmployee != null ? selectedReplacementEmployee!.userName! : "", DateUtil.getFormattedDate(startDateTime!, "MM/dd/yyyy"), DateUtil.getFormattedDate(endDateTime!, "MM/dd/yyyy"), -999, dffDataMap, - comments: comment); + await LeaveBalanceApiClient() + .validateAbsenceTransaction( + selectedAbsenceType!.dESCFLEXCONTEXTCODE!, + "HR_LOA_SS", + selectedAbsenceType!.aBSENCEATTENDANCETYPEID!, + selectedReplacementEmployee != null ? selectedReplacementEmployee!.userName! : "", + DateUtil.getFormattedDate(startDateTime!, "MM/dd/yyyy"), + DateUtil.getFormattedDate(endDateTime!, "MM/dd/yyyy"), + -999, + dffDataMap, + comments: comment); SumbitAbsenceTransactionList submit = await LeaveBalanceApiClient().submitAbsenceTransaction( selectedAbsenceType!.dESCFLEXCONTEXTCODE!, @@ -126,13 +135,13 @@ class _AddLeaveBalanceScreenState extends State { Utils.hideLoading(context); await Navigator.pushNamed(context, AppRoutes.requestSubmitScreen, arguments: RequestSubmitScreenParams(LocaleKeys.submit.tr(), submit.pTRANSACTIONID!, "", "add_leave_balance")); - // Utils.showLoading(context); + Utils.showLoading(context); await LeaveBalanceApiClient().cancelHrTransaction(submit.pTRANSACTIONID!); - // Utils.hideLoading(context); - // } catch (ex) { - // Utils.hideLoading(context); - // Utils.handleException(ex, context, null); - // } + Utils.hideLoading(context); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } } @override @@ -221,7 +230,7 @@ class _AddLeaveBalanceScreenState extends State { onTap: () { showMyBottomSheet( context, - callBackFunc: (){}, + callBackFunc: () {}, child: SearchEmployeeBottomSheet( title: LocaleKeys.searchForEmployee.tr(), apiMode: LocaleKeys.delegate.tr(), diff --git a/lib/ui/leave_balance/leave_balance_screen.dart b/lib/ui/leave_balance/leave_balance_screen.dart index f3a1f91..548ca99 100644 --- a/lib/ui/leave_balance/leave_balance_screen.dart +++ b/lib/ui/leave_balance/leave_balance_screen.dart @@ -62,7 +62,7 @@ class _LeaveBalanceState extends State { physics: const BouncingScrollPhysics(), padding: const EdgeInsets.all(21), children: [ - BalancesDashboardWidget(LocaleKeys.currentLeaveBalance.tr(), true), + BalancesDashboardWidget(LocaleKeys.currentLeaveBalance.tr(), true), 12.height, absenceTransList == null ? const SizedBox() @@ -76,14 +76,23 @@ class _LeaveBalanceState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - ItemDetailView(LocaleKeys.startDateT.tr(), absenceTransList![index].sTARTDATE ?? ""), - ItemDetailView(LocaleKeys.endDateT.tr(), absenceTransList![index].eNDDATE ?? ""), - ItemDetailView(LocaleKeys.absenceType.tr(), absenceTransList![index].aBSENCETYPE ?? ""), - ItemDetailView(LocaleKeys.absenceCategory.tr(), absenceTransList![index].aBSENCECATEGORY ?? ""), - ItemDetailView(LocaleKeys.days.tr(), absenceTransList![index].aBSENCEDAYS?.toString() ?? ""), - ItemDetailView(LocaleKeys.hours.tr(), absenceTransList![index].aBSENCEHOURS?.toString() ?? ""), - ItemDetailView(LocaleKeys.approvalStatus.tr(), absenceTransList![index].aPPROVALSTATUS ?? ""), - ItemDetailView(LocaleKeys.absenceStatus.tr(), absenceTransList![index].aBSENCESTATUS ?? ""), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.startDateT.tr(), absenceTransList![index].sTARTDATE ?? ""), + ItemDetailViewCol(LocaleKeys.endDateT.tr(), absenceTransList![index].eNDDATE ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.absenceType.tr(), absenceTransList![index].aBSENCETYPE ?? ""), + ItemDetailViewCol(LocaleKeys.absenceCategory.tr(), absenceTransList![index].aBSENCECATEGORY ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.days.tr(), absenceTransList![index].aBSENCEDAYS?.toString() ?? ""), + ItemDetailViewCol(LocaleKeys.hours.tr(), absenceTransList![index].aBSENCEHOURS?.toString() ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.approvalStatus.tr(), absenceTransList![index].aPPROVALSTATUS ?? ""), + ItemDetailViewCol(LocaleKeys.absenceStatus.tr(), absenceTransList![index].aBSENCESTATUS ?? ""), + isItLast: true, + ), ], ).objectContainerView(), separatorBuilder: (cxt, index) => 12.height, diff --git a/lib/ui/login/forgot_password_screen.dart b/lib/ui/login/forgot_password_screen.dart index 6c1868e..b2d5c92 100644 --- a/lib/ui/login/forgot_password_screen.dart +++ b/lib/ui/login/forgot_password_screen.dart @@ -50,17 +50,25 @@ class _ForgotPasswordScreenState extends State { context, 1, int.tryParse(_basicMemberInformation?.pMOBILENUMBER ?? ""), - (value) async { + (value,TextEditingController _pinPutController) async { Utils.showLoading(context); - GenericResponseModel? genericResponseModel = await LoginApiClient().checkPublicActivationCode(value, employeeId.text); - if (genericResponseModel?.errorMessage != null) { - Utils.showToast(genericResponseModel?.errorMessage ?? ""); - return; - } - Utils.hideLoading(context); - await Navigator.pushNamed(context, AppRoutes.newPassword, arguments: employeeId.text); - Navigator.pop(context); - Navigator.pop(context); + try{ + GenericResponseModel? genericResponseModel = await LoginApiClient().checkPublicActivationCode(value, employeeId.text); + if (genericResponseModel?.errorMessage != null) { + Utils.showToast(genericResponseModel?.errorMessage ?? ""); + return; + } + Utils.hideLoading(context); + await Navigator.pushNamed(context, AppRoutes.newPassword, arguments: employeeId.text); + Navigator.pop(context); + Navigator.pop(context); + }catch(ex){ + print(ex); + _pinPutController.clear(); + otpFieldClear.value = ""; + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } }, () => { Navigator.pop(context), diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index c62a7a1..20fa8bc 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -142,11 +142,11 @@ class _LoginScreenState extends State { isAppOpenBySystem = (ModalRoute.of(context)!.settings.arguments ?? true) as bool; if (!kReleaseMode) { // username.text = "15444"; // Maha User - username.text = "15153"; // Tamer User - password.text = "Abcd@12345"; - // - // username.text = "210038"; // Hashim User - // password.text = "123"; + // username.text = "15153"; // Tamer User + // password.text = "Abcd@12345"; + + // username.text = "206535"; // Hashim User + // password.text = "Namira786"; } if (isAppOpenBySystem!) checkFirebaseToken(); } diff --git a/lib/ui/login/verify_last_login_screen.dart b/lib/ui/login/verify_last_login_screen.dart index 6e51e51..81d1ba2 100644 --- a/lib/ui/login/verify_last_login_screen.dart +++ b/lib/ui/login/verify_last_login_screen.dart @@ -58,7 +58,8 @@ class _VerifyLastLoginScreenState extends State { @override Widget build(BuildContext context) { mobileLoginInfoListModel ??= ModalRoute.of(context)!.settings.arguments as GetMobileLoginInfoListModel; - String empName = AppState().isArabic(context) ? AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr! : AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn!; + // String empName = AppState().isArabic(context) ? AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr! : AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn!; + String empName = mobileLoginInfoListModel!.employeeName!; return Scaffold( appBar: AppBar( @@ -68,7 +69,7 @@ class _VerifyLastLoginScreenState extends State { ? LocaleKeys.viewBusinessCard.tr().toText12(color: MyColors.textMixColor, isUnderLine: true).onPress(() { showMDialog(context, child: BusinessCardDialog()); }) - : null, + : Container(), actions: [ Center( child: LocaleKeys.employeeDigitalID.tr().toText12(color: MyColors.textMixColor, isUnderLine: true).onPress(() { @@ -334,16 +335,16 @@ class _VerifyLastLoginScreenState extends State { if (!isDirectLogin) BasicMemberInformationModel? memberInformationModel = await LoginApiClient().mohemmSendActivationCodeByOTPNotificationType(0, AppState().memberLoginList?.pMOBILENUMBER, sendVerificationFlat, AppState().getUserName); - if (isDirectLogin) performDirectApiCall(_title, _icon, _flag, ""); + if (isDirectLogin) performDirectApiCall(_title, _icon, _flag, "", null); if (!isDirectLogin) Utils.hideLoading(context); if (!isDirectLogin) OtpDialog( context, sendVerificationFlat, int.tryParse(AppState().memberLoginList?.pMOBILENUMBER ?? ""), - (value) async { + (value, TextEditingController _pinPutController) async { Utils.showLoading(context); - performDirectApiCall(_title, _icon, _flag, value); + performDirectApiCall(_title, _icon, _flag, value, _pinPutController); }, () => { Navigator.pop(context), @@ -358,7 +359,7 @@ class _VerifyLastLoginScreenState extends State { } } - Future performDirectApiCall(String _title, String _icon, int _flag, String value, {bool isDirectLogin = false}) async { + Future performDirectApiCall(String _title, String _icon, int _flag, String value, TextEditingController? _pinPutController, {bool isDirectLogin = false}) async { try { GenericResponseModel? genericResponseModel = await LoginApiClient().checkActivationCode(false, AppState().memberLoginList?.pMOBILENUMBER, value, AppState().getUserName); GenericResponseModel? genericResponseModel1 = await LoginApiClient().insertMobileLoginInfoNEW( @@ -371,14 +372,18 @@ class _VerifyLastLoginScreenState extends State { mobileLoginInfoListModel!.deviceToken!, Platform.isAndroid ? "android" : "ios"); AppState().setMemberInformationListModel = genericResponseModel!.memberInformationList?.first; - if (genericResponseModel?.errorMessage != null) { - Utils.showToast(genericResponseModel?.errorMessage ?? ""); + if (genericResponseModel.errorMessage != null) { + Utils.showToast(genericResponseModel.errorMessage ?? ""); // Navigator.pop(context); } Utils.hideLoading(context); Navigator.pop(context); Navigator.pushNamedAndRemoveUntil(context, AppRoutes.dashboard, (Route route) => false); } catch (ex) { + if (_pinPutController != null) { + _pinPutController.clear(); + otpFieldClear.value = ""; + } Utils.hideLoading(context); Utils.handleException(ex, context, null); } diff --git a/lib/ui/login/verify_login_screen.dart b/lib/ui/login/verify_login_screen.dart index 8dba16c..ce21b63 100644 --- a/lib/ui/login/verify_login_screen.dart +++ b/lib/ui/login/verify_login_screen.dart @@ -617,7 +617,7 @@ class _VerifyLoginScreenState extends State { context, sendVerificationFlat, int.tryParse(AppState().memberLoginList?.pMOBILENUMBER ?? ""), - (value) async { + (value, TextEditingController _pinPutController) async { Utils.showLoading(context); try { GenericResponseModel? genericResponseModel = await LoginApiClient().checkActivationCode(true, AppState().memberLoginList?.pMOBILENUMBER, value, AppState().getUserName); @@ -652,6 +652,8 @@ class _VerifyLoginScreenState extends State { Navigator.pushNamedAndRemoveUntil(context, AppRoutes.dashboard, (Route route) => false); } catch (ex) { print(ex); + _pinPutController.clear(); + otpFieldClear.value = ""; Utils.hideLoading(context); Utils.handleException(ex, context, null); } diff --git a/lib/ui/marathon/widgets/marathon_banner.dart b/lib/ui/marathon/widgets/marathon_banner.dart index ee1c087..a142537 100644 --- a/lib/ui/marathon/widgets/marathon_banner.dart +++ b/lib/ui/marathon/widgets/marathon_banner.dart @@ -142,7 +142,7 @@ class MarathonBanner extends StatelessWidget { bottom: 0, child: RotatedBox( quarterTurns: 4, - child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.darkDigitColor), + child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.whiteColor), ).paddingAll(15), ) : Positioned( @@ -150,7 +150,7 @@ class MarathonBanner extends StatelessWidget { left: 0, child: RotatedBox( quarterTurns: 2, - child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.darkDigitColor), + child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.whiteColor), ).paddingAll(15), ), ], diff --git a/lib/ui/misc/request_submit_screen.dart b/lib/ui/misc/request_submit_screen.dart index 8ca9a0c..f5a2d1e 100644 --- a/lib/ui/misc/request_submit_screen.dart +++ b/lib/ui/misc/request_submit_screen.dart @@ -68,7 +68,7 @@ class _RequestSubmitScreenState extends State { } void submitRequest() async { - try { + try { Utils.showLoading(context); List> list = []; if (attachmentFiles.isNotEmpty) { @@ -90,7 +90,7 @@ class _RequestSubmitScreenState extends State { if (params!.approvalFlag == 'phone_numbers') { await ProfileApiClient().startPhoneApprovalProcess( - LocaleKeys.submit.tr(), + "SUBMIT", comments.text, params!.pItemId, params!.transactionId, @@ -104,7 +104,7 @@ class _RequestSubmitScreenState extends State { ); } else if (params!.approvalFlag == 'family_member') { await ProfileApiClient().getApproves( - LocaleKeys.submit.tr(), + "SUBMIT", comments.text, params!.transactionId!.toInt(), params!.pItemId.toString(), @@ -124,14 +124,14 @@ class _RequestSubmitScreenState extends State { ); } else if (params!.approvalFlag == 'eit') { await MyAttendanceApiClient().startEitApprovalProcess( - LocaleKeys.submit.tr(), + "SUBMIT", comments.text, params!.pItemId, params!.transactionId, ); }else if (params!.approvalFlag == 'endEmployment') { await TerminationDffApiClient().startTermApprovalProcess( - LocaleKeys.submit.tr(), + "SUBMIT", comments.text, params!.pItemId, params!.transactionId, diff --git a/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart b/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart index 0a6c9d4..6cda072 100644 --- a/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart +++ b/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart @@ -5,6 +5,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/leave_balance_api_client.dart'; import 'package:mohem_flutter_app/api/my_attendance_api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; @@ -43,7 +44,8 @@ class _DynamicInputScreenState extends State { void getTransactionsStructure() async { try { Utils.showLoading(context); - genericResponseModel = await MyAttendanceApiClient().getEitDffStructure(dynamicParams!.dynamicId); + + genericResponseModel = await MyAttendanceApiClient().getEitDffStructure(dynamicParams!.dynamicId, dynamicParams!.selectedEmp); dESCFLEXCONTEXTCODE = genericResponseModel!.pDESCFLEXCONTEXTCODE ?? ""; descFlexConTextTitle = genericResponseModel!.pDESCFLEXCONTEXTNAME ?? ""; getEitDffStructureList = genericResponseModel?.getEITDFFStructureList ?? []; @@ -90,7 +92,7 @@ class _DynamicInputScreenState extends State { values.add(ValidateEitTransactionModel(dATEVALUE: null, nAME: "PEI_EXTRA_INFO_ID", nUMBERVALUE: -1, tRANSACTIONNUMBER: 1, vARCHAR2VALUE: null).toJson()); values.add(ValidateEitTransactionModel(dATEVALUE: null, nAME: "PEI_OBJECT_VERSION_NUMBER", nUMBERVALUE: 0, tRANSACTIONNUMBER: 1, vARCHAR2VALUE: null).toJson()); - genericResponseModel = await MyAttendanceApiClient().validateEitTransaction(dESCFLEXCONTEXTCODE, dynamicParams!.dynamicId, values); + genericResponseModel = await MyAttendanceApiClient().validateEitTransaction(dESCFLEXCONTEXTCODE, dynamicParams!.dynamicId, values, empID:dynamicParams!.selectedEmp ??''); SubmitEITTransactionList submitEITTransactionList = await MyAttendanceApiClient().submitEitTransaction(dESCFLEXCONTEXTCODE, dynamicParams!.dynamicId, values); Utils.hideLoading(context); await Navigator.pushNamed(context, AppRoutes.requestSubmitScreen, @@ -112,14 +114,14 @@ class _DynamicInputScreenState extends State { Utils.showLoading(context); String segmentId = structureList.cHILDSEGMENTSVS!; if (dESCFLEXCONTEXTCODE.isEmpty) dESCFLEXCONTEXTCODE = structureList.dESCFLEXCONTEXTCODE!; - List filteredList = getEitDffStructureList?.where((element) => element.cHILDSEGMENTSVSSplited!.contains(segmentId)).toList() ?? []; - List> values = filteredList + List> values = filteredList .map((e) => GetSetValuesRequestModel( sEGMENTNAME: e.sEGMENTNAME, vALUECOLUMNNAME: e.eSERVICESDV!.pVALUECOLUMNNAME, dESCRIPTION: "", iDCOLUMNNAME: e.eSERVICESDV!.pIDCOLUMNNAME, fLEXVALUESETNAME: e.fLEXVALUESETNAME) .toJson()) .toList(); - List eServicesResponseModel = await MyAttendanceApiClient().getValueSetValues(segmentId, structureList.dESCFLEXCONTEXTCODE!, structureList.dESCFLEXNAME!, values); + List eServicesResponseModel = await MyAttendanceApiClient().getValueSetValues(segmentId, structureList.dESCFLEXCONTEXTCODE!, structureList.dESCFLEXNAME!, + values, empID:dynamicParams!.selectedEmp ??'', parentValue: structureList.eSERVICESDV!.pVALUECOLUMNNAME ); List abc = genericResponseModel?.getEITDFFStructureList ?? []; getEitDffStructureList = abc; int index = getEitDffStructureList!.indexWhere((element) => element.sEGMENTNAME == structureList.cHILDSEGMENTSVS); @@ -155,7 +157,7 @@ class _DynamicInputScreenState extends State { List> getSetList = getDefaultValuesIonicLogic(parent); if (getSetList.isNotEmpty) { - ESERVICESDV defaultValue = await MyAttendanceApiClient().getDefaultValue(segmentId, structureList.dESCFLEXCONTEXTCODE!, structureList.dESCFLEXNAME!, getSetList); + ESERVICESDV defaultValue = await MyAttendanceApiClient().getDefaultValue(segmentId, structureList.dESCFLEXCONTEXTCODE!, structureList.dESCFLEXNAME!, getSetList, dynamicParams!.selectedEmp); int index = getEitDffStructureList!.indexWhere((element) => element.sEGMENTNAME == segmentId); getEitDffStructureList![index].eSERVICESDV = defaultValue; } @@ -340,7 +342,7 @@ class _DynamicInputScreenState extends State { String? text = data?.pVALUECOLUMNNAME; String? val = data?.pIDCOLUMNNAME; - if ((val ?? "").isEmpty && parentsList[i].IsRequired == "REQUIRED") { + if ((val ?? "").isEmpty && parentsList[i].isRequired == "REQUIRED") { //alert(parentsList[i].Name +" Is required"); return []; } else {} @@ -362,7 +364,7 @@ class _DynamicInputScreenState extends State { String? text = data?.pVALUECOLUMNNAME; String? val = data?.pIDCOLUMNNAME; - if ((val ?? "").isEmpty && parentsList[i].IsRequired == "REQUIRED") { + if ((val ?? "").isEmpty && parentsList[i].isRequired == "REQUIRED") { //alert(parentsList[i].Name +" Is required"); return []; } else {} @@ -392,6 +394,9 @@ class _DynamicInputScreenState extends State { Widget build(BuildContext context) { if (dynamicParams == null) { dynamicParams = ModalRoute.of(context)!.settings.arguments as DynamicListViewParams; + if(dynamicParams!.selectedEmp.isNotEmpty){ + AppState().postParamsJson['P_SELECTED_EMPLOYEE_NUMBER'] = dynamicParams!.selectedEmp; + } getTransactionsStructure(); } return Scaffold( @@ -448,7 +453,7 @@ class _DynamicInputScreenState extends State { return DynamicTextFieldWidget( (model.sEGMENTPROMPT ?? "") + (model.rEQUIREDFLAG == "Y" ? "*" : ""), model.eSERVICESDV?.pIDCOLUMNNAME ?? "", - isReadOnly: model.rEADONLY == "Y", + // isReadOnly: model.rEADONLY == "Y", isInputTypeNum: true, onChange: (text) { model.fieldAnswer = text; diff --git a/lib/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart b/lib/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart index 57b61d9..5c4053c 100644 --- a/lib/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart +++ b/lib/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart @@ -6,6 +6,7 @@ import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/models/get_eit_transaction_list_model.dart'; +import 'package:mohem_flutter_app/models/worklist/hr/eit_otification_body_model.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/balances_dashboard_widget.dart'; import 'package:mohem_flutter_app/widgets/item_detail_view_widget.dart'; @@ -16,8 +17,12 @@ class DynamicListViewParams { String uRL; String requestID; String colsURL; + bool isUpdate; - DynamicListViewParams(this.title, this.dynamicId, {this.uRL = 'GET_EIT_DFF_STRUCTURE', this.requestID = '', this.colsURL = ''}); + List? collectionNotificationList; + final String selectedEmp; + + DynamicListViewParams(this.title, this.dynamicId, {this.selectedEmp ='', this.uRL = 'GET_EIT_DFF_STRUCTURE', this.requestID = '', this.colsURL = '', this.isUpdate = false, this.collectionNotificationList}); } class DynamicListViewScreen extends StatefulWidget { @@ -32,6 +37,7 @@ class DynamicListViewScreen extends StatefulWidget { class _DynamicListViewScreenState extends State { List? getEITTransactionList; DynamicListViewParams? dynamicParams; + // String? empId; @override void initState() { @@ -41,7 +47,7 @@ class _DynamicListViewScreenState extends State { void getTransactions() async { try { Utils.showLoading(context); - getEITTransactionList = await MyAttendanceApiClient().getEitTransaction(dynamicParams!.dynamicId); + getEITTransactionList = await MyAttendanceApiClient().getEitTransaction(dynamicParams!.dynamicId, dynamicParams!.selectedEmp); getEITTransactionList?.forEach((element) { element.collectionTransaction = element.collectionTransaction?.where((elemen) => elemen.dISPLAYFLAG == "Y").toList() ?? []; }); @@ -62,6 +68,17 @@ class _DynamicListViewScreenState extends State { late bool isTicketRequest; + List getEITWidgetsList(List? collectionTransaction) { + List itemsList = []; + + for (int t = 0; t < (collectionTransaction ?? []).length; t++) { + if (collectionTransaction![t].dISPLAYFLAG == "Y") { + itemsList.add(ItemDetailViewCol(collectionTransaction[t].sEGMENTPROMPT!, collectionTransaction[t].sEGMENTVALUEDSP ?? "")); + } + } + return itemsList; + } + @override Widget build(BuildContext context) { if (dynamicParams == null) { @@ -84,16 +101,45 @@ class _DynamicListViewScreenState extends State { shrinkWrap: true, itemBuilder: (cxt, int parentIndex) => getEITTransactionList![parentIndex].collectionTransaction!.isEmpty ? const SizedBox() - : Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - for (int t = 0; t < (getEITTransactionList![parentIndex].collectionTransaction ?? []).length; t++) - if (getEITTransactionList![parentIndex].collectionTransaction![t].dISPLAYFLAG == "Y") - ItemDetailView(getEITTransactionList![parentIndex].collectionTransaction![t].sEGMENTPROMPT!, - getEITTransactionList![parentIndex].collectionTransaction![t].sEGMENTVALUEDSP ?? ""), - ], + : GridView( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 1.9 / 0.75), + padding: const EdgeInsets.all(12.0), + shrinkWrap: true, + primary: false, + physics: const ScrollPhysics(), + children: getEITWidgetsList(getEITTransactionList![parentIndex].collectionTransaction), ).objectContainerView(), + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisSize: MainAxisSize.min, + // children: [ + // for (int t = 0; t < (getEITTransactionList![parentIndex].collectionTransaction ?? []).length; t++) + // if (getEITTransactionList![parentIndex].collectionTransaction![t].dISPLAYFLAG == "Y") + // // ItemDetailView(getEITTransactionList![parentIndex].collectionTransaction![t].sEGMENTPROMPT!, + // // getEITTransactionList![parentIndex].collectionTransaction![t].sEGMENTVALUEDSP ?? ""), + // // GridView( + // // gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2), + // // padding: const EdgeInsets.only(left: 21, right: 21, bottom: 21, top: 12), + // // shrinkWrap: true, + // // primary: false, + // // physics: const ScrollPhysics(), + // // children: getEITWidgetsList(getEITTransactionList![parentIndex].collectionTransaction), + // // ) + // // ItemDetailViewCol(getEITTransactionList![parentIndex].collectionTransaction![t].sEGMENTPROMPT!, + // // getEITTransactionList![parentIndex].collectionTransaction![t].sEGMENTVALUEDSP ?? ""), + // + // // GridView( + // // gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2), + // // padding: const EdgeInsets.only(left: 21, right: 21, bottom: 21, top: 12), + // // shrinkWrap: true, + // // primary: false, + // // physics: const ScrollPhysics(), + // // children: getEITWidgetsList(getEITTransactionList![parentIndex].collectionTransaction), + // // ) + // // ItemDetailView(getEITTransactionList![parentIndex].collectionTransaction![t].sEGMENTPROMPT!, + // // getEITTransactionList![parentIndex].collectionTransaction![t].sEGMENTVALUEDSP ?? ""), + // ], + // ).objectContainerView(), separatorBuilder: (cxt, index) => 12.height, itemCount: getEITTransactionList!.length)), ], diff --git a/lib/ui/my_attendance/services_menu_list_screen.dart b/lib/ui/my_attendance/services_menu_list_screen.dart index c05e035..cd76132 100644 --- a/lib/ui/my_attendance/services_menu_list_screen.dart +++ b/lib/ui/my_attendance/services_menu_list_screen.dart @@ -8,6 +8,7 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; +import 'package:mohem_flutter_app/models/my_team/get_employee_subordinates_list.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; @@ -16,8 +17,9 @@ import 'package:provider/provider.dart'; class ServicesMenuListScreenParams { final String title; final List list; - - ServicesMenuListScreenParams(this.title, this.list); + final String selectedEmp; + GetEmployeeSubordinatesList? getEmployeeSubordinates; + ServicesMenuListScreenParams(this.title, this.list, {this.selectedEmp =''}); } class ServicesMenuListScreen extends StatelessWidget { @@ -56,7 +58,7 @@ class ServicesMenuListScreen extends StatelessWidget { return; } if (servicesMenuData.list[index].requestType == "EIT") { - Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(servicesMenuData.list[index].prompt!, servicesMenuData.list[index].functionName!)); + Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(servicesMenuData.list[index].prompt!, servicesMenuData.list[index].functionName!, selectedEmp: servicesMenuData.selectedEmp)); } else { if (servicesMenuData.list[index].requestType == "TERMINATION") { Navigator.pushNamed(context, AppRoutes.endEmploymentScreen, diff --git a/lib/ui/my_team/create_request.dart b/lib/ui/my_team/create_request.dart index a4c6c83..53205c9 100644 --- a/lib/ui/my_team/create_request.dart +++ b/lib/ui/my_team/create_request.dart @@ -65,13 +65,14 @@ class _CreateRequestState extends State { return menus; } - void handleOnPress(context, Menus menu) { + void handleOnPress(context, Menus menu) { + if (menu.menuEntry.menuEntryType == "FUNCTION") { if (menu.menuEntry.requestType == "EIT") { Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(menu.menuEntry.prompt!, menu.menuEntry.functionName!)); } else {} } else { - Navigator.pushNamed(context, AppRoutes.servicesMenuListScreen, arguments: ServicesMenuListScreenParams(menu.menuEntry.prompt!, menu.menuEntiesList)); + Navigator.pushNamed(context, AppRoutes.servicesMenuListScreen, arguments: ServicesMenuListScreenParams(menu.menuEntry.prompt!, menu.menuEntiesList, selectedEmp: getEmployeeSubordinates?.eMPLOYEENUMBER??'')); } return; } diff --git a/lib/ui/my_team/employee_details.dart b/lib/ui/my_team/employee_details.dart index d0e167d..9fc0070 100644 --- a/lib/ui/my_team/employee_details.dart +++ b/lib/ui/my_team/employee_details.dart @@ -256,19 +256,29 @@ class _EmployeeDetailsState extends State { child: Column( children: [ Text(LocaleKeys.addFavoriteList).tr(namedArgs: {'name': '${getEmployeeSubordinates!.eMPLOYEENAME}'}), - Image.memory( - Utils.getPostBytes( - getEmployeeSubordinates!.eMPLOYEEIMAGE ?? "", - ), - errorBuilder: (BuildContext context, error, stackTrace) { - return SvgPicture.asset( - "assets/images/user.svg", height: 50, width: 50, - ); - }, - width: 50, - height: 50, - fit: BoxFit.cover, - ).circle(50).paddingOnly(top: 21), + getEmployeeSubordinates!.eMPLOYEEIMAGE == null + ? SvgPicture.asset( + "assets/images/user.svg", + height: 68, + width: 68,) + : CircleAvatar( + radius: 68, + backgroundImage: MemoryImage(Utils.dataFromBase64String(getEmployeeSubordinates!.eMPLOYEEIMAGE!)), + backgroundColor: Colors.black, + ), + // Image.memory( + // Utils.getPostBytes( + // getEmployeeSubordinates!.eMPLOYEEIMAGE ?? "", + // ), + // errorBuilder: (BuildContext context, error, stackTrace) { + // return SvgPicture.asset( + // "assets/images/user.svg", height: 50, width: 50, + // ); + // }, + // width: 50, + // height: 50, + // fit: BoxFit.cover, + // ).circle(50).paddingOnly(top: 21), ], ), ), diff --git a/lib/ui/my_team/team_members.dart b/lib/ui/my_team/team_members.dart index 50bf7e7..e7336bb 100644 --- a/lib/ui/my_team/team_members.dart +++ b/lib/ui/my_team/team_members.dart @@ -14,7 +14,8 @@ import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:url_launcher/url_launcher.dart'; class TeamMembers extends StatefulWidget { - const TeamMembers({Key? key}) : super(key: key); + final String? selectedEmp; + const TeamMembers({this.selectedEmp, Key? key}) : super(key: key); @override _TeamMembersState createState() => _TeamMembersState(); @@ -25,6 +26,7 @@ class _TeamMembersState extends State { String searchEmpName =""; String searchEmpNo = ""; String? empId; + List getEmployeeSubordinatesList = []; GetEmployeeSubordinatesList? getEmployeeSubordinates; @@ -37,7 +39,7 @@ class _TeamMembersState extends State { try { Utils.showLoading(context); getEmployeeSubordinatesList = await MyTeamApiClient().getEmployeeSubordinates(searchEmpEmail.toString(), searchEmpName.toString(), searchEmpNo.toString()); - getEmployeeSubordinatesList = await MyTeamApiClient().employeeSubordinates(searchEmpEmail.toString(), searchEmpName.toString(), searchEmpNo.toString(),getEmployeeSubordinates?.eMPLOYEENUMBER); + // getEmployeeSubordinatesList = await MyTeamApiClient().employeeSubordinates(searchEmpEmail.toString(), searchEmpName.toString(), searchEmpNo.toString(),getEmployeeSubordinates?.eMPLOYEENUMBER); Utils.hideLoading(context); setState(() {}); } catch (ex) { diff --git a/lib/ui/my_team/view_attendance.dart b/lib/ui/my_team/view_attendance.dart index c77b1b4..0896d5e 100644 --- a/lib/ui/my_team/view_attendance.dart +++ b/lib/ui/my_team/view_attendance.dart @@ -283,7 +283,6 @@ class _ViewAttendanceState extends State { cellBorderColor: Colors.white, selectionDecoration: BoxDecoration( border: Border.all(color: MyColors.white, width: 10), - borderRadius: const BorderRadius.all(Radius.circular(100)), shape: BoxShape.circle, ), dataSource: MeetingDataSource(_getDataSource()), @@ -440,7 +439,7 @@ class _ViewAttendanceState extends State { expand: false, builder: (_, controller) { dynamic dmyString = getScheduleShiftsDetailsList!.sCHEDULEDATE; - DateTime dateTime1 = DateFormat("MM/dd/yyyy hh:mm:ss a").parse(dmyString); + DateTime dateTime1 = DateFormat("MM/dd/yyyy hh:mm:ss").parse(dmyString); return Column( children: [ Container( diff --git a/lib/ui/profile/add_update_family_member.dart b/lib/ui/profile/add_update_family_member.dart index 61b6639..36a8394 100644 --- a/lib/ui/profile/add_update_family_member.dart +++ b/lib/ui/profile/add_update_family_member.dart @@ -224,8 +224,8 @@ class _AddUpdateFamilyMemberState extends State { onTap: () async { DateTime dateValue = await _selectDate(context); //date = DateFormat('yyyy/MM/dd').format(dateValue); - date = "2022-10-23T09:17:38.653+03:00"; - // date = (DateFormat('yyyy-MM-dd').format(dateValue)+"T"+ dateTime.toString() + dateTime1.toString()); + //date = "2022-10-23T09:17:38.653+03:00"; + date = (DateFormat('yyyy-MM-dd').format(dateValue)); model!.getContactDetailsList!.sEGMENTVALUEDSP = date; print(dateTime); print(dateTime1); @@ -288,7 +288,7 @@ class _AddUpdateFamilyMemberState extends State { isEnable: false, onTap: () async { DateTime dateValue = await _selectDate(context); - date = "2022-10-23T09:17:38.653+03:00"; + date = (DateFormat('yyyy-MM-dd').format(dateValue)); // date = (DateFormat('yyyy-MM-dd').format(dateValue) +"T"+ dateTime.toString() + dateTime1.toString()); // date = DateFormat('yyyy/MM/dd').format(dateValue); model!.getContactDetailsList!.sEGMENTVALUEDSP = date; @@ -349,8 +349,8 @@ class _AddUpdateFamilyMemberState extends State { isEnable: false, onTap: () async { DateTime dateValue = await _selectDate(context); - date = "2022-10-23T09:17:38.653+03:00"; - // date = (DateFormat('yyyy-MM-dd').format(dateValue)+"T"+ dateTime.toString() + dateTime1.toString()); + // date = "2022-10-23T09:17:38.653+03:00"; + date = (DateFormat('yyyy-MM-dd').format(dateValue)); // date = DateFormat('yyyy/MM/dd').format(dateValue); model!.getContactDetailsList!.sEGMENTVALUEDSP = date; setState(() {}); @@ -383,7 +383,7 @@ class _AddUpdateFamilyMemberState extends State { ), ); } else { - DateTime? picked = await showDatePicker(context: context, initialDate: selectedDate, initialEntryMode: DatePickerEntryMode.calendarOnly, firstDate: DateTime(2015, 8), lastDate: DateTime(2101)); + DateTime? picked = await showDatePicker(context: context, initialDate: selectedDate, initialEntryMode: DatePickerEntryMode.calendarOnly, firstDate: DateTime(1920, 1), lastDate: DateTime.now()); if (picked != null && picked != selectedDate) { time = picked; } @@ -392,78 +392,91 @@ class _AddUpdateFamilyMemberState extends State { } void submitUpdateForm() async { - try { - List> values1 = getBasicDetDffStructureList!.map((e) { - String? dateVal = ''; - String? vatcherVal = ''; - int? numberVal; - if (e!.fORMATTYPE == 'N') { - dateVal = null; - vatcherVal = null; - numberVal = int.parse(e.getContactDetailsList!.sEGMENTVALUEDSP??"0"); - } else if (e.fORMATTYPE == 'X') { - dateVal = e!.getContactDetailsList!.sEGMENTVALUEDSP.toString(); - vatcherVal = null; - numberVal = null; - } else { - dateVal = null; - vatcherVal = e!.getContactDetailsList!.sEGMENTVALUEDSP; - numberVal = null; - } - return ValidateEitTransactionModel(dATEVALUE: dateVal, nAME: e.aPPLICATIONCOLUMNNAME, nUMBERVALUE: numberVal, tRANSACTIONNUMBER: 1, vARCHAR2VALUE: vatcherVal.toString()).toJson(); - }).toList(); - List> values2 = getContactDffStructureList!.map((e) { - String? dateVal = ''; - String? vatcherVal = ''; - int? numberVal; - if (e!.fORMATTYPE == 'N') { - dateVal = null; - vatcherVal = null; - numberVal = int.parse(e.getContactDetailsList!.sEGMENTVALUEDSP??"0"); - } else if (e.fORMATTYPE == 'X') { - dateVal = e!.getContactDetailsList!.sEGMENTVALUEDSP; - vatcherVal = null; - numberVal = null; - } else { - dateVal = null; - numberVal = null; - vatcherVal = e!.getContactDetailsList!.sEGMENTVALUEDSP; - } - return ValidateEitTransactionModel(dATEVALUE: dateVal, nAME: e.aPPLICATIONCOLUMNNAME, nUMBERVALUE: numberVal, tRANSACTIONNUMBER: 1, vARCHAR2VALUE: vatcherVal.toString()).toJson(); - }).toList(); - List> values3 = getContactColsStructureList!.map((e) { - // String tempVar = e!.getContactDetailsList!.sEGMENTVALUEDSP ?? ""; - String? dateVal = ''; - String? vatcherVal = ''; - int? numberVal; + print("submit"); + try { + List> values1 = getBasicDetDffStructureList!.map((e) { + String? dateVal = ''; + String? varChar = ''; + int? numberVal; + if (e!.fORMATTYPE == 'N') { + dateVal = null; + varChar = null; + numberVal = int.parse(e.getContactDetailsList!.sEGMENTVALUEDSP ?? "0"); + } else if (e.fORMATTYPE == 'X') { + dateVal = e!.getContactDetailsList!.sEGMENTVALUEDSP.toString(); + varChar = null; + numberVal = null; + } else if (e.eSERVICESVS!.isNotEmpty) { + dateVal = null; + varChar = e!.getContactDetailsList!.vARCHAR2VALUE; + numberVal = null; + } else { + dateVal = null; + varChar = e!.getContactDetailsList!.sEGMENTVALUEDSP; + numberVal = null; + } + return ValidateEitTransactionModel(dATEVALUE: dateVal, nAME: e.aPPLICATIONCOLUMNNAME, nUMBERVALUE: numberVal, tRANSACTIONNUMBER: 1, vARCHAR2VALUE: varChar).toJson(); + }).toList(); + List> values2 = getContactDffStructureList!.map((e) { + String? dateVal = ''; + String? varChar = ''; + int? numberVal; + if (e!.fORMATTYPE == 'N') { + dateVal = null; + varChar = null; + numberVal = int.parse(e.getContactDetailsList!.sEGMENTVALUEDSP ?? "0"); + } else if (e.fORMATTYPE == 'X') { + dateVal = e!.getContactDetailsList!.dATEVALUE; + varChar = null; + numberVal = null; + } else if (e.eSERVICESVS!.isNotEmpty) { + dateVal = null; + varChar = e!.getContactDetailsList!.vARCHAR2VALUE; + numberVal = null; + } else { + dateVal = null; + numberVal = null; + varChar = e!.getContactDetailsList!.sEGMENTVALUEDSP; + } + return ValidateEitTransactionModel(dATEVALUE: dateVal, nAME: e.aPPLICATIONCOLUMNNAME, nUMBERVALUE: numberVal, tRANSACTIONNUMBER: 1, vARCHAR2VALUE: varChar).toJson(); + }).toList(); + List> values3 = getContactColsStructureList!.map((e) { + // String tempVar = e!.getContactDetailsList!.sEGMENTVALUEDSP ?? ""; + String? dateVal = ''; + dynamic? varChar1 = ''; + int? numberVal; - print("e.dATATYPE:${e?.dATATYPE}"); - if (e!.dATATYPE == 'VARCHAR2') { - dateVal = null; - numberVal = null; - vatcherVal = e.getContactDetailsList?.sEGMENTVALUEDSP ?? ""; - } else if (e.dATATYPE == 'DATE') { - dateVal = e.getContactDetailsList!.sEGMENTVALUEDSP.toString(); - print("dateVal:${dateVal ?? ""}"); - vatcherVal = null; - numberVal = null; - } else if (e.dATATYPE == 'NUMBER') { - dateVal = null; - vatcherVal = null; - numberVal = int.parse(e.getContactDetailsList!.sEGMENTVALUEDSP??"0"); - } else {} - return ValidateEitTransactionModel(dATEVALUE: dateVal, nAME: e.aPPLICATIONCOLUMNNAME, nUMBERVALUE: numberVal, tRANSACTIONNUMBER: 1, vARCHAR2VALUE: vatcherVal).toJson(); - }).toList(); - List> valuesFinal = [...values1, ...values2, ...values3]; - Utils.showLoading(context); - submitContactTransactionList = await ProfileApiClient().submitContactTransactionAddAndUpdate(args['actionType'], args['relationID'] ?? null, valuesFinal); - var transactionId = submitContactTransactionList!.pTRANSACTIONID; - var itemKey = submitContactTransactionList!.pITEMKEY; - Utils.hideLoading(context); - Navigator.pushNamed(context, AppRoutes.requestSubmitScreen, - arguments: RequestSubmitScreenParams(LocaleKeys.profile_familyDetails.tr(), transactionId!.toInt(), itemKey.toString(), 'family_member')); - setState(() {}); + print("e.dATATYPE:${e?.dATATYPE}"); + if (e!.dATATYPE == 'VARCHAR2') { + dateVal = null; + numberVal = null; + if (e.objectValuesList != null && e.objectValuesList!.isNotEmpty) { + varChar1 = e.getContactDetailsList?.vARCHAR2VALUE ?? ""; + } else { + varChar1 = e.getContactDetailsList?.sEGMENTVALUEDSP ?? ""; + } + } else if (e.dATATYPE == 'DATE') { + dateVal = e.getContactDetailsList!.sEGMENTVALUEDSP.toString(); + varChar1 = null; + numberVal = null; + } else if (e.dATATYPE == 'NUMBER') { + dateVal = null; + varChar1 = null; + numberVal = int.parse(e.getContactDetailsList!.sEGMENTVALUEDSP ?? "0"); + } + return ValidateEitTransactionModel(dATEVALUE: dateVal, nAME: e.aPPLICATIONCOLUMNNAME, nUMBERVALUE: numberVal, tRANSACTIONNUMBER: 1, vARCHAR2VALUE: varChar1).toJson(); + }).toList(); + List> valuesFinal = [...values1, ...values2, ...values3]; + Utils.showLoading(context); + submitContactTransactionList = await ProfileApiClient().submitContactTransactionAddAndUpdate(args['actionType'], args['relationID'] ?? null, valuesFinal); + var transactionId = submitContactTransactionList!.pTRANSACTIONID; + var itemKey = submitContactTransactionList!.pITEMKEY; + Utils.hideLoading(context); + Navigator.pushNamed(context, AppRoutes.requestSubmitScreen, + arguments: RequestSubmitScreenParams(LocaleKeys.profile_familyDetails.tr(), transactionId!.toInt(), itemKey.toString(), 'family_member')); + setState(() {}); } catch (ex) { + print(ex); Utils.hideLoading(context); Utils.handleException(ex, context, null); } diff --git a/lib/ui/profile/contact_details.dart b/lib/ui/profile/contact_details.dart index 9a6f25e..95c867a 100644 --- a/lib/ui/profile/contact_details.dart +++ b/lib/ui/profile/contact_details.dart @@ -129,7 +129,12 @@ class _ContactDetailsState extends State { separatorBuilder: (cxt, index) => 12.height, itemCount: getEmployeeAddressList.length), if (menuEntriesAddress.updateButton == 'Y') - Positioned( + AppState().isArabic(context)? Positioned( + top: 1, + left: 1, + child: const Icon(Icons.edit_location_alt_outlined, size: 20).onPress(addUpdateAddress), + ) + :Positioned( top: 1, right: 1, child: const Icon(Icons.edit_location_alt_outlined, size: 20).onPress(addUpdateAddress), diff --git a/lib/ui/profile/dynamic_screens/dynamic_input_address_screen.dart b/lib/ui/profile/dynamic_screens/dynamic_input_address_screen.dart index cc8e861..226eae9 100644 --- a/lib/ui/profile/dynamic_screens/dynamic_input_address_screen.dart +++ b/lib/ui/profile/dynamic_screens/dynamic_input_address_screen.dart @@ -88,7 +88,7 @@ class _DynamicInputScreenState extends State { getAddressDffStructureList! .insert(1, GetAddressDffStructureList(fORMATTYPE: 'C', sEGMENTPROMPT: LocaleKeys.profile_country.tr(), eSERVICESVS: eSerivceList, getEmployeeAddressList: GetEmployeeAddressList())); } - print(getAddressDffStructureList); + // genericResponseModel = await ProfileApiClient().getColStructure(dynamicParams!.dynamicId, 'GET_BASIC_DET_COLS_STRUCTURE', dynamicParams!.requestID); // getBasicDetColsStructureList = genericResponseModel?.getBasicDetColsStructureList ?? []; // getBasicDetColsStructureList?.forEach((element) { @@ -266,16 +266,15 @@ class _DynamicInputScreenState extends State { if (e.fORMATTYPE == "X") { // for date format type, date format is changed tempVar = e.eSERVICESDV?.pVALUECOLUMNNAME ?? ""; - if (tempVar.isNotEmpty) { DateTime date = DateFormat('yyyy-MM-dd').parse(tempVar); - tempVar = DateFormat('dd/MMM/yyy').format(date); + tempVar = DateFormat('dd-MMM-yyy').format(date); if (e.aPPLICATIONCOLUMNNAME == null) { effectiveDate = tempVar; } } } else if (e.eSERVICESVS?.isNotEmpty ?? false) { - tempVar = e.getEmployeeAddressList!.vARCHAR2VALUE!; + tempVar = e.getEmployeeAddressList!.vARCHAR2VALUE ?? ""; if (e.aPPLICATIONCOLUMNNAME == null) { countryCode = tempVar; } diff --git a/lib/ui/profile/dynamic_screens/dynamic_input_basic_details_screen.dart b/lib/ui/profile/dynamic_screens/dynamic_input_basic_details_screen.dart index 3127bec..444b83e 100644 --- a/lib/ui/profile/dynamic_screens/dynamic_input_basic_details_screen.dart +++ b/lib/ui/profile/dynamic_screens/dynamic_input_basic_details_screen.dart @@ -80,11 +80,23 @@ class _DynamicInputScreenState extends State { } }); } else { - getBasicDetDffStructureList?.forEach((element) { - element.userBasicDetail = new GetEmployeeBasicDetailsList(); + // getBasicDetDffStructureList?.forEach((element) { + // element.userBasicDetail = new GetEmployeeBasicDetailsList(); + // }); + // getBasicDetColsStructureList?.forEach((element) { + // element.userBasicDetail = GetEmployeeBasicDetailsList(); + // }); + getBasicDetDffStructureList?.forEach((GetBasicDetDffStructureList element) { + element.userBasicDetail = + dynamicParams!.getEmployeeBasicDetailsList!.singleWhere((GetEmployeeBasicDetailsList userDetail) => userDetail.aPPLICATIONCOLUMNNAME == element.aPPLICATIONCOLUMNNAME); }); - getBasicDetColsStructureList?.forEach((element) { - element.userBasicDetail = GetEmployeeBasicDetailsList(); + getBasicDetColsStructureList?.forEach((GetBasicDetColsStructureList element) { + element.userBasicDetail = + dynamicParams!.getEmployeeBasicDetailsList!.singleWhere((GetEmployeeBasicDetailsList userDetail) => userDetail.aPPLICATIONCOLUMNNAME == element.aPPLICATIONCOLUMNNAME); + if (element.objectValuesList != null) { + ObjectValuesList dropDownListValue = element.objectValuesList!.singleWhere((ObjectValuesList dropdown) => dropdown.cODE == element.userBasicDetail!.vARCHAR2VALUE); + element.userBasicDetail!.sEGMENTVALUEDSP = dropDownListValue.mEANING; + } }); } @@ -369,7 +381,7 @@ class _DynamicInputScreenState extends State { //values.add(ValidateEitTransactionModel(dATEVALUE: null, nAME: "PEI_OBJECT_VERSION_NUMBER", nUMBERVALUE: 0, tRANSACTIONNUMBER: 1, vARCHAR2VALUE: null).toJson()); List> valuesCols = getBasicDetColsStructureList!.map((e) { if (e.dATATYPE == 'NUMBER') { - numberValue = e.userBasicDetail!.nUMBERVALUE!; + numberValue = e.userBasicDetail!.nUMBERVALUE ?? 0; } return ValidateEitTransactionModel( dATEVALUE: e.userBasicDetail!.dATEVALUE ?? "", nAME: e.aPPLICATIONCOLUMNNAME, nUMBERVALUE: numberValue, tRANSACTIONNUMBER: 1, vARCHAR2VALUE: e.userBasicDetail!.vARCHAR2VALUE ?? "") diff --git a/lib/ui/profile/dynamic_screens/dynamic_listview_screen.dart b/lib/ui/profile/dynamic_screens/dynamic_listview_screen.dart index ed35527..dce4662 100644 --- a/lib/ui/profile/dynamic_screens/dynamic_listview_screen.dart +++ b/lib/ui/profile/dynamic_screens/dynamic_listview_screen.dart @@ -17,7 +17,8 @@ class DynamicListViewParams { String dynamicId; String uRL; String requestID; - DynamicListViewParams(this.title, this.dynamicId, {this.uRL = 'GET_EIT_DFF_STRUCTURE', this.requestID = ''}); + final String selectedEmp; + DynamicListViewParams(this.title, this.dynamicId, {this.selectedEmp ='', this.uRL = 'GET_EIT_DFF_STRUCTURE', this.requestID = ''}); } class DynamicListViewScreen extends StatefulWidget { @@ -38,9 +39,9 @@ class _DynamicListViewScreenState extends State { } void getTransactions() async { - try { + try { Utils.showLoading(context); - getEITTransactionList = await MyAttendanceApiClient().getEitTransaction(dynamicParams!.dynamicId); + getEITTransactionList = await MyAttendanceApiClient().getEitTransaction(dynamicParams!.dynamicId, dynamicParams!.selectedEmp); Utils.hideLoading(context); setState(() {}); } catch (ex) { diff --git a/lib/ui/profile/family_members.dart b/lib/ui/profile/family_members.dart index fd7bd13..25f62c0 100644 --- a/lib/ui/profile/family_members.dart +++ b/lib/ui/profile/family_members.dart @@ -128,7 +128,8 @@ class _FamilyMembersState extends State { ), DefaultButton(LocaleKeys.addNewFamilyMember.tr(), menuEntries.updateButton == 'Y' - ? () async { + ? + () async { Navigator.pushNamed(context, AppRoutes.addUpdateFamilyMember, arguments: {"relationID": relationId, "flag": 1, "actionType": "ADD"}); // ProfileScreen(); } : null).insideContainer, diff --git a/lib/ui/profile/profile_screen.dart b/lib/ui/profile/profile_screen.dart index 0bf007e..0a78873 100644 --- a/lib/ui/profile/profile_screen.dart +++ b/lib/ui/profile/profile_screen.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'dart:ui'; import 'package:easy_localization/easy_localization.dart'; @@ -16,6 +17,7 @@ import 'package:mohem_flutter_app/models/get_employee_basic_details.model.dart'; import 'package:mohem_flutter_app/models/member_information_list_model.dart'; import 'package:mohem_flutter_app/ui/profile/widgets/profile_panel.dart'; import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; +import 'package:mohem_flutter_app/widgets/image_picker.dart'; class ProfileScreen extends StatefulWidget { const ProfileScreen({Key? key}) : super(key: key); @@ -28,6 +30,7 @@ class _ProfileScreenState extends State { late MemberInformationListModel memberInformationList; final ImagePicker _picker = ImagePicker(); List getEmployeeBasicDetailsList = []; + List images = []; @override void initState() { @@ -46,24 +49,25 @@ class _ProfileScreenState extends State { children: [ memberInformationList!.eMPLOYEEIMAGE != null ? Container( - height: 300, - margin: const EdgeInsets.only(top: 50), - decoration: BoxDecoration( - image: DecorationImage( - image: MemoryImage( - Utils.dataFromBase64String(memberInformationList.eMPLOYEEIMAGE!), + height: 300, + margin: const EdgeInsets.only(top: 50), + decoration: BoxDecoration( + image: DecorationImage( + image: MemoryImage( + Utils.dataFromBase64String(memberInformationList.eMPLOYEEIMAGE!), + ), + fit: BoxFit.cover), ), - fit: BoxFit.cover), - ), - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), - child: Container( - color: Colors.white.withOpacity(0.0), - ), - ), - ): Container( - decoration: BoxDecoration(color: Colors.white.withOpacity(0.0)), - ), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Container( + color: Colors.white.withOpacity(0.0), + ), + ), + ) + : Container( + decoration: BoxDecoration(color: Colors.white.withOpacity(0.0)), + ), SingleChildScrollView( scrollDirection: Axis.vertical, child: Column( @@ -83,7 +87,14 @@ class _ProfileScreenState extends State { ), InkWell( onTap: () { - startImageSheet(); + // startImageSheet(); + ImageOptions.showImageOptionsNew(context, false, (String image, File file) { + setState(() { + // images.add(image); + Navigator.of(context).pop(); + openGallery(image); + }); + }); }, child: Container( padding: const EdgeInsets.only(left: 17, right: 17, top: 8, bottom: 8), @@ -112,7 +123,8 @@ class _ProfileScreenState extends State { } void startImageSheet() { - showMyBottomSheet(context, callBackFunc: (){}, + showMyBottomSheet(context, + callBackFunc: () {}, child: Column( children: [ Container( @@ -123,14 +135,14 @@ class _ProfileScreenState extends State { )), BottomSheetItem( onTap: () { - openGallery(false); + // openGallery(false); }, title: 'Open Gallery', icon: Icons.browse_gallery_outlined, ), BottomSheetItem( onTap: () { - openGallery(true); + // openGallery(true); }, title: 'Open Camera', icon: Icons.camera, @@ -139,19 +151,17 @@ class _ProfileScreenState extends State { )); } - void openGallery(bool isCamera) async { - XFile? image = await _picker.pickImage(source: isCamera ? ImageSource.camera : ImageSource.gallery); + void openGallery(String image) async { + // XFile? image = await _picker.pickImage(source: isCamera ? ImageSource.camera : ImageSource.gallery); - if (image != null) { - String img = base64.encode(await image!.readAsBytes()); - Utils.showLoading(context); - dynamic empImageUpdteResp = await ProfileApiClient().updateEmpImage(img); - Utils.hideLoading(context); - if (empImageUpdteResp['P_RETURN_STATUS'] == 'S') { - setState(() { - memberInformationList.eMPLOYEEIMAGE = img; - }); - } + // String img = base64.encode(await image!.readAsBytes()); + Utils.showLoading(context); + dynamic empImageUpdteResp = await ProfileApiClient().updateEmpImage(image); + Utils.hideLoading(context); + if (empImageUpdteResp['P_RETURN_STATUS'] == 'S') { + setState(() { + memberInformationList.eMPLOYEEIMAGE = image; + }); } } } diff --git a/lib/ui/profile/widgets/profile_panel.dart b/lib/ui/profile/widgets/profile_panel.dart index 5018171..440fbb3 100644 --- a/lib/ui/profile/widgets/profile_panel.dart +++ b/lib/ui/profile/widgets/profile_panel.dart @@ -44,12 +44,16 @@ class ProfilePanel extends StatelessWidget { Widget profileImage() => memberInformationList.eMPLOYEEIMAGE == null ? SvgPicture.asset( - "assets/images/user.svg", - height: 68, - width: 68,) - : CircleAvatar( - radius: 68, - backgroundImage: MemoryImage(Utils.dataFromBase64String(memberInformationList.eMPLOYEEIMAGE!)), - backgroundColor: Colors.black, - ); + "assets/images/user.svg", + height: 68, + width: 68, + ) + : ClipOval( + child: Image.memory( + Utils.dataFromBase64String(memberInformationList.eMPLOYEEIMAGE!), + width: 75, + height: 75, + fit: BoxFit.fill, + ), + ); } diff --git a/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart b/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart index b2791ec..f0f2c62 100644 --- a/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart +++ b/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart @@ -25,8 +25,9 @@ class AddItemDetailsFragment extends StatefulWidget { final Function changePageViewIndex; final GetSaleCategoriesList selectedSaleCategory; static late ItemReviewModel itemReviewModel; + static late bool isUpdate; - const AddItemDetailsFragment({Key? key, required this.changePageViewIndex, required this.selectedSaleCategory}) : super(key: key); + AddItemDetailsFragment({Key? key, required this.changePageViewIndex, required this.selectedSaleCategory}) : super(key: key); @override State createState() => _AddItemDetailsFragmentState(); @@ -246,6 +247,7 @@ class _AddItemDetailsFragmentState extends State { String details = await Utils.getStringFromPrefs(SharedPrefsConsts.editItemForSale); if(details.isNotEmpty) { var body = json.decode(details); + AddItemDetailsFragment.isUpdate = true; GetRegionsList selectedRegionAd = GetRegionsList(); @@ -268,6 +270,8 @@ class _AddItemDetailsFragmentState extends State { AddItemDetailsFragment.itemReviewModel = itemReviewModel; SelectCategoryFragment.selectedSaleCategory = selectedSaleCategoryAd; + } else { + AddItemDetailsFragment.isUpdate = false; } } diff --git a/lib/ui/screens/items_for_sale/fragments/item_review_fragment.dart b/lib/ui/screens/items_for_sale/fragments/item_review_fragment.dart index 0b02658..190460e 100644 --- a/lib/ui/screens/items_for_sale/fragments/item_review_fragment.dart +++ b/lib/ui/screens/items_for_sale/fragments/item_review_fragment.dart @@ -26,11 +26,13 @@ class ItemReviewFragment extends StatefulWidget { class _ItemReviewFragmentState extends State { ItemReviewModel? itemReviewModel; + late bool isUpdate; @override void initState() { itemReviewModel = AddItemDetailsFragment.itemReviewModel; itemReviewModel!.selectedSaleCategory = SelectCategoryFragment.selectedSaleCategory; + isUpdate = AddItemDetailsFragment.isUpdate; super.initState(); } diff --git a/lib/ui/screens/my_requests/my_requests.dart b/lib/ui/screens/my_requests/my_requests.dart index f9d8d39..dd6147f 100644 --- a/lib/ui/screens/my_requests/my_requests.dart +++ b/lib/ui/screens/my_requests/my_requests.dart @@ -76,9 +76,7 @@ class _MyRequestsState extends State { }), ), 12.height, - Expanded( - // todo list don't have data, need to confirm later , because have issues, need fixes - + getCCPTransactionsList.isNotEmpty ? Expanded( child: ListView.separated( physics: const BouncingScrollPhysics(), shrinkWrap: true, @@ -141,7 +139,7 @@ class _MyRequestsState extends State { }, separatorBuilder: (BuildContext context, int index) => 12.height, itemCount: getCCPTransactionsList.length), - ), + ) : Container(), ], ).expanded, 1.divider, diff --git a/lib/ui/screens/my_requests/new_request.dart b/lib/ui/screens/my_requests/new_request.dart index d04382d..7a73497 100644 --- a/lib/ui/screens/my_requests/new_request.dart +++ b/lib/ui/screens/my_requests/new_request.dart @@ -89,15 +89,13 @@ class _NewRequestState extends State { 1.divider, DefaultButton(LocaleKeys.submit.tr(), () { // todo need to add submit method - openNewRequest(); - }) - .insideContainer, + openNewRequest(); + }).insideContainer, ], ), ); } - void openNewRequest() async { try { List> values = getCCPDFFStructureModelList!.map((e) { @@ -123,7 +121,10 @@ class _NewRequestState extends State { submitCcpTransactionList = await MyRequestsApiClient().getSubmitNewRequest(values); getCCPTransactionsList = await MyRequestsApiClient().getCcpTransactions(values); Utils.hideLoading(context); - Navigator.pushNamed(context, AppRoutes.myRequests,); + Navigator.pushNamed( + context, + AppRoutes.myRequests, + ); setState(() {}); } catch (ex) { Utils.hideLoading(context); diff --git a/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart b/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart index 9a308ba..325f4f3 100644 --- a/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart +++ b/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart @@ -176,8 +176,8 @@ class _OffersAndDiscountsDetailsState extends State { // // launchUrl(Uri.parse(url!)); // // } // ), - getOffersList.description!.toText12(maxLine: 2, color: const Color(0xff535353)), - 16.height, + // getOffersList.description!.toText12(maxLine: 2, color: const Color(0xff535353)), + // 16.height, getOffersList.discount!.toText14(isBold: true, maxlines: 1), 8.height, Row( diff --git a/lib/ui/screens/offers_and_discounts/offers_and_discounts_home.dart b/lib/ui/screens/offers_and_discounts/offers_and_discounts_home.dart index 45247dc..343eed8 100644 --- a/lib/ui/screens/offers_and_discounts/offers_and_discounts_home.dart +++ b/lib/ui/screens/offers_and_discounts/offers_and_discounts_home.dart @@ -173,7 +173,7 @@ class _OffersAndDiscountsHomeState extends State { tag: "ItemImage" + getOffersList.rowID!, transitionOnUserGestures: true, child: AspectRatio( - aspectRatio: 148 / 127, + aspectRatio: 118 / 127, child: ClipRRect( borderRadius: BorderRadius.circular(6), child: Image.network( @@ -192,10 +192,10 @@ class _OffersAndDiscountsHomeState extends State { // // launchUrl(Uri.parse(url!)); // // } // ), - getOffersList.description!.toText12(maxLine: 2, color: const Color(0xff535353)), + // getOffersList.description!.toText12(maxLine: 2, color: const Color(0xff535353)), // 8.height, getOffersList.discount!.toText14(isBold: true, maxlines: 1), - 10.height, + 20.height, Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [checkDate(getOffersList.endDate!), SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4)], @@ -211,7 +211,7 @@ class _OffersAndDiscountsHomeState extends State { if (enteredKeyword.isEmpty) { results = getOffersList; } else { - if(AppState().isArabic(context)) { + if (AppState().isArabic(context)) { results = getOffersList.where((offer) => offer.titleAR!.toLowerCase().contains(enteredKeyword.toLowerCase())).toList(); } else { results = getOffersList.where((offer) => offer.title!.toLowerCase().contains(enteredKeyword.toLowerCase())).toList(); diff --git a/lib/ui/work_list/item_history_screen.dart b/lib/ui/work_list/item_history_screen.dart index 6cf80ca..562b59f 100644 --- a/lib/ui/work_list/item_history_screen.dart +++ b/lib/ui/work_list/item_history_screen.dart @@ -102,21 +102,39 @@ class _ItemHistoryScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - ItemDetailView(LocaleKeys.requestNumber.tr(), moItemHistoryList[index].rEQUESTNUMBER ?? ""), - ItemDetailView(LocaleKeys.uom.tr(), moItemHistoryList[index].uNITOFMEASURE ?? ""), - ItemDetailView(LocaleKeys.quantity.tr(), moItemHistoryList[index].qUANTITY?.toString() ?? ""), - ItemDetailView(LocaleKeys.dateRequired.tr(), moItemHistoryList[index].dATEREQUIRED ?? ""), - ItemDetailView(LocaleKeys.lineStatus.tr(), moItemHistoryList[index].lINESTATUSDIS ?? ""), - ItemDetailView(LocaleKeys.statusDate.tr(), moItemHistoryList[index].sTATUSDATE ?? ""), - ItemDetailView(LocaleKeys.transactionType.tr(), moItemHistoryList[index].tRANSACTIONTYPENAME ?? ""), - ItemDetailView(LocaleKeys.organization.tr(), moItemHistoryList[index].oRGANIZATIONNAME ?? ""), - ItemDetailView(LocaleKeys.operatingCode.tr(), moItemHistoryList[index].oRGANIZATIONCODE ?? ""), - ItemDetailView(LocaleKeys.operatingUnit.tr(), moItemHistoryList[index].oPERATINGUNITNAME ?? ""), - ItemDetailView(LocaleKeys.fromSubInventory.tr(), moItemHistoryList[index].fROMSUBINVENTORYCODE ?? ""), - ItemDetailView(LocaleKeys.fromLocator.tr(), moItemHistoryList[index].fROMLOCATOR ?? ""), - ItemDetailView(LocaleKeys.toSubInventory.tr(), moItemHistoryList[index].tOSUBINVENTORYCODE ?? ""), - ItemDetailView(LocaleKeys.toLocator.tr(), moItemHistoryList[index].tOLOCATOR ?? ""), - ItemDetailView(LocaleKeys.shipToLocation.tr(), moItemHistoryList[index].sHIPTOLOCATION ?? ""), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.requestNumber.tr(), moItemHistoryList[index].rEQUESTNUMBER ?? ""), + ItemDetailViewCol(LocaleKeys.uom.tr(), moItemHistoryList[index].uNITOFMEASURE ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.quantity.tr(), moItemHistoryList[index].qUANTITY?.toString() ?? ""), + ItemDetailViewCol(LocaleKeys.dateRequired.tr(), moItemHistoryList[index].dATEREQUIRED ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.lineStatus.tr(), moItemHistoryList[index].lINESTATUSDIS ?? ""), + ItemDetailViewCol(LocaleKeys.statusDate.tr(), moItemHistoryList[index].sTATUSDATE ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.transactionType.tr(), moItemHistoryList[index].tRANSACTIONTYPENAME ?? ""), + ItemDetailViewCol(LocaleKeys.organization.tr(), moItemHistoryList[index].oRGANIZATIONNAME ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.operatingCode.tr(), moItemHistoryList[index].oRGANIZATIONCODE ?? ""), + ItemDetailViewCol(LocaleKeys.operatingUnit.tr(), moItemHistoryList[index].oPERATINGUNITNAME ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.fromSubInventory.tr(), moItemHistoryList[index].fROMSUBINVENTORYCODE ?? ""), + ItemDetailViewCol(LocaleKeys.fromLocator.tr(), moItemHistoryList[index].fROMLOCATOR ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.toSubInventory.tr(), moItemHistoryList[index].tOSUBINVENTORYCODE ?? ""), + ItemDetailViewCol(LocaleKeys.toLocator.tr(), moItemHistoryList[index].tOLOCATOR ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.shipToLocation.tr(), moItemHistoryList[index].sHIPTOLOCATION ?? ""), + Container(), + isItLast: true, + ), ], ).objectContainerView(), separatorBuilder: (cxt, index) => 12.height, @@ -131,21 +149,39 @@ class _ItemHistoryScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - ItemDetailView(LocaleKeys.operatingUnit.tr(), poItemHistoryList[index].oUNAME ?? ""), - ItemDetailView(LocaleKeys.poNumber.tr(), poItemHistoryList[index].pONUMBER ?? ""), - ItemDetailView(LocaleKeys.revision.tr(), poItemHistoryList[index].rEVISIONNUM?.toString() ?? ""), - ItemDetailView(LocaleKeys.creationDate.tr(), poItemHistoryList[index].cREATIONDATE ?? ""), - ItemDetailView(LocaleKeys.supplier.tr(), poItemHistoryList[index].sUPPLIER ?? ""), - ItemDetailView(LocaleKeys.buyer.tr(), poItemHistoryList[index].bUYER ?? ""), - ItemDetailView(LocaleKeys.uom.tr(), poItemHistoryList[index].uOM ?? ""), - ItemDetailView(LocaleKeys.quantityOrdered.tr(), poItemHistoryList[index].qUANTITYORDERED?.toString() ?? ""), - ItemDetailView(LocaleKeys.quantityReceived.tr(), poItemHistoryList[index].qUANTITYRECEIVED?.toString() ?? ""), - ItemDetailView(LocaleKeys.bonusQuantity.tr(), poItemHistoryList[index].bONUSQUANTITY?.toString() ?? ""), - ItemDetailView(LocaleKeys.purchasePrice.tr(), poItemHistoryList[index].pURCHASEPRICE?.toString() ?? ""), - ItemDetailView(LocaleKeys.discountPer.tr(), poItemHistoryList[index].dISCOUNTPERCENTAGE?.toString() ?? ""), - ItemDetailView(LocaleKeys.balanceQuantity.tr(), poItemHistoryList[index].bALANCEQUANTITY?.toString() ?? ""), - ItemDetailView(LocaleKeys.netPrice.tr(), poItemHistoryList[index].nETPRICE?.toString() ?? ""), - ItemDetailView(LocaleKeys.closureStatus.tr(), poItemHistoryList[index].cLOSEDCODE ?? ""), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.operatingUnit.tr(), poItemHistoryList[index].oUNAME ?? ""), + ItemDetailViewCol(LocaleKeys.poNumber.tr(), poItemHistoryList[index].pONUMBER ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.revision.tr(), poItemHistoryList[index].rEVISIONNUM?.toString() ?? ""), + ItemDetailViewCol(LocaleKeys.creationDate.tr(), poItemHistoryList[index].cREATIONDATE ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.supplier.tr(), poItemHistoryList[index].sUPPLIER ?? ""), + ItemDetailViewCol(LocaleKeys.buyer.tr(), poItemHistoryList[index].bUYER ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.uom.tr(), poItemHistoryList[index].uOM ?? ""), + ItemDetailViewCol(LocaleKeys.quantityOrdered.tr(), poItemHistoryList[index].qUANTITYORDERED?.toString() ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.quantityReceived.tr(), poItemHistoryList[index].qUANTITYRECEIVED?.toString() ?? ""), + ItemDetailViewCol(LocaleKeys.bonusQuantity.tr(), poItemHistoryList[index].bONUSQUANTITY?.toString() ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.purchasePrice.tr(), poItemHistoryList[index].pURCHASEPRICE?.toString() ?? ""), + ItemDetailViewCol(LocaleKeys.discountPer.tr(), poItemHistoryList[index].dISCOUNTPERCENTAGE?.toString() ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.balanceQuantity.tr(), poItemHistoryList[index].bALANCEQUANTITY?.toString() ?? ""), + ItemDetailViewCol(LocaleKeys.netPrice.tr(), poItemHistoryList[index].nETPRICE?.toString() ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.closureStatus.tr(), poItemHistoryList[index].cLOSEDCODE ?? ""), + Container(), + isItLast: true, + ) ], ).objectContainerView(), separatorBuilder: (cxt, index) => 12.height, @@ -160,20 +196,35 @@ class _ItemHistoryScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - ItemDetailView(LocaleKeys.quotationNumber.tr(), quotationAnalysisList[index].qUOTNUM ?? ""), - ItemDetailView(LocaleKeys.vendorName.tr(), quotationAnalysisList[index].vENDORNAME ?? ""), - ItemDetailView(LocaleKeys.itemCode.tr(), quotationAnalysisList[index].iTEMCODE ?? ""), - ItemDetailView(LocaleKeys.description.tr(), quotationAnalysisList[index].iTEMDESC ?? ""), - ItemDetailView(LocaleKeys.quotationQty.tr(), quotationAnalysisList[index].qUOTQTY?.toString() ?? ""), - ItemDetailView(LocaleKeys.quotationUOM.tr(), quotationAnalysisList[index].qUOTUOM ?? ""), - ItemDetailView(LocaleKeys.quotationNetPrice.tr(), quotationAnalysisList[index].qUOTUNITPRICE?.toString() ?? ""), - ItemDetailView(LocaleKeys.quotationLineTotal.tr(), quotationAnalysisList[index].qUOTLINETOTAL?.toString() ?? ""), - ItemDetailView(LocaleKeys.quotationBonusQuantity.tr(), quotationAnalysisList[index].qUOTBONUSQTY ?? ""), - ItemDetailView(LocaleKeys.quotationDeliveryDate.tr(), quotationAnalysisList[index].qUOTDELIVERYDATE ?? ""), - ItemDetailView(LocaleKeys.quotationMFGPartNumber.tr(), quotationAnalysisList[index].qUOTMFGPARTNUM ?? ""), - ItemDetailView(LocaleKeys.rfqNumber.tr(), quotationAnalysisList[index].rFQNUM ?? ""), - ItemDetailView(LocaleKeys.rfqQty.tr(), quotationAnalysisList[index].rFQQTY?.toString() ?? ""), - ItemDetailView(LocaleKeys.rfqUOM.tr(), quotationAnalysisList[index].rFQUOM ?? ""), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.quotationNumber.tr(), quotationAnalysisList[index].qUOTNUM ?? ""), + ItemDetailViewCol(LocaleKeys.vendorName.tr(), quotationAnalysisList[index].vENDORNAME ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.itemCode.tr(), quotationAnalysisList[index].iTEMCODE ?? ""), + ItemDetailViewCol(LocaleKeys.description.tr(), quotationAnalysisList[index].iTEMDESC ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.quotationQty.tr(), quotationAnalysisList[index].qUOTQTY?.toString() ?? ""), + ItemDetailViewCol(LocaleKeys.quotationUOM.tr(), quotationAnalysisList[index].qUOTUOM ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.quotationNetPrice.tr(), quotationAnalysisList[index].qUOTUNITPRICE?.toString() ?? ""), + ItemDetailViewCol(LocaleKeys.quotationLineTotal.tr(), quotationAnalysisList[index].qUOTLINETOTAL?.toString() ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.quotationBonusQuantity.tr(), quotationAnalysisList[index].qUOTBONUSQTY ?? ""), + ItemDetailViewCol(LocaleKeys.quotationDeliveryDate.tr(), quotationAnalysisList[index].qUOTDELIVERYDATE ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.quotationMFGPartNumber.tr(), quotationAnalysisList[index].qUOTMFGPARTNUM ?? ""), + ItemDetailViewCol(LocaleKeys.rfqNumber.tr(), quotationAnalysisList[index].rFQNUM ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.rfqQty.tr(), quotationAnalysisList[index].rFQQTY?.toString() ?? ""), + ItemDetailViewCol(LocaleKeys.rfqUOM.tr(), quotationAnalysisList[index].rFQUOM ?? ""), + isItLast: true, + ) ], ).objectContainerView(title: "${quotationAnalysisList[index].iTEMCODE}-${quotationAnalysisList[index].iTEMDESC}"), separatorBuilder: (cxt, index) => 12.height, diff --git a/lib/ui/work_list/itg_detail_screen.dart b/lib/ui/work_list/itg_detail_screen.dart index 0f45648..cf33648 100644 --- a/lib/ui/work_list/itg_detail_screen.dart +++ b/lib/ui/work_list/itg_detail_screen.dart @@ -14,6 +14,7 @@ import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/models/itg_forms_models/allowed_actions_model.dart'; import 'package:mohem_flutter_app/models/itg_forms_models/itg_request_model.dart'; import 'package:mohem_flutter_app/models/itg_forms_models/request_detail_model.dart'; +import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/ui/work_list/itg_fragments/approval_level_fragment.dart'; import 'package:mohem_flutter_app/ui/work_list/itg_fragments/request_detail_fragment.dart'; import 'package:mohem_flutter_app/ui/work_list/sheets/delegate_sheet.dart'; @@ -21,6 +22,7 @@ import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/dialogs/itg_comments_dialog.dart'; +import 'package:provider/provider.dart'; class ItgDetailScreen extends StatefulWidget { ItgDetailScreen({Key? key}) : super(key: key); @@ -42,8 +44,11 @@ class _ItgDetailScreenState extends State { List allowedActionList = []; + late DashboardProviderModel providerData; + @override void initState() { + providerData = Provider.of(context, listen: false); super.initState(); } @@ -66,7 +71,9 @@ class _ItgDetailScreenState extends State { isRejectAvailable = itgRequest!.allowedActions!.any((element) => element.action == "Reject"); } Utils.hideLoading(context); - setState(() {}); + setState(() { + controller.jumpToPage(0); + }); } catch (ex) { Utils.hideLoading(context); Utils.handleException(ex, context, null); @@ -76,6 +83,7 @@ class _ItgDetailScreenState extends State { void getDataFromState() { if (requestDetails == null) { requestDetails = AppState().requestAllList![AppState().itgWorkListIndex!]; // ModalRoute.of(context)!.settings.arguments as WorkListResponseModel; + providerData.itgFormsModel!.totalCount = providerData.itgFormsModel!.totalCount! - 1; getItgData(); } } @@ -125,7 +133,10 @@ class _ItgDetailScreenState extends State { }, children: [ RequestDetailFragment(fields: itgRequest?.fieldGoups?[1].fields ?? []), - ApprovalLevelfragment(wFHistory: itgRequest?.wFHistory ?? [], voidCallback: reloadITG,), + ApprovalLevelfragment( + wFHistory: itgRequest?.wFHistory ?? [], + voidCallback: reloadITG, + ), RequestDetailFragment(fields: itgRequest?.fieldGoups?[0].fields ?? []), ], ).expanded, @@ -254,31 +265,118 @@ class _ItgDetailScreenState extends State { if (allowedActionList[i].action! == "Reject" || allowedActionList[i].action! == "Approve" || allowedActionList[i].action! == "CLOSE") { continue; } - fabs.add(myFab(allowedActionList[i].action!, "", isIconAsset: false).paddingOnly(bottom: 12).onPress(() => handleFabAction(allowedActionList[i]))); + fabs.add(myFab(getFabActionTitle(allowedActionList[i].action!), getFabActionImages(allowedActionList[i].action!)).paddingOnly(bottom: 12).onPress(() => handleFabAction(allowedActionList[i]))); } return fabs; } + String getFabActionImages(String actionTitle) { + String returnActionImage = ""; + switch (actionTitle) { + case "Delegate": + returnActionImage = "assets/images/delegate.svg"; + break; + case "RequestInformation": + returnActionImage = "assets/images/request_info.svg"; + break; + case "ReportGenerated": + returnActionImage = "assets/images/worklist/report_generated.svg"; + break; + case "DataCorrected": + returnActionImage = "assets/images/worklist/report_generated.svg"; + break; + case "Doable": + returnActionImage = "assets/images/worklist/doable.svg"; + break; + case "NotDoable": + returnActionImage = "assets/images/worklist/not_doable.svg"; + break; + case "Answer": + returnActionImage = "assets/images/worklist/answer_hr.svg"; + break; + default: + returnActionImage = "assets/images/request_info.svg"; + break; + } + + return returnActionImage; + } + + String getFabActionTitle(String title) { + String returnTitle = ""; + switch (title) { + case "Delegate": + returnTitle = "Delegate"; + break; + case "RequestInformation": + returnTitle = "Request Information"; + break; + case "ReportGenerated": + returnTitle = "Report Generated"; + break; + case "DataCorrected": + returnTitle = "Data Corrected"; + break; + case "Doable": + returnTitle = "Doable"; + break; + case "NotDoable": + returnTitle = "Not Doable"; + break; + default: + returnTitle = title; + break; + } + return returnTitle; + } + void handleFabAction(AllowedActions action) { print(action.toJson()); switch (action.action) { case "Delegate": - showMyBottomSheet(context, callBackFunc: reloadITG, - child: DelegateSheet(title: LocaleKeys.delegate.tr(), apiMode: action.action!, notificationID: null, actionHistoryList: null, wFHistory: itgRequest?.wFHistory ?? [])); + showMyBottomSheet(context, + callBackFunc: reloadITG, + child: DelegateSheet( + title: LocaleKeys.delegate.tr(), + apiMode: action.action!, + notificationID: null, + actionHistoryList: null, + wFHistory: itgRequest?.wFHistory ?? [], + callBackFunc: reloadITG, + )); break; case "RequestInformation": - showMyBottomSheet(context, callBackFunc: reloadITG, - child: DelegateSheet(title: LocaleKeys.request_info.tr(), apiMode: action.action!, notificationID: null, actionHistoryList: null, wFHistory: itgRequest?.wFHistory ?? [])); - + showMyBottomSheet(context, + callBackFunc: reloadITG, + child: DelegateSheet( + title: LocaleKeys.request_info.tr(), + apiMode: action.action!, + notificationID: null, + actionHistoryList: null, + wFHistory: itgRequest?.wFHistory ?? [], + callBackFunc: reloadITG, + )); break; - case "RFC": - // do something else + case "Doable": + performAction("APPROVED"); break; - case "UPDATE_ACTION": - // do something else + case "NotDoable": + performAction("REJECTED"); + break; + case "Answer": + performAction("Answer"); + break; + case "ReportGenerated": + performDataCorrectionORReportGeneratedAction(requestDetails!.requestType!, requestDetails!.iD!, requestDetails!.itemID!, AppState().memberInformationList?.eMPLOYEENUMBER ?? ""); + break; + case "DataCorrected": + performDataCorrectionORReportGeneratedAction(requestDetails!.requestType!, requestDetails!.iD!, requestDetails!.itemID!, AppState().memberInformationList?.eMPLOYEENUMBER ?? ""); break; } + setState(() { + showFabOptions = false; + }); } Widget myTab(String title, int index) { @@ -351,6 +449,8 @@ class _ItgDetailScreenState extends State { onTap: (note) { if (actionMode == "APPROVED") { performApproveAction(requestDetails!.requestType!, requestDetails!.iD!, requestDetails!.itemID!, AppState().memberInformationList?.eMPLOYEENUMBER ?? "", note); + } else if (actionMode == "Answer") { + performAnswerAction(requestDetails!.requestType!, requestDetails!.iD!, requestDetails!.itemID!, AppState().memberInformationList?.eMPLOYEENUMBER ?? "", note); } else { performRejectAction(requestDetails!.requestType!, requestDetails!.iD!, requestDetails!.itemID!, AppState().memberInformationList?.eMPLOYEENUMBER ?? "", note); } @@ -359,6 +459,30 @@ class _ItgDetailScreenState extends State { ); } + void performAnswerAction(String requestType, int taskId, int itemId, String employeeNumber, String comments) async { + try { + Utils.showLoading(context); + ITGRequest? itgRequest = await WorkListApiClient().answerITGRequest(requestType, taskId, itemId, employeeNumber, "", comments); + Utils.hideLoading(context); + Utils.showToast(LocaleKeys.yourChangeHasBeenSavedSuccessfully.tr()); + // Navigator.pop(context, "delegate_reload"); + AppState().requestAllList!.removeAt(AppState().itgWorkListIndex!); + if (AppState().requestAllList!.isEmpty) { + Navigator.pop(context, "delegate_reload"); + } else { + if (AppState().requestAllList!.length <= AppState().itgWorkListIndex!) { + Navigator.pop(context, "delegate_reload"); + } else { + requestDetails = null; + getDataFromState(); + } + } + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + void performRejectAction(String requestType, int taskId, int itemId, String employeeNumber, String comments) async { try { Utils.showLoading(context); @@ -407,6 +531,29 @@ class _ItgDetailScreenState extends State { } } + void performDataCorrectionORReportGeneratedAction(String requestType, int taskId, int itemId, String employeeNumber) async { + try { + Utils.showLoading(context); + ITGRequest? itgRequest = await WorkListApiClient().grantITGRequest(requestType, taskId, itemId, employeeNumber, "", ""); + Utils.hideLoading(context); + Utils.showToast(LocaleKeys.yourChangeHasBeenSavedSuccessfully.tr()); + AppState().requestAllList!.removeAt(AppState().itgWorkListIndex!); + if (AppState().requestAllList!.isEmpty) { + Navigator.pop(context, "delegate_reload"); + } else { + if (AppState().requestAllList!.length <= AppState().itgWorkListIndex!) { + Navigator.pop(context, "delegate_reload"); + } else { + requestDetails = null; + getDataFromState(); + } + } + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + void reloadITG() { AppState().requestAllList!.removeAt(AppState().itgWorkListIndex!); if (AppState().requestAllList!.isEmpty) { @@ -420,5 +567,4 @@ class _ItgDetailScreenState extends State { } } } - } diff --git a/lib/ui/work_list/itg_fragments/approval_level_fragment.dart b/lib/ui/work_list/itg_fragments/approval_level_fragment.dart index eb478af..52915ab 100644 --- a/lib/ui/work_list/itg_fragments/approval_level_fragment.dart +++ b/lib/ui/work_list/itg_fragments/approval_level_fragment.dart @@ -138,7 +138,8 @@ class ApprovalLevelfragment extends StatelessWidget { } else if (code.toLowerCase() == "approved" || code.toLowerCase() == "auto-approve" || code.toLowerCase() == "auto-approved" || - code.toLowerCase() == "doable") { + code.toLowerCase() == "doable" || + code.toLowerCase() == "answer") { return MyColors.greenColor; } else if (code.toLowerCase() == "requested information" || code.toLowerCase() == "assign" || code.toLowerCase() == "reassign") { return MyColors.orange; diff --git a/lib/ui/work_list/sheets/delegate_sheet.dart b/lib/ui/work_list/sheets/delegate_sheet.dart index 28a90e6..2a5f453 100644 --- a/lib/ui/work_list/sheets/delegate_sheet.dart +++ b/lib/ui/work_list/sheets/delegate_sheet.dart @@ -28,8 +28,9 @@ class DelegateSheet extends StatefulWidget { String title, apiMode; List? actionHistoryList; List? wFHistory; + VoidCallback callBackFunc; - DelegateSheet({required this.title, required this.apiMode, this.notificationID, this.actionHistoryList, this.wFHistory}); + DelegateSheet({required this.title, required this.apiMode, this.notificationID, this.actionHistoryList, this.wFHistory, required this.callBackFunc}); @override State createState() => _DelegateSheetState(); @@ -46,6 +47,8 @@ class _DelegateSheetState extends State { List? favUsersList; List? replacementList; + bool isImageLoaded = false; + @override void initState() { super.initState(); @@ -90,6 +93,17 @@ class _DelegateSheetState extends State { favUsersList = await WorkListApiClient().getFavoriteReplacementWithImage(selectedFavLetter ?? ""); if (isNeedLoading) Utils.hideLoading(context); setState(() {}); + fetchFavUsersListNew(isNeedLoading: false); + return null; + } + + Future fetchFavUsersListNew({bool isNeedLoading = true}) async { + if (isNeedLoading) Utils.showLoading(context); + favUsersList = await WorkListApiClient().getFavoriteReplacementWithImageNew(selectedFavLetter ?? ""); + if (isNeedLoading) Utils.hideLoading(context); + setState(() { + isImageLoaded = true; + }); return null; } @@ -194,7 +208,7 @@ class _DelegateSheetState extends State { ).onPress(() { showMyBottomSheet( context, - callBackFunc: (){}, + callBackFunc: () {}, child: SearchOptionsSheet( selectedType, onSelection: (String value) { @@ -392,13 +406,15 @@ class _DelegateSheetState extends State { Widget showItem(GetActionHistoryList actionHistory) { return InkWell( onTap: () { + Navigator.pop(context); showMyBottomSheet(context, - callBackFunc: (){}, + callBackFunc: widget.callBackFunc, child: SelectedItemSheet( "Comment", apiMode: widget.apiMode, actionHistoryList: actionHistory, notificationID: widget.notificationID, + isITGRequest: widget.wFHistory != null, )); }, child: Row( @@ -438,8 +454,9 @@ class _DelegateSheetState extends State { Widget showItgItem(WFHistory wfHistory) { return InkWell( onTap: () { + Navigator.pop(context); showMyBottomSheet(context, - callBackFunc: (){}, + callBackFunc: widget.callBackFunc, child: SelectedItgItemSheet( "Comment", apiMode: widget.apiMode, @@ -463,23 +480,43 @@ class _DelegateSheetState extends State { Widget showFavUserItem(GetFavoriteReplacements actionHistory) { return InkWell( onTap: () { + Navigator.pop(context); showMyBottomSheet(context, - callBackFunc: (){}, + callBackFunc: widget.callBackFunc, child: SelectedItemSheet( "Comment", apiMode: widget.apiMode, favoriteReplacements: actionHistory, notificationID: widget.notificationID, + isITGRequest: widget.wFHistory != null, )); }, child: Row( children: [ - CircularAvatar( - url: actionHistory.employeeImage, - height: 30, - width: 30, - isImageBase64: (actionHistory.employeeImage != null || actionHistory.employeeImage!.isNotEmpty) ? true : false, - ), + actionHistory.employeeImage != "" + ? CircularAvatar( + url: actionHistory.employeeImage, + height: 40, + width: 40, + isImageBase64: (actionHistory.employeeImage != null || actionHistory.employeeImage!.isNotEmpty) ? true : false, + ) + : isImageLoaded + ? CircularAvatar( + height: 40, + width: 40, + isImageBase64: false, + ) + : ClipRRect( + borderRadius: const BorderRadius.all( + Radius.circular(50), + ), + child: Image.network( + "https://play-lh.googleusercontent.com/NPo88ojmhah4HDiposucJmfQIop4z4xc8kqJK9ITO9o-yCab2zxIp7PPB_XPj2iUojo", + fit: BoxFit.cover, + height: 40, + width: 40, + ).toShimmer(), + ), 16.width, Expanded( child: (actionHistory.employeeDisplayName ?? "").toText12(), @@ -509,13 +546,15 @@ class _DelegateSheetState extends State { Widget showInputUserItem(ReplacementList actionHistory) { return InkWell( onTap: () { + Navigator.pop(context); showMyBottomSheet(context, - callBackFunc: (){}, + callBackFunc: widget.callBackFunc, child: SelectedItemSheet( LocaleKeys.comments.tr(), apiMode: widget.apiMode, replacementList: actionHistory, notificationID: widget.notificationID, + isITGRequest: widget.wFHistory != null, )); }, child: Row( diff --git a/lib/ui/work_list/sheets/selected_item_sheet.dart b/lib/ui/work_list/sheets/selected_item_sheet.dart index e053218..54f87d0 100644 --- a/lib/ui/work_list/sheets/selected_item_sheet.dart +++ b/lib/ui/work_list/sheets/selected_item_sheet.dart @@ -1,12 +1,15 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/worklist/worklist_api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/models/get_action_history_list_model.dart'; +import 'package:mohem_flutter_app/models/member_information_list_model.dart'; import 'package:mohem_flutter_app/models/worklist/get_favorite_replacements_model.dart'; import 'package:mohem_flutter_app/models/worklist/replacement_list_model.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; @@ -19,8 +22,11 @@ class SelectedItemSheet extends StatelessWidget { GetActionHistoryList? actionHistoryList; GetFavoriteReplacements? favoriteReplacements; ReplacementList? replacementList; + MemberInformationListModel? memberInformationListModel; - SelectedItemSheet(this.title, {required this.apiMode, this.notificationID, this.actionHistoryList, this.favoriteReplacements, this.replacementList}); + bool isITGRequest; + + SelectedItemSheet(this.title, {required this.apiMode, this.notificationID, this.actionHistoryList, this.favoriteReplacements, this.replacementList, this.isITGRequest = false}); TextEditingController username = TextEditingController(); String comment = ""; @@ -41,9 +47,9 @@ class SelectedItemSheet extends StatelessWidget { children: [ title.toText24(isBold: true), 21.height, - if (actionHistoryList != null) showItem(actionHistoryList!.nAME, actionHistoryList!.isFavorite), - if (favoriteReplacements != null) showItem(favoriteReplacements!.employeeDisplayName, true), - if (replacementList != null) showItem(replacementList!.employeeDisplayName, replacementList!.isFavorite), + if (actionHistoryList != null) showItem(actionHistoryList!.nAME, context, actionHistoryList!.isFavorite), + if (favoriteReplacements != null) showItem(favoriteReplacements!.employeeDisplayName, context, true), + if (replacementList != null) showItem(replacementList!.employeeDisplayName, context, replacementList!.isFavorite), 14.height, InputWidget( "Enter a note", @@ -97,7 +103,7 @@ class SelectedItemSheet extends StatelessWidget { email = replacementList!.emailAddress; userId = replacementList!.userName; } - performNetworkCall(context, email: email ?? "", userId: userId ?? ""); + isITGRequest ? performITGNetworkCall(context, email: email ?? "", userId: userId ?? "") : performNetworkCall(context, email: email ?? "", userId: userId ?? ""); } else { Utils.showToast("Please enter comments"); } @@ -115,10 +121,26 @@ class SelectedItemSheet extends StatelessWidget { ); } + void getUserInformation(BuildContext context) async { + String? empID = ""; + if (actionHistoryList != null) empID = actionHistoryList!.uSERNAME; + if (favoriteReplacements != null) empID = favoriteReplacements!.userName; + if (replacementList != null) empID = replacementList!.userName; + try { + memberInformationListModel = await WorkListApiClient().getUserInformation(-999, empID!); + if (actionHistoryList != null) empID = actionHistoryList!.eMPLOYEEIMAGE = memberInformationListModel!.eMPLOYEEIMAGE ?? AppState().getBase64ImageEmp; + if (favoriteReplacements != null) empID = favoriteReplacements!.employeeImage = memberInformationListModel!.eMPLOYEEIMAGE ?? AppState().getBase64ImageEmp; + if (replacementList != null) empID = replacementList!.employeeImage = memberInformationListModel!.eMPLOYEEIMAGE ?? AppState().getBase64ImageEmp; + (context as Element).markNeedsBuild(); + } catch (ex) { + Utils.handleException(ex, context, null); + } + } + Future performNetworkCall(BuildContext context, {String? email, String? userId}) async { Utils.showLoading(context); try { - await WorkListApiClient().submitComment(comment: comment, email: email, userId: userId, notificationId: notificationID, apiMode: apiMode); + await WorkListApiClient().submitComment(comment: comment, email: email, userId: userId, notificationId: notificationID, apiMode: apiMode, approverIndex: actionHistoryList != null ? actionHistoryList!.sEQUENCE : null); Utils.hideLoading(context); // Navigator.pop(context); // Navigator.pop(context); @@ -129,30 +151,79 @@ class SelectedItemSheet extends StatelessWidget { } } - Widget showItem(String? name, bool? isFav) { + Future performITGNetworkCall(BuildContext context, {String? email, String? userId}) async { + Utils.showLoading(context); + try { + var requestDetails = AppState().requestAllList![AppState().itgWorkListIndex!]; + if (apiMode == "Delegate") { + await WorkListApiClient().delegateITGRequest(requestDetails.requestType!, requestDetails.iD!, requestDetails.itemID!, AppState().memberInformationList?.eMPLOYEENUMBER ?? "", + isITGRequest ? favoriteReplacements!.userName! : actionHistoryList!.uSERNAME!, comment); + } else if (apiMode == "RequestInformation") { + await WorkListApiClient().informationITGRequest(requestDetails.requestType!, requestDetails.iD!, requestDetails.itemID!, AppState().memberInformationList?.eMPLOYEENUMBER ?? "", + isITGRequest ? favoriteReplacements!.userName! : actionHistoryList!.uSERNAME!, comment); + } else if (apiMode == "Answer") { + await WorkListApiClient().answerITGRequest(requestDetails.requestType!, requestDetails.iD!, requestDetails.itemID!, AppState().memberInformationList?.eMPLOYEENUMBER ?? "", + isITGRequest ? favoriteReplacements!.userName! : actionHistoryList!.uSERNAME!, comment); + } + Utils.hideLoading(context); + Navigator.pop(context, "delegate_reload"); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + + Widget showItem(String? name, BuildContext context, bool? isFav) { + if (actionHistoryList != null) { + if (actionHistoryList!.eMPLOYEEIMAGE == null || actionHistoryList!.eMPLOYEEIMAGE == "") { + // getUserInformation(context); + } + } + if (favoriteReplacements != null) { + if (favoriteReplacements!.employeeImage == null || favoriteReplacements!.employeeImage == "") { + // getUserInformation(context); + } + } + if (replacementList != null) { + if (replacementList!.employeeImage == null || replacementList!.employeeImage == "") { + // getUserInformation(context); + } + } return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), - color: Color(0xffF7F7F7), + color: const Color(0xffF7F7F7), border: Border.all( - color: Color(0xffefefef), + color: const Color(0xffefefef), width: 1, ), ), - padding: EdgeInsets.only(top: 16, bottom: 16, left: 21, right: 21), + padding: const EdgeInsets.only(top: 16, bottom: 16, left: 21, right: 21), child: Row( children: [ - (actionHistoryList != null && actionHistoryList!.eMPLOYEEIMAGE != null) - ? CircularAvatar( - height: 30, - width: 30, - url: actionHistoryList!.eMPLOYEEIMAGE, - isImageBase64: true, - ) - : CircularAvatar( - height: 30, - width: 30, - ), + (isFav != null && isFav && favoriteReplacements != null) + ? favoriteReplacements!.employeeImage != "" + ? CircularAvatar( + height: 40, + width: 40, + url: favoriteReplacements != null ? favoriteReplacements!.employeeImage : actionHistoryList!.eMPLOYEEIMAGE, + isImageBase64: true, + ) + : CircularAvatar( + height: 40, + width: 40, + ) + : (actionHistoryList != null && actionHistoryList!.eMPLOYEEIMAGE != null) + ? CircularAvatar( + height: 40, + width: 40, + url: actionHistoryList!.eMPLOYEEIMAGE, + isImageBase64: true, + ) + : CircularAvatar( + height: 40, + width: 40, + ), 16.width, Expanded( child: (name ?? "").toText12(), diff --git a/lib/ui/work_list/sheets/selected_itg_item_sheet.dart b/lib/ui/work_list/sheets/selected_itg_item_sheet.dart index 7381d59..e836a07 100644 --- a/lib/ui/work_list/sheets/selected_itg_item_sheet.dart +++ b/lib/ui/work_list/sheets/selected_itg_item_sheet.dart @@ -126,10 +126,11 @@ class SelectedItgItemSheet extends StatelessWidget { } else if (apiMode == "RequestInformation") { await WorkListApiClient() .informationITGRequest(requestDetails.requestType!, requestDetails.iD!, requestDetails.itemID!, AppState().memberInformationList?.eMPLOYEENUMBER ?? "", wfHistory.employeeID!, comment); + } else if (apiMode == "Answer") { + await WorkListApiClient() + .answerITGRequest(requestDetails.requestType!, requestDetails.iD!, requestDetails.itemID!, AppState().memberInformationList?.eMPLOYEENUMBER ?? "", wfHistory.employeeID!, comment); } Utils.hideLoading(context); - Navigator.pop(context); - Navigator.pop(context); Navigator.pop(context, "delegate_reload"); } catch (ex) { Utils.hideLoading(context); diff --git a/lib/ui/work_list/sheets/update_continue_sheet.dart b/lib/ui/work_list/sheets/update_continue_sheet.dart new file mode 100644 index 0000000..1e4e5f3 --- /dev/null +++ b/lib/ui/work_list/sheets/update_continue_sheet.dart @@ -0,0 +1,137 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/api/my_attendance_api_client.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/config/routes.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/worklist/hr/eit_otification_body_model.dart'; +import 'package:mohem_flutter_app/models/worklist/resubmit_eit_response_model.dart'; +import 'package:mohem_flutter_app/models/worklist_response_model.dart'; +import 'package:mohem_flutter_app/ui/misc/request_submit_screen.dart'; +import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart'; +import 'package:mohem_flutter_app/widgets/button/default_button.dart'; +import 'package:mohem_flutter_app/widgets/item_detail_view_widget.dart'; + +class UpdateContinueSheet extends StatefulWidget { + WorkListResponseModel? workListData; + List? getEitCollectionNotificationBodyList; + DynamicListViewParams? dynamicParams; + + UpdateContinueSheet({this.workListData, this.getEitCollectionNotificationBodyList, this.dynamicParams, Key? key}) : super(key: key); + + @override + State createState() => _UpdateContinueSheetState(); +} + +class _UpdateContinueSheetState extends State { + double itemHeight = 0; + double itemWidth = 0; + List> validatedValues = []; + @override + Widget build(BuildContext context) { + var size = MediaQuery.of(context).size; + itemHeight = (size.height - kToolbarHeight - 24) / 9; + itemWidth = size.width / 2; + return Column( + children: [ + if ((widget.workListData?.sUBJECT ?? "").isNotEmpty) widget.workListData!.sUBJECT!.toText14().paddingOnly(top: 10, right: 21, left: 21, bottom: 21), + ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (cxt, index) { + List dataList = widget.getEitCollectionNotificationBodyList!.isEmpty ? [] : (widget.getEitCollectionNotificationBodyList![index].collectionNotification ?? []); + dataList = dataList.where((o) => o.displayFlag == "Y").toList(); + bool isOdd = false; + if (dataList.length % 2 != 0) { + isOdd = true; + dataList.add(new CollectionNotificationEit()); + } + return GridView.builder( + itemCount: dataList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) => ItemDetailViewGridItem( + index, + dataList[index].segmentPrompt, + dataList[index].segmentValueDsp, + isNeedToShowEmptyDivider: (dataList.length == index + 1) + ? isOdd + ? true + : false + : false, + ), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: (itemWidth / itemHeight), + ), + ).objectContainerView(); + }, + separatorBuilder: (cxt, index) => 12.height, + itemCount: widget.getEitCollectionNotificationBodyList!.length, + ), + Padding( + padding: const EdgeInsets.only(right: 21, left: 21, bottom: 21), + child: Row( + children: [ + DefaultButton( + LocaleKeys.edit.tr(), + () => performEditAction(), + colors: const [Color(0xffE47A7E), Color(0xffDE6D71)], + ).expanded, + 8.width, + DefaultButton( + LocaleKeys.next.tr(), + () => performNextAction(), + colors: const [Color(0xff28C884), Color(0xff1BB271)], + ).expanded, + ], + ), + ), + ], + ); + } + + void performNextAction() async { + //Resubmit EIT Transaction + try { + Utils.showLoading(context); + List> values = []; + if (validatedValues!.isNotEmpty) { + values = validatedValues; + } else { + for (GetEitCollectionNotificationBodyList element in widget.getEitCollectionNotificationBodyList!) { + for (CollectionNotificationEit collectionElement in element.collectionNotification!) { + values.add({ + "TRANSACTION_NUMBER": collectionElement.transactionNumber, + "NAME": collectionElement.applicationColumnName, + "VARCHAR2_VALUE": collectionElement.varchar2Value, + "NUMBER_VALUE": collectionElement.numberValue, + "DATE_VALUE": collectionElement.dateValue, + }); + } + } + } + + ResubmitEITRequestResponse resubmitEITRequestResponse = await MyAttendanceApiClient().reSubmitEitTransaction(widget.workListData!.iTEMKEY!, widget.workListData!.nOTIFICATIONID, values); + Utils.hideLoading(context); + await Navigator.pushNamed( + context, + AppRoutes.requestSubmitScreen, + arguments: RequestSubmitScreenParams(LocaleKeys.submit.tr(), resubmitEITRequestResponse.pTRANSACTIONID!, widget.workListData!.iTEMKEY!, 'eit'), + ); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + + void performEditAction() { + // ignore: avoid_annotating_with_dynamic + Navigator.pushNamed(context, AppRoutes.addDynamicInput, arguments: widget.dynamicParams).then((dynamic value) { + validatedValues = value; + }); + } +} diff --git a/lib/ui/work_list/work_list_screen.dart b/lib/ui/work_list/work_list_screen.dart index 9471568..f9f8d91 100644 --- a/lib/ui/work_list/work_list_screen.dart +++ b/lib/ui/work_list/work_list_screen.dart @@ -226,7 +226,6 @@ class _WorkListScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container(width: double.infinity, height: 1, color: MyColors.lightGreyEFColor), SizedBox( height: 40, child: ListView.separated( @@ -347,6 +346,7 @@ class _WorkListScreenState extends State { return InkWell( onTap: () async { AppState().setItgWorkListIndex = index; + AppState().setItgRequestType = requestDetails.requestType; var shouldReloadData = await Navigator.pushNamed(context, AppRoutes.itgDetail); if (shouldReloadData != null) { if (shouldReloadData.toString() == "delegate_reload") { diff --git a/lib/ui/work_list/worklist_detail_screen.dart b/lib/ui/work_list/worklist_detail_screen.dart index ec02631..824edc3 100644 --- a/lib/ui/work_list/worklist_detail_screen.dart +++ b/lib/ui/work_list/worklist_detail_screen.dart @@ -23,12 +23,16 @@ import 'package:mohem_flutter_app/models/get_stamp_ms_notification_body_list_mod import 'package:mohem_flutter_app/models/get_stamp_ns_notification_body_list_model.dart'; import 'package:mohem_flutter_app/models/member_information_list_model.dart'; import 'package:mohem_flutter_app/models/notification_get_respond_attributes_list_model.dart'; +import 'package:mohem_flutter_app/models/worklist/GetRFCEmployeeList.dart'; import 'package:mohem_flutter_app/models/worklist/hr/eit_otification_body_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/get_basic_det_ntf_body_list_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/get_contact_notification_body_list_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/get_phones_notification_body_list_model.dart'; import 'package:mohem_flutter_app/models/worklist_response_model.dart'; +import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart'; import 'package:mohem_flutter_app/ui/work_list/sheets/delegate_sheet.dart'; +import 'package:mohem_flutter_app/ui/work_list/sheets/selected_item_sheet.dart'; +import 'package:mohem_flutter_app/ui/work_list/sheets/update_continue_sheet.dart'; import 'package:mohem_flutter_app/ui/work_list/worklist_fragments/actions_fragment.dart'; import 'package:mohem_flutter_app/ui/work_list/worklist_fragments/attachments_fragment.dart'; import 'package:mohem_flutter_app/ui/work_list/worklist_fragments/detail_fragment.dart'; @@ -65,6 +69,8 @@ class _WorkListDetailScreenState extends State { List getNotificationRespondAttributes = []; NotificationGetRespondAttributesList? notificationNoteInput; + List getRFCEmployeeList = []; + //HR Details Screen Requests List? getEitCollectionNotificationBodyList = []; List? getPhonesNotificationBodyList = []; @@ -375,62 +381,166 @@ class _WorkListDetailScreenState extends State { } fabs.add(myFab( notificationButtonsList[i].bUTTONLABEL!, - notificationButtonsList[i].bUTTONACTION == "DELEGATE" ? "assets/images/delegate.svg" : notificationButtonsList[i].bUTTONICON ?? "", - isIconAsset: notificationButtonsList[i].bUTTONACTION == "DELEGATE" ? true : false, + getActionImage(notificationButtonsList[i].bUTTONACTION!), + isIconAsset: true, ).paddingOnly(bottom: 12).onPress(() => handleFabAction(notificationButtonsList[i]))); } return fabs; } + String getActionImage(String action) { + String returnActionImage = ""; + + switch (action) { + case "DELEGATE": + returnActionImage = "assets/images/delegate.svg"; + break; + case "REQUEST_INFO": + returnActionImage = "assets/images/request_info.svg"; + break; + case "RFC": + returnActionImage = "assets/images/worklist/return.svg"; + break; + case "UPDATE_ACTION": + returnActionImage = "assets/images/worklist/update_action.svg"; + break; + case "APPROVE_AND_FORWARD": + returnActionImage = "assets/images/worklist/approve_and_forward.svg"; + break; + case "FORWARD": + returnActionImage = "assets/images/worklist/forward.svg"; + break; + default: + returnActionImage = "assets/images/request_info.svg"; + break; + } + + return returnActionImage; + } + void handleFabAction(GetNotificationButtonsList notificationButton) { print("notificationButton:${notificationButton.bUTTONACTION}"); switch (notificationButton.bUTTONACTION) { case "DELEGATE": showMyBottomSheet(context, callBackFunc: reloadWorkList, - child: DelegateSheet(title: LocaleKeys.delegate.tr(), apiMode: notificationButton.bUTTONACTION!, notificationID: workListData!.nOTIFICATIONID, actionHistoryList: actionHistoryList)); + child: DelegateSheet( + title: LocaleKeys.delegate.tr(), + apiMode: notificationButton.bUTTONACTION!, + notificationID: workListData!.nOTIFICATIONID, + actionHistoryList: actionHistoryList, + callBackFunc: reloadWorkList, + )); break; case "REQUEST_INFO": // do something else showMyBottomSheet(context, callBackFunc: reloadWorkList, - child: DelegateSheet(title: LocaleKeys.request_info.tr(), apiMode: "REQUEST_INFO", notificationID: workListData!.nOTIFICATIONID, actionHistoryList: actionHistoryList)); + child: DelegateSheet( + title: LocaleKeys.request_info.tr(), + apiMode: "REQUEST_INFO", + notificationID: workListData!.nOTIFICATIONID, + actionHistoryList: actionHistoryList, + callBackFunc: reloadWorkList, + )); break; case "TRANSFER_INFO": // do something else showMyBottomSheet(context, callBackFunc: reloadWorkList, - child: - DelegateSheet(title: notificationButton.bUTTONLABEL!, apiMode: notificationButton.bUTTONACTION!, notificationID: workListData!.nOTIFICATIONID, actionHistoryList: actionHistoryList)); + child: DelegateSheet( + title: notificationButton.bUTTONLABEL!, + apiMode: notificationButton.bUTTONACTION!, + notificationID: workListData!.nOTIFICATIONID, + actionHistoryList: actionHistoryList, + callBackFunc: reloadWorkList, + )); break; case "ANSWER_INFO": performAction(notificationButton.bUTTONACTION!, title: notificationButton.bUTTONLABEL); break; case "RFC": - // do something else + getRFCEmployeeListFunc(); break; case "APPROVE": performAction(notificationButton.bUTTONACTION!); break; case "UPDATE_ACTION": - // do something else + showMyBottomSheet( + context, + callBackFunc: reloadWorkList, + child: UpdateContinueSheet( + workListData: workListData, + getEitCollectionNotificationBodyList: getEitCollectionNotificationBodyList, + dynamicParams: DynamicListViewParams(workListData!.sUBJECT!, workListData!.fUNCTIONNAME!, + isUpdate: true, collectionNotificationList: getEitCollectionNotificationBodyList![0].collectionNotification)), + ); + break; + case "CONTINUE_ACTION": + showMyBottomSheet( + context, + callBackFunc: reloadWorkList, + child: UpdateContinueSheet( + workListData: workListData, + getEitCollectionNotificationBodyList: getEitCollectionNotificationBodyList, + dynamicParams: DynamicListViewParams(workListData!.sUBJECT!, workListData!.fUNCTIONNAME!, + isUpdate: true, collectionNotificationList: getEitCollectionNotificationBodyList![0].collectionNotification)), + ); + break; case "APPROVE_AND_FORWARD": showMyBottomSheet(context, callBackFunc: reloadWorkList, - child: DelegateSheet(title: "Approve and Forward", apiMode: notificationButton.bUTTONACTION!, notificationID: workListData!.nOTIFICATIONID, actionHistoryList: actionHistoryList)); + child: DelegateSheet( + title: "Approve and Forward", + apiMode: notificationButton.bUTTONACTION!, + notificationID: workListData!.nOTIFICATIONID, + actionHistoryList: actionHistoryList, + callBackFunc: reloadWorkList, + )); break; case "FORWARD": showMyBottomSheet(context, callBackFunc: reloadWorkList, - child: DelegateSheet(title: "Forward", apiMode: notificationButton.bUTTONACTION!, notificationID: workListData!.nOTIFICATIONID, actionHistoryList: actionHistoryList)); + child: DelegateSheet( + title: "Forward", + apiMode: notificationButton.bUTTONACTION!, + notificationID: workListData!.nOTIFICATIONID, + actionHistoryList: actionHistoryList, + callBackFunc: reloadWorkList, + )); break; case "REJECT": - performNetworkCall(context, email: "", userId: ""); + performAction(notificationButton.bUTTONACTION!); break; case "RETURNED": Navigator.pop(context); break; } + setState(() { + showFabOptions = false; + }); + } + + void getRFCEmployeeListFunc() async { + try { + Utils.showLoading(context); + getRFCEmployeeList = await WorkListApiClient().getRFCEmployeeeList(workListData!.nOTIFICATIONID!); + Utils.hideLoading(context); + actionHistoryList.last.sEQUENCE = getRFCEmployeeList[0].sEQ; + showMyBottomSheet(context, + callBackFunc: reloadWorkList, + child: SelectedItemSheet( + "Comment", + apiMode: "RFC", + actionHistoryList: actionHistoryList.last, + notificationID: workListData!.nOTIFICATIONID, + isITGRequest: false, + )); + setState(() {}); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } } Future performNetworkCall(BuildContext context, {String? email, String? userId}) async { @@ -516,7 +626,7 @@ class _WorkListDetailScreenState extends State { } void performAction(String actionMode, {String? title}) { - TextEditingController textEditingController = TextEditingController(); + print(actionMode); showDialog( context: context, builder: (cxt) => AcceptRejectInputDialog( @@ -539,7 +649,7 @@ class _WorkListDetailScreenState extends State { } ], }; - if (actionMode == "APPROVED" || actionMode == "APPROVE" || actionMode == "CLOSE") { + if (actionMode == "APPROVED" || actionMode == "APPROVE" || actionMode == "CLOSE" || actionMode == "ANSWER_INFO" || actionMode == "RFC") { performNotificationAction(payload); } else if (note.isNotEmpty && (actionMode != "APPROVED" || actionMode != "APPROVE")) { performNotificationAction(payload); @@ -823,6 +933,8 @@ class _WorkListDetailScreenState extends State { void getActionHistory() async { try { + isActionHistoryLoaded = false; + actionHistoryList.clear(); // if (apiCallCount == 0) Utils.showLoading(context); // apiCallCount++; actionHistoryList = await WorkListApiClient().getActionHistory(workListData!.nOTIFICATIONID!); @@ -842,6 +954,8 @@ class _WorkListDetailScreenState extends State { void getAttachments() async { try { + isAttachmentLoaded = false; + getAttachmentList.clear(); // if (apiCallCount == 0) Utils.showLoading(context); // apiCallCount++; getAttachmentList = await WorkListApiClient().getAttachments(workListData!.nOTIFICATIONID!); @@ -859,12 +973,76 @@ class _WorkListDetailScreenState extends State { } } + // Widget showUpdateContinueSheet(List list) { + // double itemHeight = 0; + // double itemWidth = 0; + // var size = MediaQuery.of(context).size; + // itemHeight = (size.height - kToolbarHeight - 24) / 9; + // itemWidth = size.width / 2; + // return Column( + // children: [ + // if ((workListData?.sUBJECT ?? "").isNotEmpty) workListData!.sUBJECT!.toText14().paddingOnly(top: 10, right: 21, left: 21, bottom: 21), + // ListView.separated( + // shrinkWrap: true, + // physics: const NeverScrollableScrollPhysics(), + // itemBuilder: (cxt, index) { + // List dataList = list.isEmpty ? [] : (list[index].collectionNotification ?? []); + // dataList = dataList.where((o) => o.displayFlag == "Y").toList(); + // bool isOdd = false; + // if (dataList.length % 2 != 0) { + // isOdd = true; + // dataList.add(new CollectionNotificationEit()); + // } + // return GridView.builder( + // itemCount: dataList.length, + // shrinkWrap: true, + // physics: const NeverScrollableScrollPhysics(), + // itemBuilder: (context, index) => ItemDetailViewGridItem( + // index, + // dataList[index].segmentPrompt, + // dataList[index].segmentValueDsp, + // isNeedToShowEmptyDivider: (dataList.length == index + 1) + // ? isOdd + // ? true + // : false + // : false, + // ), + // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + // crossAxisCount: 2, + // childAspectRatio: (itemWidth / itemHeight), + // ), + // ).objectContainerView(); + // }, + // separatorBuilder: (cxt, index) => 12.height, + // itemCount: list.length, + // ), + // Padding( + // padding: const EdgeInsets.only(right: 21, left: 21, bottom: 21), + // child: Row( + // children: [ + // DefaultButton( + // LocaleKeys.edit.tr(), + // () => performEditAction(), + // colors: const [Color(0xffE47A7E), Color(0xffDE6D71)], + // ).expanded, + // 8.width, + // DefaultButton( + // LocaleKeys.next.tr(), + // () => performNextAction(), + // colors: const [Color(0xff28C884), Color(0xff1BB271)], + // ).expanded, + // ], + // ), + // ), + // ], + // ); + // } + Widget showLoadingAnimation() { return Lottie.asset( 'assets/lottie/loading.json', repeat: true, reverse: false, - fit: BoxFit.contain, ); } } diff --git a/lib/ui/work_list/worklist_fragments/actions_fragment.dart b/lib/ui/work_list/worklist_fragments/actions_fragment.dart index 8a69599..58e9ac7 100644 --- a/lib/ui/work_list/worklist_fragments/actions_fragment.dart +++ b/lib/ui/work_list/worklist_fragments/actions_fragment.dart @@ -27,7 +27,7 @@ class ActionsFragment extends StatelessWidget { itemCount: actionHistoryList.length, padding: EdgeInsets.all(21), itemBuilder: (context, index) { - return showItem(context, actionHistoryList[index]); + return showItem(context, actionHistoryList[index], index); }, separatorBuilder: (BuildContext context, int index) { return 12.height; @@ -36,7 +36,7 @@ class ActionsFragment extends StatelessWidget { ); } - Widget showItem(BuildContext context, GetActionHistoryList actionHistory) { + Widget showItem(BuildContext context, GetActionHistoryList actionHistory, int index) { return Container( width: double.infinity, decoration: BoxDecoration( @@ -89,7 +89,9 @@ class ActionsFragment extends StatelessWidget { if (actionHistory.nOTIFICATIONDATE!.isNotEmpty) DateUtil.formatDateToDate(DateUtil.convertSimpleStringDateToDateddMMyyyy(actionHistory.nOTIFICATIONDATE!), false).toText12(color: MyColors.lightTextColor), ], - ) + ), + 10.height, + getActionDuration(index).toText11(maxLine: 1, color: const Color(0xff1FA269)) ], ), ) @@ -127,8 +129,22 @@ class ActionsFragment extends StatelessWidget { ); } + String getActionDuration(int index) { + if (actionHistoryList[index].aCTIONCODE == "SUBMIT") { + return ""; + } else if(actionHistoryList[index].aCTIONCODE == "PENDING") { + DateTime dateTimeFrom = DateUtil.convertSimpleStringDateToDate(actionHistoryList[++index].nOTIFICATIONDATE!); + Duration duration = DateTime.now().difference(dateTimeFrom); + return "Action duration: " + DateUtil.formatDuration(duration); + } else { + DateTime dateTimeTo = DateUtil.convertSimpleStringDateToDate(actionHistoryList[index].nOTIFICATIONDATE!); + DateTime dateTimeFrom = DateUtil.convertSimpleStringDateToDate(actionHistoryList[++index].nOTIFICATIONDATE!); + Duration duration = dateTimeTo.difference(dateTimeFrom); + return "Action duration: " + DateUtil.formatDuration(duration); + } + } + Color getStatusColor(String code) { - print("code:$code"); if (code == "SUBMIT") { return const Color(0xff2E303A); } else if (code == "REJECTED") { @@ -139,7 +155,7 @@ class ActionsFragment extends StatelessWidget { return MyColors.orange; } else if (code == "APPROVED" || code == "APPROVE" || code == "ANSWER_INFO") { return const Color(0xff1FA269); - } else if (code == "REQUEST_INFO"|| code == "FORWARD") { + } else if (code == "REQUEST_INFO" || code == "FORWARD") { return const Color(0xff2E303A); } else if (code != "SUBMIT" && code != "REJECT" && code != "PENDING") { return MyColors.orange; diff --git a/lib/widgets/app_bar_widget.dart b/lib/widgets/app_bar_widget.dart index 745dedf..c2129c1 100644 --- a/lib/widgets/app_bar_widget.dart +++ b/lib/widgets/app_bar_widget.dart @@ -7,11 +7,7 @@ import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; AppBar AppBarWidget(BuildContext context, - {required String title, - bool showHomeButton = true, - bool showNotificationButton = false, - bool showMemberButton = false, - String? image}) { + {required String title, bool showHomeButton = true, bool showNotificationButton = false, bool showMemberButton = false, String? image, List? actions}) { return AppBar( leadingWidth: 0, // leading: GestureDetector( @@ -24,17 +20,16 @@ AppBar AppBarWidget(BuildContext context, children: [ GestureDetector( behavior: HitTestBehavior.opaque, - onTap: - Feedback.wrapForTap(() => Navigator.maybePop(context), context), - child: - const Icon(Icons.arrow_back_ios, color: MyColors.darkIconColor), + onTap: Feedback.wrapForTap(() => Navigator.maybePop(context), context), + child: const Icon(Icons.arrow_back_ios, color: MyColors.darkIconColor), ), 4.width, - if (image != null) SvgPicture.asset( - image, - height: 40, - width: 40, - ), + if (image != null) + SvgPicture.asset( + image, + height: 40, + width: 40, + ), if (image != null) 14.width, title.toText24(color: MyColors.darkTextColor, isBold: true).expanded, ], @@ -46,8 +41,7 @@ AppBar AppBarWidget(BuildContext context, if (showHomeButton) IconButton( onPressed: () { - Navigator.popUntil( - context, ModalRoute.withName(AppRoutes.dashboard)); + Navigator.popUntil(context, ModalRoute.withName(AppRoutes.dashboard)); }, icon: const Icon(Icons.home, color: MyColors.darkIconColor), ), @@ -65,6 +59,7 @@ AppBar AppBarWidget(BuildContext context, }, icon: const Icon(Icons.people, color: MyColors.textMixColor), ), + ...actions??[] ], ); } diff --git a/lib/widgets/bottom_sheets/search_employee_bottom_sheet.dart b/lib/widgets/bottom_sheets/search_employee_bottom_sheet.dart index 16942c5..b88b96f 100644 --- a/lib/widgets/bottom_sheets/search_employee_bottom_sheet.dart +++ b/lib/widgets/bottom_sheets/search_employee_bottom_sheet.dart @@ -136,12 +136,13 @@ class _SearchEmployeeBottomSheetState extends State { }, ).expanded, IconButton( - constraints: const BoxConstraints(), - onPressed: () async { - await SystemChannels.textInput.invokeMethod('TextInput.hide'); - widget.fromChat ? fetchChatUser() : fetchUserByInput(); - }, - icon: Icon(Icons.search)) + constraints: const BoxConstraints(), + onPressed: () async { + await SystemChannels.textInput.invokeMethod('TextInput.hide'); + widget.fromChat ? fetchChatUser() : fetchUserByInput(); + }, + icon: const Icon(Icons.search), + ) ], ), if (replacementList != null) @@ -183,16 +184,25 @@ class _SearchEmployeeBottomSheetState extends State { if (widget.fromChat) if (chatUsersList != null && widget.fromChat) chatUsersList!.isEmpty - ? Utils.getNoDataWidget(context) + ? Column( + children: [ + 20.height, + Utils.getNoDataWidget(context), + ], + ) : ListView( physics: const BouncingScrollPhysics(), - padding: EdgeInsets.only(top: 0, bottom: 8), - children: [ + padding: const EdgeInsets.only( + top: 15, + ), + children: [ ListView.separated( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemBuilder: (cxt, index) { - return ListTile( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemBuilder: (BuildContext cxt, int index) { + return SizedBox( + height: 55, + child: ListTile( leading: Stack( children: [ SvgPicture.asset( @@ -217,24 +227,27 @@ class _SearchEmployeeBottomSheetState extends State { ], ), title: (chatUsersList![index].userName ?? "").toText14(color: MyColors.darkTextColor), - // subtitle: (chatUsersList![index].isTyping == true ? "Something is Typing" : "Last message text").toText11(color: MyColors.normalTextColor), - // trailing: ("Today").toText10(color: MyColors.lightTextColor), minVerticalPadding: 0, onTap: () { Navigator.pop(context); Navigator.pushNamed( context, AppRoutes.chatDetailed, - arguments: {"targetUser": chatUsersList![index]}, + arguments: {"targetUser": chatUsersList![index], "isNewChat": true}, ); }, - ); - }, - separatorBuilder: (BuildContext cxt, int index) => Container( - height: 1, - color: MyColors.borderE3Color, - ), - itemCount: chatUsersList?.length ?? 0), + + ), + ); + }, + separatorBuilder: (BuildContext context, int index) => const Padding( + padding: EdgeInsets.only(right: 10, left: 70, bottom: 0, top: 0), + child: Divider( + color: Color(0xFFE5E5E5), + ), + ), + itemCount: chatUsersList?.length ?? 0, + ), 12.height, ], ).expanded, @@ -293,7 +306,9 @@ class _SearchEmployeeBottomSheetState extends State { decoration: BoxDecoration( color: Colors.transparent, border: Border.all(color: MyColors.borderColor, width: 1), - borderRadius: const BorderRadius.all(Radius.circular(100)), + borderRadius: const BorderRadius.all( + Radius.circular(100), + ), ), padding: const EdgeInsets.all(4), child: Container( @@ -301,7 +316,9 @@ class _SearchEmployeeBottomSheetState extends State { height: double.infinity, decoration: BoxDecoration( color: value == groupValue ? MyColors.grey3AColor : Colors.transparent, - borderRadius: BorderRadius.all(const Radius.circular(100)), + borderRadius: const BorderRadius.all( + Radius.circular(100), + ), ), ), ), diff --git a/lib/widgets/dialogs/accept_reject_input_dialog.dart b/lib/widgets/dialogs/accept_reject_input_dialog.dart index a0faf2c..ac5db05 100644 --- a/lib/widgets/dialogs/accept_reject_input_dialog.dart +++ b/lib/widgets/dialogs/accept_reject_input_dialog.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; @@ -87,12 +88,21 @@ class AcceptRejectInputDialog extends StatelessWidget { ).expanded, 10.width, DefaultButton( - actionMode == "REJECTED" ? LocaleKeys.reject.tr() : LocaleKeys.ok.tr(), + (actionMode == "REJECTED" || actionMode == "REJECT") ? LocaleKeys.reject.tr() : LocaleKeys.ok.tr(), () { - Navigator.pop(context); - onTap(note); + if (note.isEmpty) { + if ((actionMode != "REJECTED" && actionMode != "REJECT") && actionMode != "ANSWER_INFO" && actionMode != "RFC") { + Navigator.pop(context); + onTap(note); + } else { + Utils.showToast(LocaleKeys.pleaseEnterComments.tr()); + } + } else { + Navigator.pop(context); + onTap(note); + } }, - colors: actionMode == "REJECTED" + colors: (actionMode == "REJECTED" || actionMode == "REJECT") ? const [ Color(0xffE47A7E), Color(0xffE47A7E), diff --git a/lib/widgets/image_picker.dart b/lib/widgets/image_picker.dart index 5e5ac01..f92f9f4 100644 --- a/lib/widgets/image_picker.dart +++ b/lib/widgets/image_picker.dart @@ -13,7 +13,7 @@ class ImageOptions { static void showImageOptionsNew(BuildContext context, bool showFilesOption, Function(String, File) image) { showMyBottomSheet( context, - callBackFunc: (){}, + callBackFunc: () {}, child: AttachmentOptions( showFilesOption: showFilesOption, onCameraTap: () async { @@ -43,7 +43,12 @@ class ImageOptions { } }, onFilesTap: () async { - FilePickerResult? result = await FilePicker.platform.pickFiles(); + FilePickerResult? result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowedExtensions: ['jpg', 'jpeg ', 'pdf', 'txt', 'docx', 'doc', 'pptx', 'xlsx', 'png', 'rar', 'zip', 'xls'], + ); + List files = result!.paths.map((path) => File(path!)).toList(); + image(result.files.first.path.toString(), files.first); }, ), ); diff --git a/lib/widgets/item_detail_view_widget.dart b/lib/widgets/item_detail_view_widget.dart index 05495e9..5148389 100644 --- a/lib/widgets/item_detail_view_widget.dart +++ b/lib/widgets/item_detail_view_widget.dart @@ -80,9 +80,13 @@ class ItemDetailViewGridItem extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ - Flexible(child: "$title:".toText12Auto(isBold: true, color: const Color(0xff2BB8A6))), + title != null ? Flexible(child: "$title:".toText12Auto(isBold: true, color: const Color(0xff2BB8A6))) : Container(), 4.width, - type!.toLowerCase() == "table" ? getStringFromJSON(value!) : Flexible(child: (value!.isEmpty ? "--" : value).toString().toText12Auto(color: MyColors.normalTextColor)), + type != null + ? type!.toLowerCase() == "table" + ? getStringFromJSON(value!) + : Flexible(child: (value!.isEmpty ? "--" : value).toString().toText12Auto(color: MyColors.normalTextColor)) + : Container(), ], ), ); diff --git a/pubspec.yaml b/pubspec.yaml index 257d80b..f5742bb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -85,9 +85,15 @@ dependencies: appinio_swiper: ^1.1.1 expandable: ^5.0.1 + + #Chat signalr_netcore: ^1.3.3 logging: ^1.0.1 + swipe_to: ^1.0.2 + flutter_webrtc: ^0.9.16 + camera: ^0.10.0+4 + @@ -127,6 +133,8 @@ flutter: - assets/lottie/ - assets/audio/ - assets/images/ + - assets/images/worklist/ + - assets/images/services_icons/ - assets/images/login/ - assets/icons/chat/ - assets/images/logos/