Merge branch 'refs/heads/design_3.0_traf_module' into design_3.0_GlobalHealth
# Conflicts: # lib/controllers/api_routes/urls.dart # lib/controllers/providers/api/all_requests_provider.dart # lib/dashboard_latest/widgets/request_category_list.dart # lib/models/new_models/dashboard_detail.dart # lib/new_views/pages/land_page/my_request/my_requests_page.dart # lib/new_views/pages/land_page/requests/request_paginated_listview.dart # lib/new_views/pages/land_page/widgets/request_item_view_list.dartdesign_3.0_GlobalHealth
						commit
						facf80b07e
					
				| @ -0,0 +1,120 @@ | ||||
| import 'dart:convert'; | ||||
| 
 | ||||
| import 'package:flutter/cupertino.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:test_sa/controllers/api_routes/api_manager.dart'; | ||||
| import 'package:test_sa/controllers/api_routes/urls.dart'; | ||||
| import 'package:test_sa/models/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart'; | ||||
| import 'package:test_sa/models/service_request/spare_parts.dart'; | ||||
| 
 | ||||
| class OracleCodeProvider extends ChangeNotifier { | ||||
|   // number of items call in each request | ||||
|   final pageItemNumber = 20; | ||||
| 
 | ||||
|   //reset provider data | ||||
|   void reset() { | ||||
|     _parts = null; | ||||
|     _stateCode = null; | ||||
|   } | ||||
| 
 | ||||
|   // state code of current request to defied error message | ||||
|   // like 400 customer request failed | ||||
|   // 500 service not available | ||||
|   int? _stateCode; | ||||
| 
 | ||||
|   int? get stateCode => _stateCode; | ||||
| 
 | ||||
|   // true if there is next pagein product list and false if not | ||||
|   bool _nextPage = true; | ||||
| 
 | ||||
|   bool get nextPage => _nextPage; | ||||
| 
 | ||||
|   // contain user data | ||||
|   // when user not login or register _user = null | ||||
|   List<SparePartsWorkOrders>? _parts; | ||||
| 
 | ||||
|   List<SparePartsWorkOrders>? get parts => _parts; | ||||
| 
 | ||||
|   // when categories in-process _loading = true | ||||
|   // done _loading = true | ||||
|   // failed _loading = false | ||||
|   bool _loading = false; | ||||
| 
 | ||||
|   bool get isLoading => _loading; | ||||
| 
 | ||||
|   set isLoading(bool isLoading) { | ||||
|     _loading = isLoading; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| 
 | ||||
|   /// return -2 if request in progress | ||||
|   /// return -1 if error happen when sending request | ||||
|   /// return state code if request complete may be 200, 404 or 403 | ||||
|   /// for more details check http state manager | ||||
|   /// lib\controllers\http_status_manger\http_status_manger.dart | ||||
|   // Future<int> getParts({String? title}) async { | ||||
|   //   if (_loading == true) return -2; | ||||
|   //   _loading = true; | ||||
|   //   notifyListeners(); | ||||
|   //   late Response response; | ||||
|   //   try { | ||||
|   //     response = await ApiManager.instance.post(URLs.getPartNumber, body: {if (title != null && title.isNotEmpty) "partName": title}); | ||||
|   //     _stateCode = response.statusCode; | ||||
|   //     if (response.statusCode >= 200 && response.statusCode < 300) { | ||||
|   //       // client's request was successfully received | ||||
|   //       List categoriesListJson = json.decode(utf8.decode(response.bodyBytes)); | ||||
|   //       List<SparePart> page = categoriesListJson.map((part) => SparePart.fromJson(part)).toList(); | ||||
|   //       _parts ??= []; | ||||
|   //       _parts!.addAll(page.map((e) => SparePartsWorkOrders(sparePart: e)).toList()); | ||||
|   //       _nextPage = page.length >= pageItemNumber; | ||||
|   //     } | ||||
|   //     _loading = false; | ||||
|   //     notifyListeners(); | ||||
|   //     return response.statusCode; | ||||
|   //   } catch (error) { | ||||
|   //     _loading = false; | ||||
|   //     _stateCode = -1; | ||||
|   //     notifyListeners(); | ||||
|   //     return -1; | ||||
|   //   } | ||||
|   // } | ||||
| 
 | ||||
|   /// return -2 if request in progress | ||||
|   /// return -1 if error happen when sending request | ||||
|   /// return state code if request complete may be 200, 404 or 403 | ||||
|   /// for more details check http state manager | ||||
|   /// lib\controllers\http_status_manger\http_status_manger.dart | ||||
|   Future<List<AssetNDAutoCompleteByDynamicCodesModel>> getAssetByOracleCode(String oracleCode) async { | ||||
|     late Response response; | ||||
|     try { | ||||
|       response = await ApiManager.instance.post(URLs.getAssetNDAutoCompleteByDynamicCodes, body: {"codeValue": oracleCode}); | ||||
|       List<AssetNDAutoCompleteByDynamicCodesModel> page = []; | ||||
|       if (response.statusCode >= 200 && response.statusCode < 300) { | ||||
|         // client's request was successfully received | ||||
|         List categoriesListJson = json.decode(response.body)["data"]; | ||||
|         page = categoriesListJson.map((part) => AssetNDAutoCompleteByDynamicCodesModel.fromJson(part)).toList(); | ||||
|       } | ||||
|       return page; | ||||
|     } catch (error) { | ||||
|       return []; | ||||
|     } | ||||
|   } | ||||
| // // Implement this only for spare part request for now show and search on display name .... | ||||
| // | ||||
| // Future<List<SparePart>> getPartsListByDisplayName({num? assetId, String? displayName}) async { | ||||
| //   late Response response; | ||||
| //   try { | ||||
| //       response = await ApiManager.instance.post(URLs.getPartNumber, body: {"displayName": displayName, }); | ||||
| //     List<SparePart> page = []; | ||||
| //     if (response.statusCode >= 200 && response.statusCode < 300) { | ||||
| //       // client's request was successfully received | ||||
| //       List categoriesListJson = json.decode(response.body)["data"]; | ||||
| //       page = categoriesListJson.map((part) => SparePart.fromJson(part,true)).toList(); | ||||
| //     } | ||||
| //     return page; | ||||
| //   } catch (error) { | ||||
| //     return []; | ||||
| //   } | ||||
| // } | ||||
| } | ||||
| @ -0,0 +1,64 @@ | ||||
| import 'dart:convert'; | ||||
| 
 | ||||
| import 'package:flutter/cupertino.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:test_sa/controllers/api_routes/api_manager.dart'; | ||||
| import 'package:test_sa/controllers/api_routes/urls.dart'; | ||||
| import 'package:test_sa/models/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart'; | ||||
| import 'package:test_sa/models/new_models/users_based_on_search_model.dart'; | ||||
| import 'package:test_sa/models/service_request/spare_parts.dart'; | ||||
| 
 | ||||
| class UserSearchProvider extends ChangeNotifier { | ||||
|   // number of items call in each request | ||||
|   final pageItemNumber = 20; | ||||
| 
 | ||||
|   //reset provider data | ||||
|   void reset() { | ||||
|     _parts = null; | ||||
|     _stateCode = null; | ||||
|   } | ||||
| 
 | ||||
|   // state code of current request to defied error message | ||||
|   // like 400 customer request failed | ||||
|   // 500 service not available | ||||
|   int? _stateCode; | ||||
| 
 | ||||
|   int? get stateCode => _stateCode; | ||||
| 
 | ||||
|   // true if there is next pagein product list and false if not | ||||
|   bool _nextPage = true; | ||||
| 
 | ||||
|   bool get nextPage => _nextPage; | ||||
| 
 | ||||
|   // contain user data | ||||
|   // when user not login or register _user = null | ||||
|   List<SparePartsWorkOrders>? _parts; | ||||
| 
 | ||||
|   List<SparePartsWorkOrders>? get parts => _parts; | ||||
| 
 | ||||
|   bool _loading = false; | ||||
| 
 | ||||
|   bool get isLoading => _loading; | ||||
| 
 | ||||
|   set isLoading(bool isLoading) { | ||||
|     _loading = isLoading; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| 
 | ||||
|   Future<List<UsersBasedOnSearchModel>> getUsersBasedOnSearch(String query) async { | ||||
|     late Response response; | ||||
|     try { | ||||
|       response = await ApiManager.instance.get(URLs.getUsersBasedOnSearch + "?searchText=$query"); | ||||
|       List<UsersBasedOnSearchModel> page = []; | ||||
|       if (response.statusCode >= 200 && response.statusCode < 300) { | ||||
|         // client's request was successfully received | ||||
|         List categoriesListJson = json.decode(response.body); | ||||
|         page = categoriesListJson.map((part) => UsersBasedOnSearchModel.fromJson(part)).toList(); | ||||
|       } | ||||
|       return page; | ||||
|     } catch (error) { | ||||
|       return []; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,27 @@ | ||||
| class AssetNDAutoCompleteByDynamicCodesModel { | ||||
|   int? id; | ||||
|   String? assetName; | ||||
|   int? codeTypeId; | ||||
|   String? codeValue; | ||||
|   String? displayName; | ||||
| 
 | ||||
|   AssetNDAutoCompleteByDynamicCodesModel({this.id, this.assetName, this.codeTypeId, this.codeValue, this.displayName}); | ||||
| 
 | ||||
|   AssetNDAutoCompleteByDynamicCodesModel.fromJson(Map<String, dynamic> json) { | ||||
|     id = json['id']; | ||||
|     assetName = json['assetName']; | ||||
|     codeTypeId = json['codeTypeId']; | ||||
|     codeValue = json['codeValue']; | ||||
|     displayName = json['displayName']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['id'] = this.id; | ||||
|     data['assetName'] = this.assetName; | ||||
|     data['codeTypeId'] = this.codeTypeId; | ||||
|     data['codeValue'] = this.codeValue; | ||||
|     data['displayName'] = this.displayName; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,101 @@ | ||||
| import 'package:test_sa/models/new_models/room_model.dart'; | ||||
| 
 | ||||
| import '../base.dart'; | ||||
| 
 | ||||
| class TrafDepartment extends Base { | ||||
|   TrafDepartment({ | ||||
|     this.departmentName, this.departmentCode, this.ntCode, this.costCenterNumber, this.costCenterName, this.name, this.id, this.createdBy, this.createdDate, this.modifiedBy, this.modifiedDate | ||||
|   }) : super(identifier: id?.toString() ?? '', name: departmentName); // Handle potential null id | ||||
| 
 | ||||
|   TrafDepartment.fromJson(Map<String, dynamic> json) { | ||||
|     departmentName = json['departmentName'] ?? json['name']; | ||||
|     departmentCode = json['departmentCode']; | ||||
|     ntCode = json['ntCode']; | ||||
|     costCenterNumber = json['costCenterNumber']; | ||||
|     costCenterName = json['costCenterName']; | ||||
|     name = json['name']; | ||||
|     id = json['id']; | ||||
|     createdBy = json['createdBy']; | ||||
|     createdDate = json['createdDate']; | ||||
|     modifiedBy = json['modifiedBy']; | ||||
|     modifiedDate = json['modifiedDate']; | ||||
|   } | ||||
| 
 | ||||
|   num? id; // Now nullable | ||||
|   String? departmentName; // Now nullable | ||||
|   String? departmentCode; // Now nullable | ||||
|   String? ntCode; | ||||
|   String? costCenterNumber; | ||||
|   String? costCenterName; | ||||
|   String? name; | ||||
|   String? createdBy; | ||||
|   String? createdDate; | ||||
|   String? modifiedBy; | ||||
|   String? modifiedDate; | ||||
| 
 | ||||
|   // TrafDepartment copyWith({ | ||||
|   //   num? id, // Parameters are now nullable | ||||
|   //   String? departmentName, | ||||
|   //   String? departmentCode, | ||||
|   //   String? departmentId, | ||||
|   //   String? ntCode, | ||||
|   //   List<Rooms>? rooms, | ||||
|   // }) => | ||||
|   //     TrafDepartment( | ||||
|   //       id: id ?? this.id, | ||||
|   //       departmentName: departmentName ?? this.departmentName, | ||||
|   //       departmentCode: departmentCode ?? this.departmentCode, | ||||
|   //       departmentId: departmentId ?? this.departmentId, | ||||
|   //       ntCode: ntCode ?? this.ntCode, | ||||
|   //       rooms: rooms ?? this.rooms, | ||||
|   //     ); | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // class TrafDepartment extends Base { | ||||
| //   String? departmentName; | ||||
| //   String? departmentCode; | ||||
| //   Null? ntCode; | ||||
| //   Null? costCenterNumber; | ||||
| //   Null? costCenterName; | ||||
| //   String? name; | ||||
| //   int? id; | ||||
| //   String? createdBy; | ||||
| //   String? createdDate; | ||||
| //   Null? modifiedBy; | ||||
| //   Null? modifiedDate; | ||||
| // | ||||
| //   TrafDepartment( | ||||
| //       {this.departmentName, this.departmentCode, this.ntCode, this.costCenterNumber, this.costCenterName, this.name, this.id, this.createdBy, this.createdDate, this.modifiedBy, this.modifiedDate}); | ||||
| // | ||||
| //   TrafDepartment.fromJson(Map<String, dynamic> json) { | ||||
| //     departmentName = json['departmentName']; | ||||
| //     departmentCode = json['departmentCode']; | ||||
| //     ntCode = json['ntCode']; | ||||
| //     costCenterNumber = json['costCenterNumber']; | ||||
| //     costCenterName = json['costCenterName']; | ||||
| //     name = json['name']; | ||||
| //     id = json['id']; | ||||
| //     createdBy = json['createdBy']; | ||||
| //     createdDate = json['createdDate']; | ||||
| //     modifiedBy = json['modifiedBy']; | ||||
| //     modifiedDate = json['modifiedDate']; | ||||
| //   } | ||||
| // | ||||
| //   Map<String, dynamic> toJson() { | ||||
| //     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
| //     data['departmentName'] = this.departmentName; | ||||
| //     data['departmentCode'] = this.departmentCode; | ||||
| //     data['ntCode'] = this.ntCode; | ||||
| //     data['costCenterNumber'] = this.costCenterNumber; | ||||
| //     data['costCenterName'] = this.costCenterName; | ||||
| //     data['name'] = this.name; | ||||
| //     data['id'] = this.id; | ||||
| //     data['createdBy'] = this.createdBy; | ||||
| //     data['createdDate'] = this.createdDate; | ||||
| //     data['modifiedBy'] = this.modifiedBy; | ||||
| //     data['modifiedDate'] = this.modifiedDate; | ||||
| //     return data; | ||||
| //   } | ||||
| // } | ||||
| @ -0,0 +1,44 @@ | ||||
| class UsersBasedOnSearchModel { | ||||
|   String? userId; | ||||
|   String? userName; | ||||
|   String? email; | ||||
|   String? employeeId; | ||||
|   int? languageId; | ||||
|   String? extensionNo; | ||||
|   String? phoneNumber; | ||||
|   bool? isActive; | ||||
| 
 | ||||
|   UsersBasedOnSearchModel( | ||||
|       {this.userId, | ||||
|         this.userName, | ||||
|         this.email, | ||||
|         this.employeeId, | ||||
|         this.languageId, | ||||
|         this.extensionNo, | ||||
|         this.phoneNumber, | ||||
|         this.isActive}); | ||||
| 
 | ||||
|   UsersBasedOnSearchModel.fromJson(Map<String, dynamic> json) { | ||||
|     userId = json['userId']; | ||||
|     userName = json['userName']; | ||||
|     email = json['email']; | ||||
|     employeeId = json['employeeId']; | ||||
|     languageId = json['languageId']; | ||||
|     extensionNo = json['extensionNo']; | ||||
|     phoneNumber = json['phoneNumber']; | ||||
|     isActive = json['isActive']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['userId'] = this.userId; | ||||
|     data['userName'] = this.userName; | ||||
|     data['email'] = this.email; | ||||
|     data['employeeId'] = this.employeeId; | ||||
|     data['languageId'] = this.languageId; | ||||
|     data['extensionNo'] = this.extensionNo; | ||||
|     data['phoneNumber'] = this.phoneNumber; | ||||
|     data['isActive'] = this.isActive; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,132 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:test_sa/controllers/providers/api/oracle_code_provider.dart'; | ||||
| import 'package:test_sa/controllers/providers/api/parts_provider.dart'; | ||||
| import 'package:test_sa/controllers/providers/api/user_provider.dart'; | ||||
| import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; | ||||
| import 'package:test_sa/extensions/context_extension.dart'; | ||||
| import 'package:test_sa/extensions/int_extensions.dart'; | ||||
| import 'package:test_sa/extensions/widget_extensions.dart'; | ||||
| import 'package:test_sa/models/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart'; | ||||
| import 'package:test_sa/new_views/app_style/app_color.dart'; | ||||
| import 'package:test_sa/views/app_style/sizing.dart'; | ||||
| 
 | ||||
| import '../../../extensions/text_extensions.dart'; | ||||
| import '../../../models/service_request/spare_parts.dart'; | ||||
| import '../../../new_views/app_style/app_text_style.dart'; | ||||
| 
 | ||||
| class AssetAutoCompleteField extends StatefulWidget { | ||||
|   final String initialValue; | ||||
|   final num? assetId; | ||||
|   final bool clearAfterPick, byName; | ||||
|   final Function(AssetNDAutoCompleteByDynamicCodesModel) onPick; | ||||
| 
 | ||||
|   const AssetAutoCompleteField({Key? key, required this.byName, required this.initialValue, this.assetId, required this.onPick, this.clearAfterPick = true}) : super(key: key); | ||||
| 
 | ||||
|   @override | ||||
|   _AssetAutoCompleteFieldState createState() => _AssetAutoCompleteFieldState(); | ||||
| } | ||||
| 
 | ||||
| class _AssetAutoCompleteFieldState extends State<AssetAutoCompleteField> { | ||||
|   late OracleCodeProvider _oracleCodeProvider; | ||||
| 
 | ||||
|   late TextEditingController _controller; | ||||
| 
 | ||||
|   bool loading = false; | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     _controller = TextEditingController(text: widget.initialValue); | ||||
|     super.initState(); | ||||
|     _oracleCodeProvider = Provider.of<OracleCodeProvider>(context, listen: false); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void didUpdateWidget(covariant AssetAutoCompleteField oldWidget) { | ||||
|     if (widget.initialValue != oldWidget.initialValue) { | ||||
|       _controller = TextEditingController(text: widget.initialValue); | ||||
|     } | ||||
|     super.didUpdateWidget(oldWidget); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void dispose() { | ||||
|     _controller.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final border = UnderlineInputBorder(borderSide: BorderSide.none, borderRadius: BorderRadius.circular(10)); | ||||
|     return Container( | ||||
|       decoration: BoxDecoration( | ||||
|         color: AppColor.background(context), | ||||
|         borderRadius: BorderRadius.circular(AppStyle.borderRadius * AppStyle.getScaleFactor(context)), | ||||
|         //  boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)], | ||||
|       ), | ||||
|       child: Autocomplete<AssetNDAutoCompleteByDynamicCodesModel>( | ||||
|         optionsBuilder: (TextEditingValue textEditingValue) async { | ||||
|           if (textEditingValue.text.isEmpty) { | ||||
|             if (loading) { | ||||
|               setState(() { | ||||
|                 loading = false; | ||||
|               }); | ||||
|             } | ||||
|             return const Iterable<AssetNDAutoCompleteByDynamicCodesModel>.empty(); | ||||
|           } | ||||
|           if (!loading) { | ||||
|             setState(() { | ||||
|               loading = true; | ||||
|             }); | ||||
|           } | ||||
|           List<AssetNDAutoCompleteByDynamicCodesModel> workOrders = (await _oracleCodeProvider.getAssetByOracleCode(textEditingValue.text)); | ||||
|           setState(() { | ||||
|             loading = false; | ||||
|           }); | ||||
|           return workOrders; | ||||
|         }, | ||||
|         displayStringForOption: (AssetNDAutoCompleteByDynamicCodesModel option) => widget.byName ? option.displayName ?? "" : option.codeValue ?? "", | ||||
|         fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) { | ||||
|           return TextField( | ||||
|             controller: _controller, | ||||
|             focusNode: fieldFocusNode, | ||||
|             style: AppTextStyles.bodyText.copyWith(color: AppColor.black10), | ||||
|             textAlign: TextAlign.start, | ||||
|             decoration: InputDecoration( | ||||
|               border: border, | ||||
|               disabledBorder: border, | ||||
|               focusedBorder: border, | ||||
|               enabledBorder: border, | ||||
|               errorBorder: border, | ||||
|               contentPadding: EdgeInsets.symmetric(vertical: 8.toScreenHeight, horizontal: 16.toScreenWidth), | ||||
|               constraints: const BoxConstraints(), | ||||
|               suffixIconConstraints: const BoxConstraints(maxHeight: 24, maxWidth: 24 + 8), | ||||
|               filled: true, | ||||
|               fillColor: AppColor.fieldBgColor(context), | ||||
|               errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60), | ||||
|               floatingLabelStyle: AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? null : AppColor.neutral20), | ||||
|               labelText: context.translation.oracleCode, | ||||
|               labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.textColor(context)), | ||||
|               suffixIcon: loading ? const CircularProgressIndicator(color: AppColor.primary10, strokeWidth: 3.0).paddingOnly(end: 8) : null, | ||||
|             ), | ||||
|             textInputAction: TextInputAction.search, | ||||
|             onChanged: (text) { | ||||
|               fieldTextEditingController.text = text; | ||||
|             }, | ||||
|             onSubmitted: (String value) { | ||||
|               onFieldSubmitted(); | ||||
|             }, | ||||
|           ); | ||||
|         }, | ||||
|         onSelected: (AssetNDAutoCompleteByDynamicCodesModel selection) { | ||||
|           if (widget.clearAfterPick) { | ||||
|             _controller.clear(); | ||||
|           } else { | ||||
|             _controller.text = widget.byName ? (selection.displayName ?? "") : (selection.codeValue ?? ""); | ||||
|           } | ||||
|           widget.onPick(selection); | ||||
|         }, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,443 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:test_sa/extensions/context_extension.dart'; | ||||
| import 'package:test_sa/extensions/int_extensions.dart'; | ||||
| import 'package:test_sa/extensions/string_extensions.dart'; | ||||
| import 'package:test_sa/extensions/text_extensions.dart'; | ||||
| import 'package:test_sa/extensions/widget_extensions.dart'; | ||||
| import 'package:test_sa/helper/utils.dart'; | ||||
| import 'package:test_sa/models/device/asset.dart'; | ||||
| import 'package:test_sa/models/lookup.dart'; | ||||
| import 'package:test_sa/models/new_models/traf_department.dart'; | ||||
| import 'package:test_sa/models/new_models/users_based_on_search_model.dart'; | ||||
| import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; | ||||
| import 'package:test_sa/modules/traf_module/asset_auto_complete_field.dart'; | ||||
| import 'package:test_sa/modules/traf_module/traf_request_detail_page.dart'; | ||||
| import 'package:test_sa/modules/traf_module/update_traf_request_page.dart'; | ||||
| import 'package:test_sa/modules/traf_module/users_auto_complete_field.dart'; | ||||
| import 'package:test_sa/new_views/app_style/app_color.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/multiple_item_drop_down_menu.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; | ||||
| import 'package:test_sa/providers/loading_list_notifier.dart'; | ||||
| import 'package:test_sa/providers/lookups/department_lookup_provider.dart'; | ||||
| import 'package:test_sa/providers/lookups/request_type_lookup_provider.dart'; | ||||
| import 'package:test_sa/providers/lookups/yes_no_lookup_provider.dart'; | ||||
| import 'package:test_sa/views/widgets/equipment/asset_picker.dart'; | ||||
| 
 | ||||
| import 'traf_request_model.dart'; | ||||
| import 'traf_request_provider.dart'; | ||||
| 
 | ||||
| class CreateTRAFRequestPage extends StatefulWidget { | ||||
|   static const String id = "/create-TRAF"; | ||||
| 
 | ||||
|   CreateTRAFRequestPage({Key? key}) : super(key: key); | ||||
| 
 | ||||
|   @override | ||||
|   _CreateTRAFRequestPageState createState() { | ||||
|     return _CreateTRAFRequestPageState(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class _CreateTRAFRequestPageState extends State<CreateTRAFRequestPage> { | ||||
|   final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); | ||||
| 
 | ||||
|   bool _acknowledgement = false; | ||||
|   Asset? asset; | ||||
| 
 | ||||
|   Lookup? requestType; | ||||
|   Lookup? isUsedSolelyOrShared; | ||||
|   Lookup? otherServicesEffects; | ||||
|   Lookup? useInCombination; | ||||
| 
 | ||||
|   TrafRequestDataModel? trafRequest; | ||||
| 
 | ||||
|   List<Lookup> abc = []; | ||||
| 
 | ||||
|   List<TrafDepartment> departments = []; | ||||
|   List<UsersBasedOnSearchModel> userBasedOnSearch = []; | ||||
| 
 | ||||
|   FocusNode otherServicesEffectsNode = FocusNode(); | ||||
|   FocusNode useInCombinationNode = FocusNode(); | ||||
|   FocusNode departmentNode = FocusNode(); | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     trafRequest = TrafRequestDataModel(id: 0, attachments: [], departments: [], qty: 1); | ||||
|     resetProviders(); | ||||
|   } | ||||
| 
 | ||||
|   void resetProviders() { | ||||
|     Provider.of<RequestTypeLookupProvider>(context, listen: false).reset(); | ||||
|     // Provider.of<YesNoLookupProvider>(context, listen: false).reset(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void dispose() { | ||||
|     super.dispose(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       appBar: const DefaultAppBar(title: "TRAF Request"), | ||||
|       body: Form( | ||||
|         key: _formKey, | ||||
|         child: Column( | ||||
|           children: [ | ||||
|             ListView(padding: const EdgeInsets.all(16), children: [ | ||||
|               Column( | ||||
|                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                 children: [ | ||||
|                   // SingleItemDropDownMenu<TaskType, TaskTypeProvider>( | ||||
|                   //   context: context, | ||||
|                   //   height: 56.toScreenHeight, | ||||
|                   //   title: context.translation.taskType, | ||||
|                   //   showShadow: false, | ||||
|                   //   backgroundColor: AppColor.fieldBgColor(context), | ||||
|                   //   showAsBottomSheet: true, | ||||
|                   //   initialValue: selectedType, | ||||
|                   //   onSelect: (type) {}, | ||||
|                   // ), | ||||
|                   SingleItemDropDownMenu<Lookup, RequestTypeLookupProvider>( | ||||
|                     context: context, | ||||
|                     showAsBottomSheet: true, | ||||
|                     backgroundColor: AppColor.neutral100, | ||||
|                     showShadow: false, | ||||
|                     title: context.translation.requestType, | ||||
|                     initialValue: requestType, | ||||
|                     onSelect: (value) { | ||||
|                       requestType = value; | ||||
|                       trafRequest?.requestTypeId = value?.id; | ||||
|                       if (requestType?.value == 1) { | ||||
|                         trafRequest?.assetId = null; | ||||
|                         // trafRequest?.qty = 0; | ||||
|                       } else if (requestType?.value == 2) { | ||||
|                         trafRequest?.assetNDId = null; | ||||
|                       } | ||||
|                       setState(() {}); | ||||
|                     }, | ||||
|                   ), | ||||
|                   if (requestType?.value == 1) ...[ | ||||
|                     12.height, | ||||
|                     AssetAutoCompleteField( | ||||
|                       clearAfterPick: false, | ||||
|                       byName: true, | ||||
|                       initialValue: "", | ||||
|                       onPick: (asset) { | ||||
|                         trafRequest?.assetNDId = asset.id; | ||||
|                         // model.partCatalogItem = PartCatalogItem(id: part.sparePart?.id, partNumber: part.sparePart?.partNo, partName: part.sparePart?.partName, oracleCode: part.sparePart?.oracleCode); | ||||
|                         // setState(() {}); | ||||
|                       }, | ||||
|                     ), | ||||
|                   ], | ||||
|                   if (requestType?.value == 2) ...[ | ||||
|                     12.height, | ||||
|                     AssetPicker( | ||||
|                       device: asset, | ||||
|                       editable: false, | ||||
|                       showLoading: false, | ||||
|                       borderColor: AppColor.black20, | ||||
|                       backgroundColor: AppColor.white936, | ||||
|                       onPick: (asset) async { | ||||
|                         this.asset = asset; | ||||
|                         trafRequest?.assetId = asset.id?.toInt(); | ||||
|                         // trafRequest?.qty = 1; | ||||
|                         setState(() {}); | ||||
|                         // pendingAssetServiceRequest = null; | ||||
|                         // _serviceRequest.device = asset; | ||||
|                         // await checkAssetForPendingServiceRequest(asset.id!.toInt()); | ||||
|                         // if (pendingAssetServiceRequest != null && pendingAssetServiceRequest!.details!.isNotEmpty) { | ||||
|                         //   showPendingRequestBottomSheet(); | ||||
|                         // } | ||||
|                       }, | ||||
|                     ), | ||||
|                   ], | ||||
|                   12.height, | ||||
|                   Text( | ||||
|                     "Request Details", | ||||
|                     style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, fontWeight: FontWeight.w600), | ||||
|                   ), | ||||
|                   12.height, | ||||
|                   AppTextFormField( | ||||
|                     initialValue: "", | ||||
|                     labelText: "How would the requested technology solve the current situation and/or serve the purpose?", | ||||
|                     backgroundColor: AppColor.fieldBgColor(context), | ||||
|                     labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), | ||||
|                     floatingLabelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), | ||||
|                     showShadow: false, | ||||
|                     makeMultiLinesNull: true, | ||||
|                     onChange: (value) { | ||||
|                       trafRequest?.purposeAnswer = value; | ||||
|                     }, | ||||
|                   ), | ||||
|                   12.height, | ||||
|                   AppTextFormField( | ||||
|                     initialValue: "", | ||||
|                     labelText: "What is the current practice?", | ||||
|                     backgroundColor: AppColor.fieldBgColor(context), | ||||
|                     labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), | ||||
|                     showShadow: false, | ||||
|                     makeMultiLinesNull: true, | ||||
|                     onChange: (value) { | ||||
|                       trafRequest?.currentPractise = value; | ||||
|                     }, | ||||
|                   ), | ||||
|                   12.height, | ||||
|                   AppTextFormField( | ||||
|                     initialValue: "", | ||||
|                     makeMultiLinesNull: true, | ||||
|                     labelText: "Census Q1", | ||||
|                     backgroundColor: AppColor.fieldBgColor(context), | ||||
|                     labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), | ||||
|                     showShadow: false, | ||||
|                     textInputType: TextInputType.number, | ||||
|                     onChange: (value) { | ||||
|                       trafRequest?.censusQ1 = int.tryParse(value); | ||||
|                     }, | ||||
|                   ), | ||||
|                   12.height, | ||||
|                   AppTextFormField( | ||||
|                     initialValue: "", | ||||
|                     makeMultiLinesNull: true, | ||||
|                     labelText: "Census Q2", | ||||
|                     backgroundColor: AppColor.fieldBgColor(context), | ||||
|                     labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), | ||||
|                     showShadow: false, | ||||
|                     textInputType: TextInputType.number, | ||||
|                     onChange: (value) { | ||||
|                       trafRequest?.censusQ2 = int.tryParse(value); | ||||
|                     }, | ||||
|                   ), | ||||
|                   12.height, | ||||
|                   AppTextFormField( | ||||
|                     initialValue: "", | ||||
|                     makeMultiLinesNull: true, | ||||
|                     labelText: "Census Q3", | ||||
|                     backgroundColor: AppColor.fieldBgColor(context), | ||||
|                     labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), | ||||
|                     showShadow: false, | ||||
|                     textInputType: TextInputType.number, | ||||
|                     onChange: (value) { | ||||
|                       trafRequest?.censusQ3 = int.tryParse(value); | ||||
|                     }, | ||||
|                   ), | ||||
|                   12.height, | ||||
|                   AppTextFormField( | ||||
|                     initialValue: "", | ||||
|                     makeMultiLinesNull: true, | ||||
|                     labelText: "Census Q4", | ||||
|                     backgroundColor: AppColor.fieldBgColor(context), | ||||
|                     labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), | ||||
|                     showShadow: false, | ||||
|                     textInputType: TextInputType.number, | ||||
|                     onChange: (value) { | ||||
|                       trafRequest?.censusQ4 = int.tryParse(value); | ||||
|                     }, | ||||
|                   ), | ||||
|                   12.height, | ||||
|                   SearchUserAutoCompleteField( | ||||
|                     clearAfterPick: true, | ||||
|                     byName: true, | ||||
|                     initialValue: "", | ||||
|                     onPick: (user) { | ||||
|                       userBasedOnSearch.add(user); | ||||
|                       FocusScope.of(context).unfocus(); | ||||
|                       // model.partCatalogItem = PartCatalogItem(id: part.sparePart?.id, partNumber: part.sparePart?.partNo, partName: part.sparePart?.partName, oracleCode: part.sparePart?.oracleCode); | ||||
|                       setState(() {}); | ||||
|                     }, | ||||
|                   ), | ||||
|                   if (userBasedOnSearch.isNotEmpty) ...[ | ||||
|                     // 12.height, | ||||
|                     ListView.separated( | ||||
|                         shrinkWrap: true, | ||||
|                         physics: const NeverScrollableScrollPhysics(), | ||||
|                         padding: const EdgeInsets.only(left: 12, right: 12), | ||||
|                         itemBuilder: (cxt, index) => Column( | ||||
|                               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                               mainAxisSize: MainAxisSize.min, | ||||
|                               children: [ | ||||
|                                 ListTile( | ||||
|                                   dense: true, | ||||
|                                   contentPadding: EdgeInsets.zero, | ||||
|                                   title: Text("Name: ${userBasedOnSearch[index].userName ?? ""}"), | ||||
|                                   subtitle: Text("Email: ${userBasedOnSearch[index].email ?? ""}\nPhone: ${userBasedOnSearch[index].phoneNumber ?? ""}"), | ||||
|                                   trailing: const Icon(Icons.delete_rounded, color: Color(0xffF63939), size: 20).onPress(() { | ||||
|                                     userBasedOnSearch.removeAt(index); | ||||
|                                     setState(() {}); | ||||
|                                   }), | ||||
|                                 ), | ||||
|                               ], | ||||
|                             ), | ||||
|                         separatorBuilder: (cxt, index) => Divider(thickness: 1, height: 1, color: context.isDark ? AppColor.neutral20 : AppColor.neutral30), | ||||
|                         itemCount: userBasedOnSearch.length) | ||||
|                   ], | ||||
|                   12.height, | ||||
|                   SingleItemDropDownMenu<Lookup, YesNoLookupProvider>( | ||||
|                     context: context, | ||||
|                     showAsBottomSheet: true, | ||||
|                     backgroundColor: AppColor.neutral100, | ||||
|                     showShadow: false, | ||||
|                     height: 80, | ||||
|                     title: "Is the requesting department going to use the technology solely or shared with other departments?", | ||||
|                     initialValue: isUsedSolelyOrShared, | ||||
|                     onSelect: (value) { | ||||
|                       isUsedSolelyOrShared = value; | ||||
|                       trafRequest?.usingSolelyOrSharedId = value?.value; | ||||
|                       if (isUsedSolelyOrShared?.value != 1) { | ||||
|                         departments = []; | ||||
|                         trafRequest?.trafDepartments = []; | ||||
|                         Provider.of<DepartmentLookupProvider>(context, listen: false).reset(); | ||||
|                         FocusScope.of(context).unfocus(); | ||||
|                       } else { | ||||
|                         departmentNode.requestFocus(); | ||||
|                       } | ||||
|                       setState(() {}); | ||||
|                     }, | ||||
|                   ), | ||||
|                   if (isUsedSolelyOrShared?.value == 1) ...[ | ||||
|                     12.height, | ||||
|                     Focus( | ||||
|                       focusNode: departmentNode, | ||||
|                       child: MultipleItemDropDownMenu<TrafDepartment, DepartmentLookupProvider>( | ||||
|                         context: context, | ||||
|                         showAsBottomSheet: true, | ||||
|                         backgroundColor: AppColor.neutral100, | ||||
|                         showShadow: false, | ||||
|                         showCancel: true, | ||||
|                         requestById: context.userProvider.user?.clientId, | ||||
|                         title: "Please specify departments and relations", | ||||
|                         initialValue: departments, | ||||
|                         onSelect: (value) { | ||||
|                           departments = value ?? []; | ||||
|                           trafRequest?.trafDepartments = departments.map((element) => Departments(id: 0, trafId: 0, departmentId: element.id!.toInt())).toList(); | ||||
|                         }, | ||||
|                       ), | ||||
|                     ), | ||||
|                   ], | ||||
|                   12.height, | ||||
|                   SingleItemDropDownMenu<Lookup, YesNoLookupProvider>( | ||||
|                     context: context, | ||||
|                     showAsBottomSheet: true, | ||||
|                     backgroundColor: AppColor.neutral100, | ||||
|                     showShadow: false, | ||||
|                     height: 80, | ||||
|                     title: "Would other services be effected by acquiring the new equipment?", | ||||
|                     initialValue: otherServicesEffects, | ||||
|                     onSelect: (value) { | ||||
|                       otherServicesEffects = value; | ||||
|                       trafRequest?.isEffectedId = value?.value; | ||||
|                       if (otherServicesEffects?.value != 1) { | ||||
|                         trafRequest?.effectedServices = null; | ||||
|                         FocusScope.of(context).unfocus(); | ||||
|                       } else { | ||||
|                         otherServicesEffectsNode.requestFocus(); | ||||
|                       } | ||||
|                       setState(() {}); | ||||
|                     }, | ||||
|                   ), | ||||
|                   if (otherServicesEffects?.value == 1) ...[ | ||||
|                     12.height, | ||||
|                     AppTextFormField( | ||||
|                       initialValue: "", | ||||
|                       // makeMultiLinesNull: true, | ||||
|                       textInputType: TextInputType.multiline, | ||||
|                       alignLabelWithHint: true, | ||||
|                       node: otherServicesEffectsNode, | ||||
|                       labelText: "List down these services and stat how would it be effected", | ||||
|                       backgroundColor: AppColor.fieldBgColor(context), | ||||
|                       labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), | ||||
|                       showShadow: false, | ||||
|                       onChange: (value) { | ||||
|                         trafRequest?.effectedServices = value; | ||||
|                       }, | ||||
|                     ), | ||||
|                   ], | ||||
|                   12.height, | ||||
|                   SingleItemDropDownMenu<Lookup, YesNoLookupProvider>( | ||||
|                     context: context, | ||||
|                     showAsBottomSheet: true, | ||||
|                     backgroundColor: AppColor.neutral100, | ||||
|                     showShadow: false, | ||||
|                     height: 80, | ||||
|                     title: "Is the equipment going to be used with combination of other equipment to accomplish a specific procedure?", | ||||
|                     initialValue: useInCombination, | ||||
|                     onSelect: (value) { | ||||
|                       useInCombination = value; | ||||
|                       trafRequest?.isCombinationId = value?.value; | ||||
|                       if (useInCombination?.value != 1) { | ||||
|                         trafRequest?.usedWithCombination = null; | ||||
|                         FocusScope.of(context).unfocus(); | ||||
|                       } else { | ||||
|                         useInCombinationNode.requestFocus(); | ||||
|                       } | ||||
|                       setState(() {}); | ||||
|                     }, | ||||
|                   ), | ||||
|                   if (useInCombination?.value == 1) ...[ | ||||
|                     12.height, | ||||
|                     AppTextFormField( | ||||
|                       initialValue: "", | ||||
|                       textInputType: TextInputType.multiline, | ||||
|                       alignLabelWithHint: true, | ||||
|                       node: useInCombinationNode, | ||||
|                       labelText: "kindly describe in detail", | ||||
|                       backgroundColor: AppColor.fieldBgColor(context), | ||||
|                       labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), | ||||
|                       showShadow: false, | ||||
|                       onChange: (value) { | ||||
|                         trafRequest?.usedWithCombination = value; | ||||
|                       }, | ||||
|                     ), | ||||
|                   ] | ||||
|                 ], | ||||
|               ).toShadowContainer(context, padding: 12, borderRadius: 20), | ||||
|               16.height, | ||||
|               Row( | ||||
|                 children: [ | ||||
|                   Checkbox( | ||||
|                       value: _acknowledgement, | ||||
|                       visualDensity: const VisualDensity(horizontal: -4.0, vertical: -4.0), | ||||
|                       activeColor: AppColor.blueStatus(context), | ||||
|                       materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, | ||||
|                       onChanged: (value) { | ||||
|                         setState(() { | ||||
|                           _acknowledgement = value!; | ||||
|                         }); | ||||
|                       }), | ||||
|                   12.width, | ||||
|                   "I acknowledge the information filled above is correct".addTranslation.bodyText(context).custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral120).expanded, | ||||
|                 ], | ||||
|               ), | ||||
|             ]).expanded, | ||||
|             FooterActionButton.footerContainer( | ||||
|               context: context, | ||||
|               child: AppFilledButton( | ||||
|                 buttonColor: AppColor.primary10, | ||||
|                 label: context.translation.submitRequest, | ||||
|                 disableButton: !_acknowledgement, | ||||
|                 onPressed: _verifyAndSubmit, | ||||
|                 // buttonColor: AppColor.primary10, | ||||
|               ), | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   void _verifyAndSubmit() async { | ||||
|     trafRequest?.employeeId = context.userProvider.user?.userID; | ||||
|     trafRequest?.siteId = context.userProvider.user?.clientId; | ||||
|     trafRequest?.trafContacts = []; | ||||
|     trafRequest?.trafContacts = userBasedOnSearch.map((item) => TrafContacts(id: 0, trafId: 0, name: item.userName, userId: item.userId)).toList(); | ||||
| 
 | ||||
|     Utils.showLoading(context); | ||||
|     await Provider.of<TrafRequestProvider>(context, listen: false).addTraf(trafRequest!.toJson()); | ||||
|     Utils.hideLoading(context); | ||||
|     Navigator.pop(context); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,166 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:test_sa/controllers/providers/api/user_provider.dart'; | ||||
| import 'package:test_sa/extensions/context_extension.dart'; | ||||
| import 'package:test_sa/extensions/int_extensions.dart'; | ||||
| import 'package:test_sa/extensions/text_extensions.dart'; | ||||
| import 'package:test_sa/extensions/widget_extensions.dart'; | ||||
| import 'package:test_sa/models/enums/user_types.dart'; | ||||
| import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; | ||||
| import 'package:test_sa/modules/traf_module/traf_request_provider.dart'; | ||||
| import 'package:test_sa/modules/traf_module/update_traf_request_page.dart'; | ||||
| import 'package:test_sa/new_views/app_style/app_color.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; | ||||
| import 'package:test_sa/views/widgets/loaders/app_loading.dart'; | ||||
| 
 | ||||
| import 'traf_request_model.dart'; | ||||
| 
 | ||||
| class TrafRequestDetailPage extends StatefulWidget { | ||||
|   static const String id = "/details-TRAF"; | ||||
| 
 | ||||
|   final int trafId; | ||||
| 
 | ||||
|   TrafRequestDetailPage({Key? key, required this.trafId}) : super(key: key); | ||||
| 
 | ||||
|   @override | ||||
|   _TrafRequestDetailPageState createState() { | ||||
|     return _TrafRequestDetailPageState(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class _TrafRequestDetailPageState extends State<TrafRequestDetailPage> { | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     Provider.of<TrafRequestProvider>(context, listen: false).getTRAFById(widget.trafId); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void dispose() { | ||||
|     super.dispose(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     bool isEngineer = (Provider.of<UserProvider>(context, listen: false).user?.type) == UsersTypes.engineer; | ||||
|     return Scaffold( | ||||
|         appBar: const DefaultAppBar(title: "TRAF Request"), | ||||
|         body: Selector<TrafRequestProvider, bool>( | ||||
|           selector: (_, myModel) => myModel.isLoading, // Selects only the userName | ||||
|           builder: (_, isLoading, __) { | ||||
|             if (isLoading) return const ALoading(); | ||||
|             TrafRequestProvider trafProvider = Provider.of<TrafRequestProvider>(context, listen: false); | ||||
|             return Column( | ||||
|               children: [ | ||||
|                 ListView( | ||||
|                   padding: const EdgeInsets.all(16), | ||||
|                   children: [ | ||||
|                     requesterInformation(trafProvider.trafRequestDataModel!), | ||||
|                     12.height, | ||||
|                     requestInformation(trafProvider.trafRequestDataModel!), | ||||
|                     12.height, | ||||
|                     assetInformation(trafProvider.trafRequestDataModel!), | ||||
|                   ], | ||||
|                 ).expanded, | ||||
|                 // // if (isEngineer) | ||||
|                 // FooterActionButton.footerContainer( | ||||
|                 //   context: context, | ||||
|                 //   child: AppFilledButton( | ||||
|                 //       buttonColor: AppColor.primary10, | ||||
|                 //       label: "Update", | ||||
|                 //       onPressed: () { | ||||
|                 //         Navigator.pushNamed(context, UpdateTrafRequestPage.id); | ||||
|                 //       }), | ||||
|                 // ), | ||||
|               ], | ||||
|             ); | ||||
|           }, | ||||
|         )); | ||||
|   } | ||||
| 
 | ||||
|   Widget requesterInformation(TrafRequestDataModel data) { | ||||
|     return Column( | ||||
|       mainAxisSize: MainAxisSize.min, | ||||
|       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|       children: [ | ||||
|         Text( | ||||
|           "Requester Information", | ||||
|           style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), | ||||
|         ), | ||||
|         8.height, | ||||
|         '${context.translation.employeeId}: ${data.employeeIdForDisplay ?? '-'}'.bodyText(context), | ||||
|         '${context.translation.name}: ${data.employeeName ?? '-'}'.bodyText(context), // todo ask shaheer | ||||
|         '${context.translation.email}: ${data.employeeEmail ?? '-'}'.bodyText(context), // todo ask shaheer | ||||
|         'Position: ${data.positionName ?? '-'}'.bodyText(context), // todo ask shaheer | ||||
|         '${context.translation.site}: ${data.siteName ?? '-'}'.bodyText(context), | ||||
|         '${context.translation.department}: ${data.departments?.map((item) => item.departmentName).toList() ?? '-'}'.bodyText(context), | ||||
|         'Requester Extension: ${data.requesterExtensionName ?? '-'}'.bodyText(context), | ||||
|         '${context.translation.extensionNo}: ${data.requesterExtensionNumber ?? '-'}'.bodyText(context), | ||||
|       ], | ||||
|     ).toShadowContainer(context, borderRadius: 20, padding: 12); | ||||
|   } | ||||
| 
 | ||||
|   Widget requestInformation(TrafRequestDataModel data) { | ||||
|     return Column( | ||||
|       mainAxisSize: MainAxisSize.min, | ||||
|       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|       children: [ | ||||
|         Text( | ||||
|           "Request Information", | ||||
|           style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), | ||||
|         ), | ||||
|         8.height, | ||||
|         'TRAF No: ${data.reqCode ?? '-'}'.bodyText(context), | ||||
|         'Request type: ${data.requestTypeName ?? '-'}'.bodyText(context), | ||||
|         4.height, | ||||
|         'How would the requested technology solve the current situation and/or serve the purpose?: ${data.purposeAnswer ?? '-'}'.bodyText(context), | ||||
|         4.height, | ||||
|         'What is the current practice?: ${data.currentPractise ?? '-'}'.bodyText(context), | ||||
|         4.height, | ||||
|         'Census Q1: ${data.censusQ1 ?? '-'}'.bodyText(context), | ||||
|         'Census Q2: ${data.censusQ2 ?? '-'}'.bodyText(context), | ||||
|         'Census Q3: ${data.censusQ3 ?? '-'}'.bodyText(context), | ||||
|         'Census Q4: ${data.censusQ4 ?? '-'}'.bodyText(context), | ||||
|         4.height, | ||||
|         'List down names & contact information of users going to use the technology & specify if they are part-time or full time?:\n${data.trafContacts?.map((item) => item.name).toList() ?? '-'}' | ||||
|             .bodyText(context), | ||||
|         4.height, | ||||
|         'Is the requesting department going to use the technology solely or shared with other departments?:\n${data.usingSolelyOrSharedName ?? '-'}'.bodyText(context), | ||||
|         4.height, | ||||
|         'Would other services be effected by acquiring the new equipment?:\n${data.effectedServices ?? '-'}'.bodyText(context), | ||||
|         4.height, | ||||
|         'Is the equipment going to be used with combination of other equipment to accomplish a specific procedure?:\n${data.usedWithCombination ?? '-'}'.bodyText(context), | ||||
|       ], | ||||
|     ).toShadowContainer(context, borderRadius: 20, padding: 12); | ||||
|   } | ||||
| 
 | ||||
|   Widget assetInformation(TrafRequestDataModel data) { | ||||
|     return Column( | ||||
|       mainAxisSize: MainAxisSize.min, | ||||
|       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|       children: [ | ||||
|         Text( | ||||
|           "Asset Information", | ||||
|           style: AppTextStyles.heading4.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), | ||||
|         ), | ||||
|         8.height, | ||||
|         '${context.translation.assetName}: ${data.assetName ?? '-'}'.bodyText(context), | ||||
|         '${context.translation.model}: ${data.modelName ?? '-'}'.bodyText(context), | ||||
|         '${context.translation.manufacture}: ${data.manufacturerName ?? '-'}'.bodyText(context), | ||||
|         if (data.requestTypeId == 733) ...[ | ||||
|           'Last price & PO: ${data.poNumber ?? '-'}'.bodyText(context), | ||||
|           'The quantity of the same asset: ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer | ||||
|           'Existing asset under SLA: ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer | ||||
|           'Age of the asset: ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer | ||||
|           'Total Maintenance Cost (TMC): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer | ||||
|           'Net Book Value (NBV): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer | ||||
|           'Mean Time Between Failure (MTBF): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer | ||||
|           'Down Time (DT): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer | ||||
|           'Up Time (UT): ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer | ||||
|           'Purchased Price: ${data.qty ?? '-'}'.bodyText(context), // todo check with shaheer | ||||
|         ], | ||||
|       ], | ||||
|     ).toShadowContainer(context, borderRadius: 20, padding: 12); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,125 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:test_sa/extensions/context_extension.dart'; | ||||
| import 'package:test_sa/extensions/int_extensions.dart'; | ||||
| import 'package:test_sa/extensions/string_extensions.dart'; | ||||
| import 'package:test_sa/extensions/text_extensions.dart'; | ||||
| import 'package:test_sa/extensions/widget_extensions.dart'; | ||||
| import 'package:test_sa/models/all_requests_and_count_model.dart'; | ||||
| import 'package:test_sa/models/new_models/dashboard_detail.dart'; | ||||
| import 'package:test_sa/new_views/app_style/app_color.dart'; | ||||
| import 'package:test_sa/views/widgets/requests/request_status.dart'; | ||||
| 
 | ||||
| import 'traf_request_detail_page.dart'; | ||||
| 
 | ||||
| class TrafRequestItemView extends StatelessWidget { | ||||
|   final Data? requestData; | ||||
|   final RequestsDetails? requestDetails; | ||||
|   final bool showShadow; | ||||
| 
 | ||||
|   const TrafRequestItemView({Key? key, this.requestData, this.requestDetails, this.showShadow = true}) : super(key: key); | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     if (requestData != null) { | ||||
|       return Column( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           Row( | ||||
|             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|             children: [ | ||||
|               // StatusLabel( | ||||
|               //   label: requestData!.priorityName!, | ||||
|               //   textColor: AppColor.getRequestStatusTextColorByName(context, requestData!.priorityName!), | ||||
|               //   backgroundColor: AppColor.getRequestStatusColorByName(context, requestData!.priorityName!), | ||||
|               // ), | ||||
|               // 8.width, | ||||
|               StatusLabel( | ||||
|                 label: requestData!.statusName!, | ||||
|                 textColor: AppColor.getRequestStatusTextColorByName(context, requestData!.statusName!), | ||||
|                 backgroundColor: AppColor.getRequestStatusColorByName(context, requestData!.statusName!), | ||||
|               ), | ||||
|               1.width.expanded, | ||||
|               Text( | ||||
|                 requestData!.transactionDate?.toServiceRequestCardFormat ?? "", | ||||
|                 textAlign: TextAlign.end, | ||||
|                 style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|           8.height, | ||||
|           (requestData?.typeTransaction ?? "TRAF Request").heading5(context), | ||||
|           infoWidget(label: context.translation.requestType, value: requestData?.requestTypeName, context: context), | ||||
|           infoWidget(label: "TRAF No", value: requestData?.requestNo, context: context), | ||||
|           8.height, | ||||
|           Row( | ||||
|             mainAxisSize: MainAxisSize.min, | ||||
|             children: [ | ||||
|               Text( | ||||
|                 context.translation.viewDetails, | ||||
|                 style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)), | ||||
|               ), | ||||
|               4.width, | ||||
|               Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14) | ||||
|             ], | ||||
|           ), | ||||
|         ], | ||||
|       ).toShadowContainer(context, withShadow: showShadow).onPress(() async { | ||||
|         Navigator.push(context, MaterialPageRoute(builder: (context) => TrafRequestDetailPage(trafId: requestData!.id!))); | ||||
|       }); | ||||
|     } | ||||
|     return Column( | ||||
|       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|       children: [ | ||||
|         Row( | ||||
|           crossAxisAlignment: CrossAxisAlignment.start, | ||||
|           children: [ | ||||
|             // StatusLabel( | ||||
|             //   label: requestDetails!.priority!, | ||||
|             //   textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.priority!), | ||||
|             //   backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails?.priority!), | ||||
|             // ), | ||||
|             // 8.width, | ||||
|             StatusLabel( | ||||
|               label: requestDetails!.status!, | ||||
|               textColor: AppColor.getRequestStatusTextColorByName(context, requestDetails?.status!), | ||||
|               backgroundColor: AppColor.getRequestStatusColorByName(context, requestDetails?.status!), | ||||
|             ), | ||||
|             1.width.expanded, | ||||
|             Text( | ||||
|               requestDetails!.date?.toServiceRequestCardFormat ?? "", | ||||
|               textAlign: TextAlign.end, | ||||
|               style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50), | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|         8.height, | ||||
|         (requestDetails?.nameOfType ?? "TRAF Request").heading5(context), | ||||
|         8.height, | ||||
|         infoWidget(label: context.translation.requestType, value: requestDetails!.requestType, context: context), | ||||
|         infoWidget(label: "TRAF No", value: requestDetails!.requestNo, context: context), | ||||
|         infoWidget(label: context.translation.site, value: requestDetails!.site, context: context), | ||||
|         8.height, | ||||
|         Row( | ||||
|           mainAxisSize: MainAxisSize.min, | ||||
|           children: [ | ||||
|             Text( | ||||
|               context.translation.viewDetails, | ||||
|               style: AppTextStyles.bodyText.copyWith(color: AppColor.blueStatus(context)), | ||||
|             ), | ||||
|             4.width, | ||||
|             Icon(Icons.arrow_forward, color: AppColor.blueStatus(context), size: 14) | ||||
|           ], | ||||
|         ), | ||||
|       ], | ||||
|     ).toShadowContainer(context, withShadow: showShadow).onPress(() async { | ||||
|       Navigator.push(context, MaterialPageRoute(builder: (context) => TrafRequestDetailPage(trafId: requestDetails!.id!))); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   Widget infoWidget({required String label, String? value, required BuildContext context}) { | ||||
|     if (value != null && value.isNotEmpty) { | ||||
|       return '$label:  $value'.bodyText(context); | ||||
|     } | ||||
|     return const SizedBox(); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,574 @@ | ||||
| class TrafRequestModel { | ||||
|   List<TrafRequestDataModel>? trafRequestDataModel; | ||||
|   int? totalRows; | ||||
|   int? count; | ||||
|   String? message; | ||||
|   String? title; | ||||
|   String? innerMessage; | ||||
|   int? responseCode; | ||||
|   bool? isSuccess; | ||||
| 
 | ||||
|   TrafRequestModel({this.trafRequestDataModel, this.totalRows, this.count, this.message, this.title, this.innerMessage, this.responseCode, this.isSuccess}); | ||||
| 
 | ||||
|   TrafRequestModel.fromJson(Map<String, dynamic> json) { | ||||
|     if (json['TrafRequestDataModel'] != null) { | ||||
|       trafRequestDataModel = <TrafRequestDataModel>[]; | ||||
|       json['TrafRequestDataModel'].forEach((v) { | ||||
|         trafRequestDataModel!.add(new TrafRequestDataModel.fromJson(v)); | ||||
|       }); | ||||
|     } | ||||
|     totalRows = json['totalRows']; | ||||
|     count = json['count']; | ||||
|     message = json['message']; | ||||
|     title = json['title']; | ||||
|     innerMessage = json['innerMessage']; | ||||
|     responseCode = json['responseCode']; | ||||
|     isSuccess = json['isSuccess']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     if (this.trafRequestDataModel != null) { | ||||
|       data['TrafRequestDataModel'] = this.trafRequestDataModel!.map((v) => v.toJson()).toList(); | ||||
|     } | ||||
|     data['totalRows'] = this.totalRows; | ||||
|     data['count'] = this.count; | ||||
|     data['message'] = this.message; | ||||
|     data['title'] = this.title; | ||||
|     data['innerMessage'] = this.innerMessage; | ||||
|     data['responseCode'] = this.responseCode; | ||||
|     data['isSuccess'] = this.isSuccess; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class TrafRequestDataModel { | ||||
|   int? reqNo; | ||||
|   String? reqCode; | ||||
|   String? employeeId; | ||||
|   String? employeeIdForDisplay; | ||||
|   String? employeeName; | ||||
|   String? employeeEmail; | ||||
|   int? positionId; | ||||
|   String? positionName; | ||||
|   List<Departments>? departments; | ||||
|   List<Departments>? trafDepartments; | ||||
|   int? siteId; | ||||
|   String? siteName; | ||||
|   String? requesterExtensionNumber; | ||||
|   String? requesterExtensionName; | ||||
|   int? requesterExtensionPositionId; | ||||
|   String? requesterExtensionPositionName; | ||||
|   int? requestTypeId; | ||||
|   String? requestTypeName; | ||||
|   int? assetNDId; | ||||
|   int? modelId; | ||||
|   String? modelName; | ||||
|   int? manufacturerId; | ||||
|   String? manufacturerName; | ||||
|   int? assetId; | ||||
|   String? assetSerialNo; | ||||
|   String? assetNumber; | ||||
|   String? assetName; | ||||
|   int? assetAssetNDId; | ||||
|   int? qty; | ||||
|   String? purposeAnswer; | ||||
|   String? currentPractise; | ||||
|   int? censusQ1; | ||||
|   int? censusQ2; | ||||
|   int? censusQ3; | ||||
|   int? censusQ4; | ||||
|   List<TrafContacts>? trafContacts; | ||||
|   int? usingSolelyOrSharedId; | ||||
|   String? usingSolelyOrSharedName; | ||||
|   int? isEffectedId; | ||||
|   String? isEffectedName; | ||||
|   String? effectedServices; | ||||
|   int? isCombinationId; | ||||
|   String? isCombinationName; | ||||
|   String? usedWithCombination; | ||||
|   String? comment; | ||||
|   List<Attachments>? attachments; | ||||
|   int? isBudgetId; | ||||
|   String? isBudgetName; | ||||
|   String? firstLineManagerId; | ||||
|   String? firstLineManagerName; | ||||
|   int? firstLineManagerApprovalId; | ||||
|   String? firstLineManagerApprovalName; | ||||
|   int? tlapiId; | ||||
|   String? tlapiName; | ||||
|   String? assessorEmployeeId; | ||||
|   String? assessorEmployeeName; | ||||
|   String? secondLineManagerId; | ||||
|   String? secondLineManagerName; | ||||
|   int? secondLineManagerApprovalId; | ||||
|   String? secondLineManagerApprovalName; | ||||
|   String? hospitalManagementId; | ||||
|   String? hospitalManagementName; | ||||
|   int? hospitalManagementApprovalId; | ||||
|   String? hospitalManagementApprovalName; | ||||
|   List<OracleCodes>? oracleCodes; | ||||
|   String? assessorTeamLeaderId; | ||||
|   String? assessorTeamLeaderName; | ||||
|   int? assessorTeamLeaderApprovalId; | ||||
|   String? assessorTeamLeaderApprovalName; | ||||
|   int? statusOfDRId; | ||||
|   String? statusOfDRName; | ||||
|   int? statusOfRequesterId; | ||||
|   String? statusOfRequesterName; | ||||
|   bool? approved; | ||||
|   String? approvalUserId; | ||||
|   String? approvalUserName; | ||||
|   String? approvalDate; | ||||
|   String? docDate; | ||||
|   String? approvalDocDate; | ||||
|   String? isAssigned; | ||||
|   String? poNumber; | ||||
|   int? apiDirectorId; | ||||
|   String? apiDirectorName; | ||||
|   int? apiDirectorApprovalId; | ||||
|   String? apiDirectorApprovalName; | ||||
|   List<int>? trafAssessIds; | ||||
|   List<int>? trafPRIds; | ||||
|   List<int>? trafOfferIds; | ||||
|   int? id; | ||||
|   String? createdBy; | ||||
|   String? createdDate; | ||||
|   String? modifiedBy; | ||||
|   String? modifiedDate; | ||||
| 
 | ||||
|   TrafRequestDataModel( | ||||
|       {this.reqNo, | ||||
|       this.reqCode, | ||||
|       this.employeeId, | ||||
|       this.employeeIdForDisplay, | ||||
|       this.employeeName, | ||||
|       this.employeeEmail, | ||||
|       this.positionId, | ||||
|       this.positionName, | ||||
|       this.departments, | ||||
|       this.trafDepartments, | ||||
|       this.siteId, | ||||
|       this.siteName, | ||||
|       this.requesterExtensionNumber, | ||||
|       this.requesterExtensionName, | ||||
|       this.requesterExtensionPositionId, | ||||
|       this.requesterExtensionPositionName, | ||||
|       this.requestTypeId, | ||||
|       this.requestTypeName, | ||||
|       this.assetNDId, | ||||
|       this.modelId, | ||||
|       this.modelName, | ||||
|       this.manufacturerId, | ||||
|       this.manufacturerName, | ||||
|       this.assetId, | ||||
|       this.assetSerialNo, | ||||
|       this.assetNumber, | ||||
|       this.assetName, | ||||
|       this.assetAssetNDId, | ||||
|       this.qty, | ||||
|       this.purposeAnswer, | ||||
|       this.currentPractise, | ||||
|       this.censusQ1, | ||||
|       this.censusQ2, | ||||
|       this.censusQ3, | ||||
|       this.censusQ4, | ||||
|       this.trafContacts, | ||||
|       this.usingSolelyOrSharedId, | ||||
|       this.usingSolelyOrSharedName, | ||||
|       this.isEffectedId, | ||||
|       this.isEffectedName, | ||||
|       this.effectedServices, | ||||
|       this.isCombinationId, | ||||
|       this.isCombinationName, | ||||
|       this.usedWithCombination, | ||||
|       this.comment, | ||||
|       this.attachments, | ||||
|       this.isBudgetId, | ||||
|       this.isBudgetName, | ||||
|       this.firstLineManagerId, | ||||
|       this.firstLineManagerName, | ||||
|       this.firstLineManagerApprovalId, | ||||
|       this.firstLineManagerApprovalName, | ||||
|       this.tlapiId, | ||||
|       this.tlapiName, | ||||
|       this.assessorEmployeeId, | ||||
|       this.assessorEmployeeName, | ||||
|       this.secondLineManagerId, | ||||
|       this.secondLineManagerName, | ||||
|       this.secondLineManagerApprovalId, | ||||
|       this.secondLineManagerApprovalName, | ||||
|       this.hospitalManagementId, | ||||
|       this.hospitalManagementName, | ||||
|       this.hospitalManagementApprovalId, | ||||
|       this.hospitalManagementApprovalName, | ||||
|       this.oracleCodes, | ||||
|       this.assessorTeamLeaderId, | ||||
|       this.assessorTeamLeaderName, | ||||
|       this.assessorTeamLeaderApprovalId, | ||||
|       this.assessorTeamLeaderApprovalName, | ||||
|       this.statusOfDRId, | ||||
|       this.statusOfDRName, | ||||
|       this.statusOfRequesterId, | ||||
|       this.statusOfRequesterName, | ||||
|       this.approved, | ||||
|       this.approvalUserId, | ||||
|       this.approvalUserName, | ||||
|       this.approvalDate, | ||||
|       this.docDate, | ||||
|       this.approvalDocDate, | ||||
|       this.isAssigned, | ||||
|       this.poNumber, | ||||
|       this.apiDirectorId, | ||||
|       this.apiDirectorName, | ||||
|       this.apiDirectorApprovalId, | ||||
|       this.apiDirectorApprovalName, | ||||
|       this.trafAssessIds, | ||||
|       this.trafPRIds, | ||||
|       this.trafOfferIds, | ||||
|       this.id, | ||||
|       this.createdBy, | ||||
|       this.createdDate, | ||||
|       this.modifiedBy, | ||||
|       this.modifiedDate}); | ||||
| 
 | ||||
|   TrafRequestDataModel.fromJson(Map<String, dynamic> json) { | ||||
|     reqNo = json['reqNo']; | ||||
|     reqCode = json['reqCode']; | ||||
|     employeeId = json['employeeId']; | ||||
|     employeeIdForDisplay = json['employeeIdForDisplay']; | ||||
|     employeeName = json['employeeName']; | ||||
|     employeeEmail = json['employeeEmail']; | ||||
|     positionId = json['positionId']; | ||||
|     positionName = json['positionName']; | ||||
|     if (json['departments'] != null) { | ||||
|       departments = <Departments>[]; | ||||
|       json['departments'].forEach((v) { | ||||
|         departments!.add(new Departments.fromJson(v)); | ||||
|       }); | ||||
|     } | ||||
|     if (json['trafDepartments'] != null) { | ||||
|       trafDepartments = <Departments>[]; | ||||
|       json['trafDepartments'].forEach((v) { | ||||
|         trafDepartments!.add(new Departments.fromJson(v)); | ||||
|       }); | ||||
|     } | ||||
|     siteId = json['siteId']; | ||||
|     siteName = json['siteName']; | ||||
|     requesterExtensionNumber = json['requesterExtensionNumber']; | ||||
|     requesterExtensionName = json['requesterExtensionName']; | ||||
|     requesterExtensionPositionId = json['requesterExtensionPositionId']; | ||||
|     requesterExtensionPositionName = json['requesterExtensionPositionName']; | ||||
|     requestTypeId = json['requestTypeId']; | ||||
|     requestTypeName = json['requestTypeName']; | ||||
|     assetNDId = json['assetNDId']; | ||||
|     modelId = json['modelId']; | ||||
|     modelName = json['modelName']; | ||||
|     manufacturerId = json['manufacturerId']; | ||||
|     manufacturerName = json['manufacturerName']; | ||||
|     assetId = json['assetId']; | ||||
|     assetSerialNo = json['assetSerialNo']; | ||||
|     assetNumber = json['assetNumber']; | ||||
|     assetName = json['assetName']; | ||||
|     assetAssetNDId = json['assetAssetNDId']; | ||||
|     qty = json['qty']; | ||||
|     purposeAnswer = json['purposeAnswer']; | ||||
|     currentPractise = json['currentPractise']; | ||||
|     censusQ1 = json['censusQ1']; | ||||
|     censusQ2 = json['censusQ2']; | ||||
|     censusQ3 = json['censusQ3']; | ||||
|     censusQ4 = json['censusQ4']; | ||||
|     if (json['trafContacts'] != null) { | ||||
|       trafContacts = <TrafContacts>[]; | ||||
|       json['trafContacts'].forEach((v) { | ||||
|         trafContacts!.add(new TrafContacts.fromJson(v)); | ||||
|       }); | ||||
|     } | ||||
|     usingSolelyOrSharedId = json['usingSolelyOrSharedId']; | ||||
|     usingSolelyOrSharedName = json['usingSolelyOrSharedName']; | ||||
|     isEffectedId = json['isEffectedId']; | ||||
|     isEffectedName = json['isEffectedName']; | ||||
|     effectedServices = json['effectedServices']; | ||||
|     isCombinationId = json['isCombinationId']; | ||||
|     isCombinationName = json['isCombinationName']; | ||||
|     usedWithCombination = json['usedWithCombination']; | ||||
|     comment = json['comment']; | ||||
|     if (json['attachments'] != null) { | ||||
|       attachments = <Attachments>[]; | ||||
|       json['attachments'].forEach((v) { | ||||
|         attachments!.add(new Attachments.fromJson(v)); | ||||
|       }); | ||||
|     } | ||||
|     isBudgetId = json['isBudgetId']; | ||||
|     isBudgetName = json['isBudgetName']; | ||||
|     firstLineManagerId = json['firstLineManagerId']; | ||||
|     firstLineManagerName = json['firstLineManagerName']; | ||||
|     firstLineManagerApprovalId = json['firstLineManagerApprovalId']; | ||||
|     firstLineManagerApprovalName = json['firstLineManagerApprovalName']; | ||||
|     tlapiId = json['tlapiId']; | ||||
|     tlapiName = json['tlapiName']; | ||||
|     assessorEmployeeId = json['assessorEmployeeId']; | ||||
|     assessorEmployeeName = json['assessorEmployeeName']; | ||||
|     secondLineManagerId = json['secondLineManagerId']; | ||||
|     secondLineManagerName = json['secondLineManagerName']; | ||||
|     secondLineManagerApprovalId = json['secondLineManagerApprovalId']; | ||||
|     secondLineManagerApprovalName = json['secondLineManagerApprovalName']; | ||||
|     hospitalManagementId = json['hospitalManagementId']; | ||||
|     hospitalManagementName = json['hospitalManagementName']; | ||||
|     hospitalManagementApprovalId = json['hospitalManagementApprovalId']; | ||||
|     hospitalManagementApprovalName = json['hospitalManagementApprovalName']; | ||||
|     if (json['oracleCodes'] != null) { | ||||
|       oracleCodes = <OracleCodes>[]; | ||||
|       json['oracleCodes'].forEach((v) { | ||||
|         oracleCodes!.add(new OracleCodes.fromJson(v)); | ||||
|       }); | ||||
|     } | ||||
|     assessorTeamLeaderId = json['assessorTeamLeaderId']; | ||||
|     assessorTeamLeaderName = json['assessorTeamLeaderName']; | ||||
|     assessorTeamLeaderApprovalId = json['assessorTeamLeaderApprovalId']; | ||||
|     assessorTeamLeaderApprovalName = json['assessorTeamLeaderApprovalName']; | ||||
|     statusOfDRId = json['statusOfDRId']; | ||||
|     statusOfDRName = json['statusOfDRName']; | ||||
|     statusOfRequesterId = json['statusOfRequesterId']; | ||||
|     statusOfRequesterName = json['statusOfRequesterName']; | ||||
|     approved = json['approved']; | ||||
|     approvalUserId = json['approvalUserId']; | ||||
|     approvalUserName = json['approvalUserName']; | ||||
|     approvalDate = json['approvalDate']; | ||||
|     docDate = json['docDate']; | ||||
|     approvalDocDate = json['approvalDocDate']; | ||||
|     isAssigned = json['isAssigned']; | ||||
|     poNumber = json['poNumber']; | ||||
|     apiDirectorId = json['apiDirectorId']; | ||||
|     apiDirectorName = json['apiDirectorName']; | ||||
|     apiDirectorApprovalId = json['apiDirectorApprovalId']; | ||||
|     apiDirectorApprovalName = json['apiDirectorApprovalName']; | ||||
|     trafAssessIds = json['trafAssessIds'].cast<int>(); | ||||
|     trafPRIds = json['trafPRIds'].cast<int>(); | ||||
|     trafOfferIds = json['trafOfferIds'].cast<int>(); | ||||
|     id = json['id']; | ||||
|     createdBy = json['createdBy']; | ||||
|     createdDate = json['createdDate']; | ||||
|     modifiedBy = json['modifiedBy']; | ||||
|     modifiedDate = json['modifiedDate']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['reqNo'] = this.reqNo; | ||||
|     data['reqCode'] = this.reqCode; | ||||
|     data['employeeId'] = this.employeeId; | ||||
|     data['employeeIdForDisplay'] = this.employeeIdForDisplay; | ||||
|     data['employeeName'] = this.employeeName; | ||||
|     data['employeeEmail'] = this.employeeEmail; | ||||
|     data['positionId'] = this.positionId; | ||||
|     data['positionName'] = this.positionName; | ||||
|     if (this.departments != null) { | ||||
|       data['departments'] = this.departments!.map((v) => v.toJson()).toList(); | ||||
|     } | ||||
|     if (this.trafDepartments != null) { | ||||
|       data['trafDepartments'] = this.trafDepartments!.map((v) => v.toJson()).toList(); | ||||
|     } | ||||
|     data['siteId'] = this.siteId; | ||||
|     data['siteName'] = this.siteName; | ||||
|     data['requesterExtensionNumber'] = this.requesterExtensionNumber; | ||||
|     data['requesterExtensionName'] = this.requesterExtensionName; | ||||
|     data['requesterExtensionPositionId'] = this.requesterExtensionPositionId; | ||||
|     data['requesterExtensionPositionName'] = this.requesterExtensionPositionName; | ||||
|     data['requestTypeId'] = this.requestTypeId; | ||||
|     data['requestTypeName'] = this.requestTypeName; | ||||
|     data['assetNDId'] = this.assetNDId; | ||||
|     data['modelId'] = this.modelId; | ||||
|     data['modelName'] = this.modelName; | ||||
|     data['manufacturerId'] = this.manufacturerId; | ||||
|     data['manufacturerName'] = this.manufacturerName; | ||||
|     data['assetId'] = this.assetId; | ||||
|     data['assetSerialNo'] = this.assetSerialNo; | ||||
|     data['assetNumber'] = this.assetNumber; | ||||
|     data['assetName'] = this.assetName; | ||||
|     data['assetAssetNDId'] = this.assetAssetNDId; | ||||
|     data['qty'] = this.qty; | ||||
|     data['purposeAnswer'] = this.purposeAnswer; | ||||
|     data['currentPractise'] = this.currentPractise; | ||||
|     data['censusQ1'] = this.censusQ1; | ||||
|     data['censusQ2'] = this.censusQ2; | ||||
|     data['censusQ3'] = this.censusQ3; | ||||
|     data['censusQ4'] = this.censusQ4; | ||||
|     if (this.trafContacts != null) { | ||||
|       data['trafContacts'] = this.trafContacts!.map((v) => v.toJson()).toList(); | ||||
|     } | ||||
|     data['usingSolelyOrSharedId'] = this.usingSolelyOrSharedId; | ||||
|     data['usingSolelyOrSharedName'] = this.usingSolelyOrSharedName; | ||||
|     data['isEffectedId'] = this.isEffectedId; | ||||
|     data['isEffectedName'] = this.isEffectedName; | ||||
|     data['effectedServices'] = this.effectedServices; | ||||
|     data['isCombinationId'] = this.isCombinationId; | ||||
|     data['isCombinationName'] = this.isCombinationName; | ||||
|     data['usedWithCombination'] = this.usedWithCombination; | ||||
|     data['comment'] = this.comment; | ||||
|     if (this.attachments != null) { | ||||
|       data['attachments'] = this.attachments!.map((v) => v.toJson()).toList(); | ||||
|     } else { | ||||
|       data['attachments'] = []; | ||||
|     } | ||||
|     data['isBudgetId'] = this.isBudgetId; | ||||
|     data['isBudgetName'] = this.isBudgetName; | ||||
|     data['firstLineManagerId'] = this.firstLineManagerId; | ||||
|     data['firstLineManagerName'] = this.firstLineManagerName; | ||||
|     data['firstLineManagerApprovalId'] = this.firstLineManagerApprovalId; | ||||
|     data['firstLineManagerApprovalName'] = this.firstLineManagerApprovalName; | ||||
|     data['tlapiId'] = this.tlapiId; | ||||
|     data['tlapiName'] = this.tlapiName; | ||||
|     data['assessorEmployeeId'] = this.assessorEmployeeId; | ||||
|     data['assessorEmployeeName'] = this.assessorEmployeeName; | ||||
|     data['secondLineManagerId'] = this.secondLineManagerId; | ||||
|     data['secondLineManagerName'] = this.secondLineManagerName; | ||||
|     data['secondLineManagerApprovalId'] = this.secondLineManagerApprovalId; | ||||
|     data['secondLineManagerApprovalName'] = this.secondLineManagerApprovalName; | ||||
|     data['hospitalManagementId'] = this.hospitalManagementId; | ||||
|     data['hospitalManagementName'] = this.hospitalManagementName; | ||||
|     data['hospitalManagementApprovalId'] = this.hospitalManagementApprovalId; | ||||
|     data['hospitalManagementApprovalName'] = this.hospitalManagementApprovalName; | ||||
|     if (this.oracleCodes != null) { | ||||
|       data['oracleCodes'] = this.oracleCodes!.map((v) => v.toJson()).toList(); | ||||
|     } | ||||
|     data['assessorTeamLeaderId'] = this.assessorTeamLeaderId; | ||||
|     data['assessorTeamLeaderName'] = this.assessorTeamLeaderName; | ||||
|     data['assessorTeamLeaderApprovalId'] = this.assessorTeamLeaderApprovalId; | ||||
|     data['assessorTeamLeaderApprovalName'] = this.assessorTeamLeaderApprovalName; | ||||
|     data['statusOfDRId'] = this.statusOfDRId; | ||||
|     data['statusOfDRName'] = this.statusOfDRName; | ||||
|     data['statusOfRequesterId'] = this.statusOfRequesterId; | ||||
|     data['statusOfRequesterName'] = this.statusOfRequesterName; | ||||
|     data['approved'] = this.approved; | ||||
|     data['approvalUserId'] = this.approvalUserId; | ||||
|     data['approvalUserName'] = this.approvalUserName; | ||||
|     data['approvalDate'] = this.approvalDate; | ||||
|     data['docDate'] = this.docDate; | ||||
|     data['approvalDocDate'] = this.approvalDocDate; | ||||
|     data['isAssigned'] = this.isAssigned; | ||||
|     data['poNumber'] = this.poNumber; | ||||
|     data['apiDirectorId'] = this.apiDirectorId; | ||||
|     data['apiDirectorName'] = this.apiDirectorName; | ||||
|     data['apiDirectorApprovalId'] = this.apiDirectorApprovalId; | ||||
|     data['apiDirectorApprovalName'] = this.apiDirectorApprovalName; | ||||
|     data['trafAssessIds'] = this.trafAssessIds; | ||||
|     data['trafPRIds'] = this.trafPRIds; | ||||
|     data['trafOfferIds'] = this.trafOfferIds; | ||||
|     data['id'] = this.id; | ||||
|     data['createdBy'] = this.createdBy; | ||||
|     data['createdDate'] = this.createdDate; | ||||
|     data['modifiedBy'] = this.modifiedBy; | ||||
|     data['modifiedDate'] = this.modifiedDate; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class TrafContacts { | ||||
|   int? id; | ||||
|   int? trafId; | ||||
|   String? userId; | ||||
|   String? name; | ||||
|   String? telephone; | ||||
|   String? notes; | ||||
| 
 | ||||
|   TrafContacts({this.id, this.trafId, this.userId, this.name, this.telephone, this.notes}); | ||||
| 
 | ||||
|   TrafContacts.fromJson(Map<String, dynamic> json) { | ||||
|     id = json['id']; | ||||
|     trafId = json['trafId']; | ||||
|     userId = json['userId']; | ||||
|     name = json['name']; | ||||
|     telephone = json['telephone']; | ||||
|     notes = json['notes']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['id'] = this.id; | ||||
|     data['trafId'] = this.trafId; | ||||
|     data['userId'] = this.userId; | ||||
|     data['name'] = this.name; | ||||
|     data['telephone'] = this.telephone; | ||||
|     data['notes'] = this.notes; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class OracleCodes { | ||||
|   int? id; | ||||
|   int? assetNDId; | ||||
|   int? codeTypeId; | ||||
|   String? codeTypeName; | ||||
|   int? codeTypeValue; | ||||
|   String? codeValue; | ||||
| 
 | ||||
|   OracleCodes({this.id, this.assetNDId, this.codeTypeId, this.codeTypeName, this.codeTypeValue, this.codeValue}); | ||||
| 
 | ||||
|   OracleCodes.fromJson(Map<String, dynamic> json) { | ||||
|     id = json['id']; | ||||
|     assetNDId = json['assetNDId']; | ||||
|     codeTypeId = json['codeTypeId']; | ||||
|     codeTypeName = json['codeTypeName']; | ||||
|     codeTypeValue = json['codeTypeValue']; | ||||
|     codeValue = json['codeValue']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['id'] = this.id; | ||||
|     data['assetNDId'] = this.assetNDId; | ||||
|     data['codeTypeId'] = this.codeTypeId; | ||||
|     data['codeTypeName'] = this.codeTypeName; | ||||
|     data['codeTypeValue'] = this.codeTypeValue; | ||||
|     data['codeValue'] = this.codeValue; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class Departments { | ||||
|   int? id; | ||||
|   int? trafId; | ||||
|   int? departmentId; | ||||
|   String? departmentName; | ||||
| 
 | ||||
|   Departments({this.id, this.trafId, this.departmentId, this.departmentName}); | ||||
| 
 | ||||
|   Departments.fromJson(Map<String, dynamic> json) { | ||||
|     id = json['id']; | ||||
|     trafId = json['trafId']; | ||||
|     departmentId = json['departmentId']; | ||||
|     departmentName = json['departmentName']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['id'] = this.id; | ||||
|     data['trafId'] = this.trafId; | ||||
|     data['departmentId'] = this.departmentId; | ||||
|     data['departmentName'] = this.departmentName; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class Attachments { | ||||
|   int? id; | ||||
|   int? trafId; | ||||
|   String? attachmentName; | ||||
| 
 | ||||
|   Attachments({this.id, this.trafId, this.attachmentName}); | ||||
| 
 | ||||
|   Attachments.fromJson(Map<String, dynamic> json) { | ||||
|     id = json['id']; | ||||
|     trafId = json['trafId']; | ||||
|     attachmentName = json['attachmentName']; | ||||
|   } | ||||
| 
 | ||||
|   Map<String, dynamic> toJson() { | ||||
|     final Map<String, dynamic> data = new Map<String, dynamic>(); | ||||
|     data['id'] = this.id; | ||||
|     data['trafId'] = this.trafId; | ||||
|     data['attachmentName'] = this.attachmentName; | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,50 @@ | ||||
| import 'dart:convert'; | ||||
| 
 | ||||
| import 'package:flutter/foundation.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:test_sa/controllers/api_routes/api_manager.dart'; | ||||
| import 'package:test_sa/controllers/api_routes/urls.dart'; | ||||
| 
 | ||||
| import 'traf_request_model.dart'; | ||||
| 
 | ||||
| class TrafRequestProvider extends ChangeNotifier { | ||||
|   bool isLoading = false; | ||||
| 
 | ||||
|   TrafRequestDataModel? trafRequestDataModel; | ||||
| 
 | ||||
|   Future<int> addTraf(Map<String, dynamic> body) async { | ||||
|     try { | ||||
|       isLoading = true; | ||||
|       notifyListeners(); | ||||
|       Response response = await ApiManager.instance.post(URLs.addTRAF, body: body); | ||||
|       if (response.statusCode >= 200 && response.statusCode < 300) { | ||||
|         // trafRequestDataModel = TrafRequestDataModel.fromJson(json.decode(response.body)["data"]); | ||||
|       } | ||||
|       isLoading = false; | ||||
|       notifyListeners(); | ||||
|       return 0; | ||||
|     } catch (error) { | ||||
|       isLoading = false; | ||||
|       notifyListeners(); | ||||
|       return -1; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   Future<int> getTRAFById(int trafId) async { | ||||
|     try { | ||||
|       isLoading = true; | ||||
|       notifyListeners(); | ||||
|       Response response = await ApiManager.instance.get("${URLs.getTRAFById}?tRAFId=$trafId"); | ||||
|       if (response.statusCode >= 200 && response.statusCode < 300) { | ||||
|         trafRequestDataModel = TrafRequestDataModel.fromJson(json.decode(response.body)["data"]); | ||||
|       } | ||||
|       isLoading = false; | ||||
|       notifyListeners(); | ||||
|       return 0; | ||||
|     } catch (error) { | ||||
|       isLoading = false; | ||||
|       notifyListeners(); | ||||
|       return -1; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,245 @@ | ||||
| import 'dart:convert'; | ||||
| import 'dart:io'; | ||||
| 
 | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:test_sa/extensions/context_extension.dart'; | ||||
| import 'package:test_sa/extensions/int_extensions.dart'; | ||||
| import 'package:test_sa/extensions/text_extensions.dart'; | ||||
| import 'package:test_sa/extensions/widget_extensions.dart'; | ||||
| import 'package:test_sa/models/generic_attachment_model.dart'; | ||||
| import 'package:test_sa/models/lookup.dart'; | ||||
| import 'package:test_sa/modules/cm_module/utilities/service_request_utils.dart'; | ||||
| import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; | ||||
| import 'package:test_sa/modules/traf_module/traf_request_model.dart'; | ||||
| import 'package:test_sa/modules/traf_module/traf_request_provider.dart'; | ||||
| import 'package:test_sa/new_views/app_style/app_color.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/multiple_item_drop_down_menu.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart'; | ||||
| import 'package:test_sa/providers/lookups/classification_lookup_provider.dart'; | ||||
| import 'package:test_sa/providers/lookups/recommendation_lookup_provider.dart'; | ||||
| import 'package:test_sa/providers/lookups/request_type_lookup_provider.dart'; | ||||
| import 'package:test_sa/views/widgets/equipment/asset_picker.dart'; | ||||
| import 'package:test_sa/models/device/asset.dart'; | ||||
| import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; | ||||
| 
 | ||||
| class UpdateTrafRequestPage extends StatefulWidget { | ||||
|   static const String id = "/update-TRAF"; | ||||
| 
 | ||||
|   UpdateTrafRequestPage({Key? key}) : super(key: key); | ||||
| 
 | ||||
|   @override | ||||
|   _UpdateTrafRequestPageState createState() { | ||||
|     return _UpdateTrafRequestPageState(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class _UpdateTrafRequestPageState extends State<UpdateTrafRequestPage> { | ||||
|   Lookup? requestType; | ||||
|   Lookup? assessorRecommendation; | ||||
|   Lookup? classificationType; | ||||
|   late TrafRequestProvider trafRequestProvider; | ||||
| 
 | ||||
|   late TrafRequestDataModel trafRequest; | ||||
| 
 | ||||
|   List<Lookup> abc = []; | ||||
| 
 | ||||
|   List<Asset> _deviceList = []; | ||||
|   Asset? asset; | ||||
|   List<GenericAttachmentModel> attachments = []; | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     Provider.of<ClassificationLookupProvider>(context, listen: false).reset(); | ||||
|     Provider.of<RecommendationLookupProvider>(context, listen: false).reset(); | ||||
|     trafRequestProvider = Provider.of<TrafRequestProvider>(context, listen: false); | ||||
|     trafRequest = trafRequestProvider.trafRequestDataModel!; | ||||
|     attachments = trafRequest.attachments?.map((item) => GenericAttachmentModel(id: item.id, name: item.attachmentName)).toList() ?? []; | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void dispose() { | ||||
|     super.dispose(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|         appBar: const DefaultAppBar(title: "Update TRAF Request"), | ||||
|         body: Column( | ||||
|           children: [ | ||||
|             SingleChildScrollView( | ||||
|               padding: const EdgeInsets.all(16), | ||||
|               child: Column( | ||||
|                 mainAxisSize: MainAxisSize.min, | ||||
|                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                 children: [ | ||||
|                   SingleItemDropDownMenu<Lookup, ClassificationLookupProvider>( | ||||
|                     context: context, | ||||
|                     showAsBottomSheet: true, | ||||
|                     backgroundColor: AppColor.neutral100, | ||||
|                     showShadow: false, | ||||
|                     title: "Classification", | ||||
|                     initialValue: classificationType, | ||||
|                     onSelect: (value) { | ||||
|                       classificationType = value; | ||||
|                       // trafRequest?.cla = value?.value; | ||||
|                       // setState(() {}); | ||||
|                     }, | ||||
|                   ), | ||||
|                   12.height, | ||||
|                   SingleItemDropDownMenu<Lookup, RequestTypeLookupProvider>( | ||||
|                     context: context, | ||||
|                     showAsBottomSheet: true, | ||||
|                     backgroundColor: AppColor.neutral100, | ||||
|                     showShadow: false, | ||||
|                     title: context.translation.requestType, | ||||
|                     initialValue: requestType, | ||||
|                     onSelect: (value) { | ||||
|                       requestType = value; | ||||
|                       setState(() {}); | ||||
|                     }, | ||||
|                   ), | ||||
|                   if (requestType?.id == 732) ...[ | ||||
|                     12.height, | ||||
|                     AppTextFormField( | ||||
|                       initialValue: "", | ||||
|                       makeMultiLinesNull: true, | ||||
|                       labelText: "Recommended Quantity", | ||||
|                       backgroundColor: AppColor.fieldBgColor(context), | ||||
|                       labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), | ||||
|                       showShadow: false, | ||||
|                       textInputType: TextInputType.number, | ||||
|                       onChange: (value) { | ||||
|                         trafRequest.qty = int.tryParse(value); | ||||
|                       }, | ||||
|                     ), | ||||
|                   ], | ||||
|                   if (requestType?.id == 733) ...[ | ||||
|                     12.height, | ||||
|                     AssetPicker( | ||||
|                       device: asset, | ||||
|                       editable: false, | ||||
|                       showLoading: false, | ||||
| 
 | ||||
|                       // borderColor: AppColor.black20, | ||||
|                       // backgroundColor: AppColor.white936, | ||||
|                       onPick: (asset) async { | ||||
|                         this.asset = asset; | ||||
|                         setState(() {}); | ||||
|                         // pendingAssetServiceRequest = null; | ||||
|                         // _serviceRequest.device = asset; | ||||
|                         // await checkAssetForPendingServiceRequest(asset.id!.toInt()); | ||||
|                         // if (pendingAssetServiceRequest != null && pendingAssetServiceRequest!.details!.isNotEmpty) { | ||||
|                         //   showPendingRequestBottomSheet(); | ||||
|                         // } | ||||
|                       }, | ||||
|                     ), | ||||
|                   ], | ||||
|                   // 12.height, | ||||
|                   // MultipleItemDropDownMenu<Lookup, RequestTypeLookupProvider>( | ||||
|                   //   context: context, | ||||
|                   //   showAsBottomSheet: true, | ||||
|                   //   backgroundColor: AppColor.neutral100, | ||||
|                   //   showShadow: false, | ||||
|                   //   showCancel: true, | ||||
|                   //   title: "Please specify departments and relations", | ||||
|                   //   initialValue: abc, | ||||
|                   //   onSelect: (value) { | ||||
|                   //     abc = value ?? []; | ||||
|                   //     // setState(() { | ||||
|                   //     // | ||||
|                   //     // }); | ||||
|                   //   }, | ||||
|                   // ), | ||||
|                   12.height, | ||||
|                   Text( | ||||
|                     "Quantity to be Transfer", | ||||
|                     style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50, fontWeight: FontWeight.w500), | ||||
|                   ), | ||||
|                   8.height, | ||||
|                   AssetPicker( | ||||
|                     deviceList: _deviceList, | ||||
|                     showLoading: false, | ||||
|                     borderColor: AppColor.black20, | ||||
|                     buttonColor: AppColor.white936, | ||||
|                     multiSelection: true, | ||||
|                     onAssetRemove: (itemId) { | ||||
|                       _deviceList.removeWhere((asset) => asset.id?.toInt() == itemId); | ||||
|                       setState(() {}); | ||||
|                     }, | ||||
|                     onMultiAssetPick: (assetList) { | ||||
|                       _deviceList.addAll(assetList); | ||||
|                       final seenIds = <num?>{}; | ||||
|                       _deviceList = _deviceList.where((item) => seenIds.add(item.id)).toList(); | ||||
|                       setState(() {}); | ||||
|                     }, | ||||
|                   ), | ||||
|                   12.height, | ||||
|                   SingleItemDropDownMenu<Lookup, RecommendationLookupProvider>( | ||||
|                     context: context, | ||||
|                     showAsBottomSheet: true, | ||||
|                     backgroundColor: AppColor.neutral100, | ||||
|                     showShadow: false, | ||||
|                     title: "Assessor Recommendation", | ||||
|                     initialValue: assessorRecommendation, | ||||
|                     onSelect: (value) { | ||||
|                       assessorRecommendation = value; | ||||
|                       trafRequest?.requestTypeId = value?.value; | ||||
|                       // trafRequest?.requestTypeId = value?.value; | ||||
|                       // setState(() {}); | ||||
|                     }, | ||||
|                   ), | ||||
|                   12.height, | ||||
|                   AppTextFormField( | ||||
|                     initialValue: "", | ||||
|                     textInputType: TextInputType.multiline, | ||||
|                     alignLabelWithHint: true, | ||||
|                     labelText: "Assessor comment", | ||||
|                     backgroundColor: AppColor.fieldBgColor(context), | ||||
|                     labelStyle: AppTextStyles.textFieldLabelStyle.copyWith(color: AppColor.textColor(context)), | ||||
|                     showShadow: false, | ||||
|                     onChange: (value) { | ||||
|                       trafRequest?.comment = value; // todo confirm parameter shaheer | ||||
|                     }, | ||||
|                   ), | ||||
|                   12.height, | ||||
|                   AttachmentPicker( | ||||
|                     label: context.translation.attachFiles, | ||||
|                     attachment: attachments, | ||||
|                     buttonColor: AppColor.black10, | ||||
|                     onlyImages: false, | ||||
|                     buttonIcon: 'image-plus'.toSvgAsset(color: AppColor.neutral120), | ||||
|                     onChange: (attachment) { | ||||
|                       attachments = attachment; | ||||
|                     }, | ||||
|                   ), | ||||
|                 ], | ||||
|               ).toShadowContainer(context, borderRadius: 20, padding: 12), | ||||
|             ).expanded, | ||||
|             FooterActionButton.footerContainer( | ||||
|               context: context, | ||||
|               child: AppFilledButton( | ||||
|                   buttonColor: AppColor.primary10, | ||||
|                   label: "Update", | ||||
|                   onPressed: () { | ||||
|                     trafRequest.attachments = []; | ||||
|                     for (var item in attachments) { | ||||
|                       String fileName = | ||||
|                           ServiceRequestUtils.isLocalUrl(item.name ?? '') ? ("${item.name ?? ''.split("/").last}|${base64Encode(File(item.name ?? '').readAsBytesSync())}") : item.name ?? ''; | ||||
|                       trafRequest.attachments!.add( | ||||
|                         Attachments(id: item.id, trafId: item.id, attachmentName: fileName), | ||||
|                       ); | ||||
|                     } | ||||
|                   } | ||||
|                   // buttonColor: AppColor.primary10, | ||||
|                   ), | ||||
|             ), | ||||
|           ], | ||||
|         )); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,134 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:test_sa/controllers/providers/api/oracle_code_provider.dart'; | ||||
| import 'package:test_sa/controllers/providers/api/parts_provider.dart'; | ||||
| import 'package:test_sa/controllers/providers/api/search_user_provider.dart'; | ||||
| import 'package:test_sa/controllers/providers/api/user_provider.dart'; | ||||
| import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; | ||||
| import 'package:test_sa/extensions/context_extension.dart'; | ||||
| import 'package:test_sa/extensions/int_extensions.dart'; | ||||
| import 'package:test_sa/extensions/widget_extensions.dart'; | ||||
| import 'package:test_sa/models/new_models/asset_nd_auto_complete_by_dynamic_codes_model.dart'; | ||||
| import 'package:test_sa/models/new_models/users_based_on_search_model.dart'; | ||||
| import 'package:test_sa/new_views/app_style/app_color.dart'; | ||||
| import 'package:test_sa/views/app_style/sizing.dart'; | ||||
| 
 | ||||
| import '../../../extensions/text_extensions.dart'; | ||||
| import '../../../models/service_request/spare_parts.dart'; | ||||
| import '../../../new_views/app_style/app_text_style.dart'; | ||||
| 
 | ||||
| class SearchUserAutoCompleteField extends StatefulWidget { | ||||
|   final String initialValue; | ||||
|   final num? assetId; | ||||
|   final bool clearAfterPick, byName; | ||||
|   final Function(UsersBasedOnSearchModel) onPick; | ||||
| 
 | ||||
|   const SearchUserAutoCompleteField({Key? key, required this.byName, required this.initialValue, this.assetId, required this.onPick, this.clearAfterPick = true}) : super(key: key); | ||||
| 
 | ||||
|   @override | ||||
|   _UsersAutoCompleteFieldState createState() => _UsersAutoCompleteFieldState(); | ||||
| } | ||||
| 
 | ||||
| class _UsersAutoCompleteFieldState extends State<SearchUserAutoCompleteField> { | ||||
|   late UserSearchProvider _TechnologyUsersProvider; | ||||
| 
 | ||||
|   late TextEditingController _controller; | ||||
| 
 | ||||
|   bool loading = false; | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     _controller = TextEditingController(text: widget.initialValue); | ||||
|     super.initState(); | ||||
|     _TechnologyUsersProvider = Provider.of<UserSearchProvider>(context, listen: false); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void didUpdateWidget(covariant SearchUserAutoCompleteField oldWidget) { | ||||
|     if (widget.initialValue != oldWidget.initialValue) { | ||||
|       _controller = TextEditingController(text: widget.initialValue); | ||||
|     } | ||||
|     super.didUpdateWidget(oldWidget); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void dispose() { | ||||
|     _controller.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final border = UnderlineInputBorder(borderSide: BorderSide.none, borderRadius: BorderRadius.circular(10)); | ||||
|     return Container( | ||||
|       decoration: BoxDecoration( | ||||
|         color: AppColor.background(context), | ||||
|         borderRadius: BorderRadius.circular(AppStyle.borderRadius * AppStyle.getScaleFactor(context)), | ||||
|         //  boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)], | ||||
|       ), | ||||
|       child: Autocomplete<UsersBasedOnSearchModel>( | ||||
|         optionsBuilder: (TextEditingValue textEditingValue) async { | ||||
|           if (textEditingValue.text.isEmpty) { | ||||
|             if (loading) { | ||||
|               setState(() { | ||||
|                 loading = false; | ||||
|               }); | ||||
|             } | ||||
|             return const Iterable<UsersBasedOnSearchModel>.empty(); | ||||
|           } | ||||
|           if (!loading) { | ||||
|             setState(() { | ||||
|               loading = true; | ||||
|             }); | ||||
|           } | ||||
|           List<UsersBasedOnSearchModel> workOrders = (await _TechnologyUsersProvider.getUsersBasedOnSearch(textEditingValue.text)); | ||||
|           setState(() { | ||||
|             loading = false; | ||||
|           }); | ||||
|           return workOrders; | ||||
|         }, | ||||
|         displayStringForOption: (UsersBasedOnSearchModel option) => widget.byName ? option.userName ?? "" : option.employeeId ?? "", | ||||
|         fieldViewBuilder: (BuildContext context, TextEditingController fieldTextEditingController, FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) { | ||||
|           return TextField( | ||||
|             controller: _controller, | ||||
|             focusNode: fieldFocusNode, | ||||
|             style: AppTextStyles.bodyText.copyWith(color: AppColor.black10), | ||||
|             textAlign: TextAlign.start, | ||||
|             decoration: InputDecoration( | ||||
|               border: border, | ||||
|               disabledBorder: border, | ||||
|               focusedBorder: border, | ||||
|               enabledBorder: border, | ||||
|               errorBorder: border, | ||||
|               contentPadding: EdgeInsets.symmetric(vertical: 8.toScreenHeight, horizontal: 16.toScreenWidth), | ||||
|               constraints: const BoxConstraints(), | ||||
|               suffixIconConstraints: const BoxConstraints(maxHeight: 24, maxWidth: 24 + 8), | ||||
|               filled: true, | ||||
|               fillColor: AppColor.fieldBgColor(context), | ||||
|               errorStyle: AppTextStyle.tiny.copyWith(color: context.isDark ? AppColor.red50 : AppColor.red60), | ||||
|               floatingLabelStyle: AppTextStyle.body1.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? null : AppColor.neutral20), | ||||
|               labelText: "Users going to use technology", | ||||
|               labelStyle: AppTextStyles.tinyFont.copyWith(color: AppColor.textColor(context)), | ||||
|               suffixIcon: loading ? const CircularProgressIndicator(color: AppColor.primary10, strokeWidth: 3.0).paddingOnly(end: 8) : null, | ||||
|             ), | ||||
|             textInputAction: TextInputAction.search, | ||||
|             onChanged: (text) { | ||||
|               fieldTextEditingController.text = text; | ||||
|             }, | ||||
|             onSubmitted: (String value) { | ||||
|               onFieldSubmitted(); | ||||
|             }, | ||||
|           ); | ||||
|         }, | ||||
|         onSelected: (UsersBasedOnSearchModel selection) { | ||||
|           if (widget.clearAfterPick) { | ||||
|             _controller.clear(); | ||||
|           } else { | ||||
|             _controller.text = widget.byName ? (selection.userName ?? "") : (selection.employeeId ?? ""); | ||||
|           } | ||||
|           widget.onPick(selection); | ||||
|         }, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,252 @@ | ||||
| import 'package:flutter/cupertino.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:test_sa/extensions/context_extension.dart'; | ||||
| import 'package:test_sa/extensions/int_extensions.dart'; | ||||
| import 'package:test_sa/extensions/widget_extensions.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/app_loading_manager.dart'; | ||||
| import 'package:test_sa/providers/loading_list_notifier.dart'; | ||||
| import 'package:test_sa/views/widgets/bottom_sheets/selection_bottom_sheet.dart'; | ||||
| import 'package:test_sa/views/widgets/fullscreen_dialogs/multiple_selection_fullscreen_dialog.dart'; | ||||
| import 'package:test_sa/views/widgets/fullscreen_dialogs/selection_fullscreen_dialog.dart'; | ||||
| 
 | ||||
| import '../../models/base.dart'; | ||||
| import '../app_style/app_color.dart'; | ||||
| 
 | ||||
| class MultipleItemDropDownMenu<T extends Base, X extends LoadingListNotifier> extends StatefulWidget { | ||||
|   final BuildContext context; | ||||
|   final Function(List<T>?)? onSelect; // Now accepts nullable values | ||||
|   final List<T> initialValue; | ||||
|   final T? disableValue; | ||||
|   final bool enabled; | ||||
|   final bool showAsBottomSheet; | ||||
|   final bool showAsFullScreenDialog; | ||||
|   final List<T>? staticData; | ||||
|   final String title; | ||||
|   final double? height; | ||||
|   final bool showShadow; | ||||
|   final bool showCancel; | ||||
|   final Color? backgroundColor; // Now nullable | ||||
|   final bool? loading; // Now nullable | ||||
|   final int? requestById; // Now nullable | ||||
| 
 | ||||
|   /// To use a static data (without calling API) | ||||
|   /// just send [NullableLoadingProvider] as generic data type and fill the [staticData] | ||||
|   const MultipleItemDropDownMenu({ | ||||
|     Key? key, | ||||
|     this.title = "", | ||||
|     required this.context, | ||||
|     this.onSelect, | ||||
|     this.initialValue = const [], | ||||
|     this.disableValue, | ||||
|     this.enabled = true, | ||||
|     this.height, | ||||
|     this.showAsBottomSheet = false, | ||||
|     this.showAsFullScreenDialog = true, | ||||
|     this.staticData, // Provide a default empty list | ||||
|     this.showShadow = true, | ||||
|     this.showCancel = false, | ||||
|     this.backgroundColor, | ||||
|     this.loading, | ||||
|     this.requestById, | ||||
|   }) : super(key: key); | ||||
| 
 | ||||
|   @override | ||||
|   State<MultipleItemDropDownMenu<T, X>> createState() => _MultipleItemDropDownMenuState<T, X>(); | ||||
| } | ||||
| 
 | ||||
| class _MultipleItemDropDownMenuState<T extends Base, X extends LoadingListNotifier> extends State<MultipleItemDropDownMenu<T, X>> { | ||||
|   List<T> _selectedItem = []; | ||||
|   X? provider; | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     if (X != NullableLoadingProvider) { | ||||
|       provider = Provider.of<X>(widget.context); | ||||
|     } | ||||
| 
 | ||||
|     if (widget.initialValue.isNotEmpty) { | ||||
|       // Get the source list | ||||
|       final sourceList = (X == NullableLoadingProvider ? widget.staticData : provider?.items); | ||||
| 
 | ||||
|       if (sourceList != null && sourceList.isNotEmpty) { | ||||
|         // Collect matches based on identifier | ||||
|         final results = sourceList.where((element) => widget.initialValue!.any((init) => init.name == element.name)).toList(); | ||||
| 
 | ||||
|         if (results.isNotEmpty) { | ||||
|           _selectedItem | ||||
|             ..clear() | ||||
|             ..addAll(results.cast<T>()); | ||||
| 
 | ||||
|           // Call onSelect if items changed | ||||
|           if (widget.onSelect != null) { | ||||
|             widget.onSelect!(_selectedItem); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // if (widget.initialValue != null) { | ||||
|     //   final result = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.where((element) => element.identifier == widget.initialValue?.identifier); | ||||
|     //   if (result?.isNotEmpty ?? false) _selectedItem.add(result!.first as T); | ||||
|     //   if (widget.onSelect != null && (widget.initialValue?.identifier ?? "") != (_selectedItem?.identifier ?? "")) { | ||||
|     //     widget.onSelect!(_selectedItem); // Non-null assertion after null check | ||||
|     //   } | ||||
|     // } | ||||
|     super.initState(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void setState(VoidCallback fn) { | ||||
|     if (mounted) super.setState(fn); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void didUpdateWidget(covariant MultipleItemDropDownMenu<T, X> oldWidget) { | ||||
|     if (widget.initialValue.isNotEmpty) { | ||||
|       // Get the source list | ||||
|       final sourceList = (X == NullableLoadingProvider ? widget.staticData : provider?.items); | ||||
|       if (sourceList != null && sourceList.isNotEmpty) { | ||||
|         // Collect matches based on identifier | ||||
| 
 | ||||
|         final results = sourceList.where((element) => widget.initialValue!.any((init) => init.name == element.name)).toList(); | ||||
|         if (results.isNotEmpty) { | ||||
|           _selectedItem | ||||
|             ..clear() | ||||
|             ..addAll(results.cast<T>()); | ||||
| 
 | ||||
|           // Call onSelect if items changed | ||||
|           if (widget.onSelect != null) { | ||||
|             widget.onSelect!(_selectedItem); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     // if (widget.initialValue != null) { | ||||
|     //   final result = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.where((element) => element.identifier == widget.initialValue?.identifier); | ||||
|     // | ||||
|     //   if (result?.isNotEmpty ?? false) { | ||||
|     //     _selectedItem.add(result!.first as T); | ||||
|     //   } else { | ||||
|     //     _selectedItem = []; | ||||
|     //   } | ||||
|     //   // if (widget.onSelect != null && (widget.initialValue?.identifier ?? "") != (_selectedItem?.identifier ?? "")) { | ||||
|     //   //   widget.onSelect!(_selectedItem); // Non-null assertion after null check | ||||
|     //   // } | ||||
|     // } else { | ||||
|     //   _selectedItem = []; | ||||
|     // } | ||||
|     super.didUpdateWidget(oldWidget); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final isEmpty = (X == NullableLoadingProvider ? widget.staticData : provider?.items)?.isEmpty ?? true; | ||||
|     return AppLoadingManager( | ||||
|       isLoading: widget.loading ?? ((X == NullableLoadingProvider) ? false : provider?.loading ?? false), | ||||
|       // Provide default value if null | ||||
|       isFailedLoading: (X == NullableLoadingProvider) ? false : provider?.items == null, | ||||
|       stateCode: (X == NullableLoadingProvider) ? 200 : provider?.stateCode, | ||||
|       onRefresh: () async { | ||||
|         if (X != NullableLoadingProvider) { | ||||
|           provider?.reset(); | ||||
|           await provider?.getData(id: widget.requestById); | ||||
|         } | ||||
|       }, | ||||
|       child: Container( | ||||
|         // height: widget.height ?? 60.toScreenHeight, | ||||
|         padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth, vertical: 8.toScreenHeight), | ||||
|         decoration: BoxDecoration( | ||||
|           color: context.isDark && (widget.enabled == false || isEmpty) | ||||
|               ? AppColor.neutral20 | ||||
|               : (widget.enabled == false || isEmpty) | ||||
|                   ? AppColor.neutral40 | ||||
|                   : widget.backgroundColor ?? AppColor.background(context), | ||||
|           borderRadius: BorderRadius.circular(10), | ||||
|           boxShadow: widget.showShadow ? [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)] : null, | ||||
|         ), | ||||
|         child: Row( | ||||
|           crossAxisAlignment: CrossAxisAlignment.start, | ||||
|           children: [ | ||||
|             Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               mainAxisAlignment: MainAxisAlignment.center, | ||||
|               mainAxisSize: MainAxisSize.min, | ||||
|               children: [ | ||||
|                 Text( | ||||
|                   widget.title, | ||||
|                   style: Theme.of(context).textTheme.bodySmall?.copyWith(color: context.isDark ? null : AppColor.neutral20, fontWeight: FontWeight.w500), | ||||
|                 ), | ||||
|                 if (_selectedItem.isEmpty) | ||||
|                   Text( | ||||
|                     context.translation.select, | ||||
|                     style: Theme.of(context).textTheme.bodyLarge, | ||||
|                   ) | ||||
|                 else | ||||
|                   ListView.separated( | ||||
|                     shrinkWrap: true, | ||||
|                     physics: const NeverScrollableScrollPhysics(), | ||||
|                     padding: const EdgeInsets.only(top: 8), | ||||
|                     separatorBuilder: (cxt, index) => const Divider( | ||||
|                       thickness: 1, | ||||
|                       color: Colors.white, | ||||
|                     ), | ||||
|                     itemBuilder: (cxt, index) => Text( | ||||
|                       _selectedItem[index].name ?? "", // Null-aware operator for value.name | ||||
|                       maxLines: 1, | ||||
|                       overflow: TextOverflow.ellipsis, | ||||
|                       style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w500, color: context.isDark ? AppColor.white10 : AppColor.black10), | ||||
|                     ), | ||||
|                     itemCount: _selectedItem.length, | ||||
|                   ), | ||||
|               ], | ||||
|             ) | ||||
|                 .onPress( | ||||
|                   widget.enabled && widget.showAsFullScreenDialog | ||||
|                       ? () { | ||||
|                           openDialog(); | ||||
|                         } | ||||
|                       : null, | ||||
|                 ) | ||||
|                 .expanded, | ||||
|             if (widget.enabled) const PositionedDirectional(end: 0, child: Icon(Icons.keyboard_arrow_down_rounded)), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   void openDialog() async { | ||||
|     Widget child = MultipleSelectionFullScreenDialog<T>( | ||||
|       // Specify generic type | ||||
|       items: ((X == NullableLoadingProvider) ? widget.staticData : provider?.items as List<T>) ?? [], | ||||
|       // Provide default empty list if null | ||||
|       selectedItem: _selectedItem, | ||||
|       disableItem: widget.disableValue, | ||||
|       title: widget.title, | ||||
|       showCancel: widget.showCancel, | ||||
|       onSelect: (selectedT) { | ||||
|         setState(() { | ||||
|           _selectedItem = selectedT; | ||||
|         }); | ||||
|         widget.onSelect!(selectedT); | ||||
|       }, | ||||
|       builderString: (emp) => emp?.name ?? "", // Null-aware operator for emp.name | ||||
|     ); | ||||
|     pushRouter(child); | ||||
|     // final selectedT = await pushRouter(child); | ||||
|     // if (selectedT != null) { | ||||
|     //   setState(() { | ||||
|     //     _selectedItem = selectedT; | ||||
|     //   }); | ||||
|     //   widget.onSelect!(selectedT); // Non-null assertion after null check | ||||
|     // } | ||||
|   } | ||||
| 
 | ||||
|   Future<T?> pushRouter(Widget child) async { | ||||
|     // PageRoute<T> pRoute = !Platform.isIOS ? CupertinoPageRoute(fullscreenDialog: true, builder: (context) => child) : MaterialPageRoute(fullscreenDialog: true, builder: (context) => child); | ||||
| 
 | ||||
|     return await Navigator.of(context).push(CupertinoPageRoute(fullscreenDialog: true, builder: (context) => child)); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,33 @@ | ||||
| import 'dart:convert'; | ||||
| 
 | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:test_sa/providers/loading_list_notifier.dart'; | ||||
| 
 | ||||
| import '../../controllers/api_routes/api_manager.dart'; | ||||
| import '../../controllers/api_routes/urls.dart'; | ||||
| import '../../models/lookup.dart'; | ||||
| 
 | ||||
| class ClassificationLookupProvider extends LoadingListNotifier<Lookup> { | ||||
|   @override | ||||
|   Future getData({int? id}) async { | ||||
|     if (loading == true) return -2; | ||||
|     loading = true; | ||||
|     notifyListeners(); | ||||
|     try { | ||||
|       Response response = await ApiManager.instance.get(URLs.getClassificationTypeLookup); | ||||
|       stateCode = response.statusCode; | ||||
|       if (response.statusCode >= 200 && response.statusCode < 300) { | ||||
|         List categoriesListJson = json.decode(response.body)["data"]; | ||||
|         items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList(); | ||||
|       } | ||||
|       loading = false; | ||||
|       notifyListeners(); | ||||
|       return response.statusCode; | ||||
|     } catch (error) { | ||||
|       loading = false; | ||||
|       stateCode = -1; | ||||
|       notifyListeners(); | ||||
|       return -1; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,34 @@ | ||||
| import 'dart:convert'; | ||||
| 
 | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:test_sa/models/new_models/traf_department.dart'; | ||||
| import 'package:test_sa/providers/loading_list_notifier.dart'; | ||||
| 
 | ||||
| import '../../controllers/api_routes/api_manager.dart'; | ||||
| import '../../controllers/api_routes/urls.dart'; | ||||
| import '../../models/lookup.dart'; | ||||
| 
 | ||||
| class DepartmentLookupProvider extends LoadingListNotifier<TrafDepartment> { | ||||
|   @override | ||||
|   Future getData({int? id}) async { | ||||
|     if (loading == true) return -2; | ||||
|     loading = true; | ||||
|     notifyListeners(); | ||||
|     try { | ||||
|       Response response = await ApiManager.instance.get(URLs.getDepartmentBasedOnSite + "?customerId=$id"); | ||||
|       stateCode = response.statusCode; | ||||
|       if (response.statusCode >= 200 && response.statusCode < 300) { | ||||
|         List categoriesListJson = json.decode(response.body)["data"]; | ||||
|         items = categoriesListJson.map((item) => TrafDepartment.fromJson(item)).toList(); | ||||
|       } | ||||
|       loading = false; | ||||
|       notifyListeners(); | ||||
|       return response.statusCode; | ||||
|     } catch (error) { | ||||
|       loading = false; | ||||
|       stateCode = -1; | ||||
|       notifyListeners(); | ||||
|       return -1; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,33 @@ | ||||
| import 'dart:convert'; | ||||
| 
 | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:test_sa/providers/loading_list_notifier.dart'; | ||||
| 
 | ||||
| import '../../controllers/api_routes/api_manager.dart'; | ||||
| import '../../controllers/api_routes/urls.dart'; | ||||
| import '../../models/lookup.dart'; | ||||
| 
 | ||||
| class RecommendationLookupProvider extends LoadingListNotifier<Lookup> { | ||||
|   @override | ||||
|   Future getData({int? id}) async { | ||||
|     if (loading == true) return -2; | ||||
|     loading = true; | ||||
|     notifyListeners(); | ||||
|     try { | ||||
|       Response response = await ApiManager.instance.get(URLs.getRecommendationTypeLookup); | ||||
|       stateCode = response.statusCode; | ||||
|       if (response.statusCode >= 200 && response.statusCode < 300) { | ||||
|         List categoriesListJson = json.decode(response.body)["data"]; | ||||
|         items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList(); | ||||
|       } | ||||
|       loading = false; | ||||
|       notifyListeners(); | ||||
|       return response.statusCode; | ||||
|     } catch (error) { | ||||
|       loading = false; | ||||
|       stateCode = -1; | ||||
|       notifyListeners(); | ||||
|       return -1; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,33 @@ | ||||
| import 'dart:convert'; | ||||
| 
 | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:test_sa/providers/loading_list_notifier.dart'; | ||||
| 
 | ||||
| import '../../controllers/api_routes/api_manager.dart'; | ||||
| import '../../controllers/api_routes/urls.dart'; | ||||
| import '../../models/lookup.dart'; | ||||
| 
 | ||||
| class RequestTypeLookupProvider extends LoadingListNotifier<Lookup> { | ||||
|   @override | ||||
|   Future getData({int? id}) async { | ||||
|     if (loading == true) return -2; | ||||
|     loading = true; | ||||
|     notifyListeners(); | ||||
|     try { | ||||
|       Response response = await ApiManager.instance.get(URLs.getTrafRequestTypeLookup); | ||||
|       stateCode = response.statusCode; | ||||
|       if (response.statusCode >= 200 && response.statusCode < 300) { | ||||
|         List categoriesListJson = json.decode(response.body)["data"]; | ||||
|         items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList(); | ||||
|       } | ||||
|       loading = false; | ||||
|       notifyListeners(); | ||||
|       return response.statusCode; | ||||
|     } catch (error) { | ||||
|       loading = false; | ||||
|       stateCode = -1; | ||||
|       notifyListeners(); | ||||
|       return -1; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,33 @@ | ||||
| import 'dart:convert'; | ||||
| 
 | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:test_sa/providers/loading_list_notifier.dart'; | ||||
| 
 | ||||
| import '../../controllers/api_routes/api_manager.dart'; | ||||
| import '../../controllers/api_routes/urls.dart'; | ||||
| import '../../models/lookup.dart'; | ||||
| 
 | ||||
| class YesNoLookupProvider extends LoadingListNotifier<Lookup> { | ||||
|   @override | ||||
|   Future getData({int? id}) async { | ||||
|     if (loading == true) return -2; | ||||
|     loading = true; | ||||
|     notifyListeners(); | ||||
|     try { | ||||
|       Response response = await ApiManager.instance.get(URLs.getYesNoRequestTypeLookup); | ||||
|       stateCode = response.statusCode; | ||||
|       if (response.statusCode >= 200 && response.statusCode < 300) { | ||||
|         List categoriesListJson = json.decode(response.body)["data"]; | ||||
|         items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList(); | ||||
|       } | ||||
|       loading = false; | ||||
|       notifyListeners(); | ||||
|       return response.statusCode; | ||||
|     } catch (error) { | ||||
|       loading = false; | ||||
|       stateCode = -1; | ||||
|       notifyListeners(); | ||||
|       return -1; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,172 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:test_sa/extensions/context_extension.dart'; | ||||
| import 'package:test_sa/extensions/int_extensions.dart'; | ||||
| import 'package:test_sa/extensions/text_extensions.dart'; | ||||
| import 'package:test_sa/extensions/widget_extensions.dart'; | ||||
| import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; | ||||
| import 'package:test_sa/new_views/app_style/app_color.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; | ||||
| 
 | ||||
| typedef SelectionBuilderString = String Function(dynamic); | ||||
| 
 | ||||
| class MultipleSelectionBottomSheet<T> extends StatefulWidget { | ||||
|   final List<T>? items; | ||||
|   final List<T> selectedItem; // Now nullable | ||||
|   final String title; | ||||
|   final SelectionBuilderString builderString; | ||||
|   final bool showCancel; | ||||
|   final Function(List<T>) onSelect; | ||||
| 
 | ||||
|   const MultipleSelectionBottomSheet({Key? key, this.items = const [], this.selectedItem = const [], this.title = "", required this.builderString, this.showCancel = false, required this.onSelect}) | ||||
|       : super(key: key); | ||||
| 
 | ||||
|   @override | ||||
|   _MultipleSelectionBottomSheetState createState() => _MultipleSelectionBottomSheetState<T>(); | ||||
| } | ||||
| 
 | ||||
| class _MultipleSelectionBottomSheetState<T> extends State<MultipleSelectionBottomSheet<T>> { | ||||
|   late List<T> _selectedValue; // Now nullable | ||||
| 
 | ||||
|   String query = ""; | ||||
| 
 | ||||
|   List<T>? get filteredList => widget.items?.where((element) => widget.builderString(element).toLowerCase().contains(query.toLowerCase())).toList(); | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     _selectedValue = widget.selectedItem; | ||||
|     super.initState(); | ||||
|   } | ||||
| 
 | ||||
|   FocusNode searchFocusNode = FocusNode(); | ||||
| 
 | ||||
|   @override | ||||
|   void dispose() { | ||||
|     searchFocusNode.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Container( | ||||
|       height: MediaQuery.of(context).size.height * .7, | ||||
|       color: Theme.of(context).scaffoldBackgroundColor, | ||||
|       child: Column( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           Row( | ||||
|             children: [ | ||||
|               widget.title.heading5(context).expanded, | ||||
|               if (widget.showCancel) | ||||
|                 AnimatedOpacity( | ||||
|                   opacity: _selectedValue != null ? 1 : 0, | ||||
|                   duration: const Duration(milliseconds: 250), | ||||
|                   child: Container( | ||||
|                     height: 30, | ||||
|                     decoration: BoxDecoration(color: const Color(0xffF63939).withOpacity(.75), borderRadius: BorderRadius.circular(30)), | ||||
|                     padding: const EdgeInsets.only(left: 8, right: 12, top: 4, bottom: 4), | ||||
|                     alignment: Alignment.center, | ||||
|                     child: Row( | ||||
|                       mainAxisSize: MainAxisSize.min, | ||||
|                       children: [ | ||||
|                         const Icon(Icons.clear, color: Colors.white, size: 16), | ||||
|                         4.width, | ||||
|                         const Text( | ||||
|                           "Clear", | ||||
|                           style: TextStyle(fontSize: 14, color: Colors.white), | ||||
|                         ) | ||||
|                       ], | ||||
|                     ), | ||||
|                   ).onPress(_selectedValue.isNotEmpty | ||||
|                       ? () { | ||||
|                           Navigator.pop(context); | ||||
|                           widget.onSelect([]); | ||||
|                         } | ||||
|                       : null), | ||||
|                 ), | ||||
|             ], | ||||
|           ).paddingOnly(top: 16, start: 16, end: 16, bottom: 0), | ||||
|           TextField( | ||||
|             onChanged: (queryString) { | ||||
|               query = queryString; | ||||
|               setState(() {}); | ||||
|             }, | ||||
|             style: const TextStyle(fontSize: 14), | ||||
|             focusNode: searchFocusNode, | ||||
|             decoration: InputDecoration( | ||||
|               hintText: 'Search by name', | ||||
|               labelText: 'Search', | ||||
|               labelStyle: TextStyle(color: AppColor.textColor(context)), | ||||
|               filled: true, | ||||
|               fillColor: AppColor.fieldBgColor(context), | ||||
|               hintStyle: const TextStyle(fontSize: 14), | ||||
|               focusedBorder: OutlineInputBorder( | ||||
|                 borderSide: BorderSide(color: AppColor.blueStatus(context), width: 2.0), | ||||
|                 borderRadius: const BorderRadius.all(Radius.circular(12.0)), | ||||
|               ), | ||||
|               enabledBorder: OutlineInputBorder( | ||||
|                 borderSide: BorderSide(color: AppColor.blueStatus(context), width: 1.0), | ||||
|                 borderRadius: const BorderRadius.all(Radius.circular(12.0)), | ||||
|               ), | ||||
|               contentPadding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), | ||||
|             ), | ||||
|           ).paddingOnly(top: 16, start: 16, end: 16, bottom: 16), | ||||
|           Expanded( | ||||
|             // Wrap ListView with Expanded | ||||
|             child: ListView.builder( | ||||
|               itemCount: filteredList?.length, | ||||
|               padding: EdgeInsets.zero, | ||||
|               itemBuilder: (cxt, index) => Theme( | ||||
|                 data: Theme.of(context).copyWith( | ||||
|                   radioTheme: RadioThemeData( | ||||
|                     fillColor: MaterialStateColor.resolveWith((states) { | ||||
|                       if (states.contains(MaterialState.selected)) { | ||||
|                         return AppColor.textColor(context); // Active color | ||||
|                       } | ||||
|                       return Colors.grey; // Inactive color | ||||
|                     }), | ||||
|                   ), | ||||
|                 ), | ||||
|                 child: CheckboxListTile( | ||||
|                   value: checkItContains(filteredList![index]), | ||||
|                   dense: true, | ||||
|                   // groupValue: _selectedValue, | ||||
|                   activeColor: AppColor.textColor(context), | ||||
|                   contentPadding: const EdgeInsets.only(left: 16, right: 16), | ||||
|                   onChanged: (value) { | ||||
|                     if (value == true) { | ||||
|                       _selectedValue.add(filteredList![index]); | ||||
|                     } else if (value == false) { | ||||
|                       _selectedValue.remove(filteredList![index]); | ||||
|                     } | ||||
|                     searchFocusNode.unfocus(); | ||||
|                     setState(() {}); | ||||
|                   }, | ||||
|                   title: Text( | ||||
|                     widget.builderString(filteredList![index]).cleanupWhitespace.capitalizeFirstOfEach ?? "", | ||||
|                     style: Theme.of(context).textTheme.bodyLarge, | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
|           8.height, | ||||
|           if (_selectedValue.isNotEmpty) | ||||
|             FooterActionButton.footerContainer( | ||||
|                 context: context, | ||||
|                 child: AppFilledButton( | ||||
|                   label: context.translation.select, | ||||
|                   maxWidth: true, | ||||
|                   onPressed: () { | ||||
|                     Navigator.pop(context); | ||||
|                     widget.onSelect(_selectedValue); | ||||
|                   }, | ||||
|                 )), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   bool? checkItContains(T) { | ||||
|     return widget.items?.contains(T); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,193 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:test_sa/extensions/context_extension.dart'; | ||||
| import 'package:test_sa/extensions/int_extensions.dart'; | ||||
| import 'package:test_sa/extensions/text_extensions.dart'; | ||||
| import 'package:test_sa/extensions/widget_extensions.dart'; | ||||
| import 'package:test_sa/models/base.dart'; | ||||
| import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; | ||||
| import 'package:test_sa/new_views/app_style/app_color.dart'; | ||||
| import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; | ||||
| 
 | ||||
| typedef SelectionBuilderString = String Function(dynamic); | ||||
| 
 | ||||
| class MultipleSelectionFullScreenDialog<T extends Base> extends StatefulWidget { | ||||
|   final List<T>? items; | ||||
|   final List<T> selectedItem; // Now nullable | ||||
|   final T? disableItem; // Now nullable | ||||
|   final String title; | ||||
|   final bool showCancel; | ||||
|   final SelectionBuilderString builderString; | ||||
|   final Function(List<T>) onSelect; | ||||
| 
 | ||||
|   const MultipleSelectionFullScreenDialog( | ||||
|       {Key? key, this.items, this.selectedItem = const [], this.disableItem, this.title = "", required this.builderString, this.showCancel = false, required this.onSelect}) | ||||
|       : super(key: key); | ||||
| 
 | ||||
|   @override | ||||
|   _SelectionBottomSheetState createState() => _SelectionBottomSheetState<T>(); | ||||
| } | ||||
| 
 | ||||
| class _SelectionBottomSheetState<T extends Base> extends State<MultipleSelectionFullScreenDialog<T>> { | ||||
|   late List<T> _selectedValue; // Now nullable | ||||
| 
 | ||||
|   String query = ""; | ||||
| 
 | ||||
|   List<T>? get filteredList => widget.items?.where((element) => widget.builderString(element).toLowerCase().contains(query.toLowerCase())).toList(); | ||||
| 
 | ||||
|   @override | ||||
|   void initState() { | ||||
|     _selectedValue = widget.selectedItem; | ||||
|     super.initState(); | ||||
|   } | ||||
| 
 | ||||
|   FocusNode searchFocusNode = FocusNode(); | ||||
| 
 | ||||
|   @override | ||||
|   void dispose() { | ||||
|     searchFocusNode.dispose(); | ||||
|     super.dispose(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         title: widget.title.heading5(context), | ||||
|         actions: [ | ||||
|           if (widget.showCancel) | ||||
|             AnimatedContainer( | ||||
|               height: 24, | ||||
|               duration: const Duration(milliseconds: 250), | ||||
|               margin: const EdgeInsets.only(right: 16), | ||||
|               decoration: BoxDecoration(color: _selectedValue.isNotEmpty ? const Color(0xffF63939).withOpacity(.75) : Colors.grey.withOpacity(.75), borderRadius: BorderRadius.circular(30)), | ||||
|               padding: const EdgeInsets.only(left: 4, right: 8, top: 4, bottom: 4), | ||||
|               alignment: Alignment.center, | ||||
|               child: Row( | ||||
|                 mainAxisSize: MainAxisSize.min, | ||||
|                 crossAxisAlignment: CrossAxisAlignment.center, | ||||
|                 children: [ | ||||
|                   const Icon(Icons.clear, color: Colors.white, size: 16), | ||||
|                   4.width, | ||||
|                   const Text( | ||||
|                     "Clear", | ||||
|                     style: TextStyle(fontSize: 14, color: Colors.white, height: 1), | ||||
|                   ) | ||||
|                 ], | ||||
|               ).onPress(_selectedValue.isNotEmpty | ||||
|                   ? () { | ||||
|                       Navigator.pop(context); | ||||
|                       widget.onSelect([]); | ||||
|                     } | ||||
|                   : null), | ||||
|             ), | ||||
|         ], | ||||
|       ), | ||||
|       body: Column( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           SearchBar( | ||||
|             focusNode: searchFocusNode, | ||||
|             elevation: WidgetStateProperty.all<double>(0), | ||||
|             backgroundColor: WidgetStateProperty.all<Color?>(context.isDark ? AppColor.neutral120 : null), | ||||
|             leading: Icon(Icons.search, color: AppColor.iconColor(context)), | ||||
|             textStyle: WidgetStateProperty.all<TextStyle>( | ||||
|               TextStyle(color: AppColor.textColor(context), fontSize: 16.0), | ||||
|             ), | ||||
|             hintStyle: WidgetStateProperty.all<TextStyle>( | ||||
|               TextStyle(color: AppColor.textColor(context), fontSize: 14.0), | ||||
|             ), | ||||
|             hintText: 'Search by name', | ||||
|             onChanged: (queryString) { | ||||
|               query = queryString; | ||||
|               setState(() {}); | ||||
|             }, | ||||
|           ).paddingOnly(top: 16, start: 16, end: 16, bottom: 8), | ||||
|           // TextField( | ||||
|           //   onChanged: (queryString) { | ||||
|           //     query = queryString; | ||||
|           //     setState(() {}); | ||||
|           //   }, | ||||
|           //   style: const TextStyle(fontSize: 14), | ||||
|           //   focusNode: searchFocusNode, | ||||
|           //   decoration: InputDecoration( | ||||
|           //     hintText: 'Search by name', | ||||
|           //     labelText: 'Search', | ||||
|           //     hintStyle: const TextStyle(fontSize: 14), | ||||
|           //     focusedBorder: OutlineInputBorder( | ||||
|           //       borderSide: BorderSide(color: AppColor.blueStatus(context), width: 2.0), | ||||
|           //       borderRadius: const BorderRadius.all(Radius.circular(12.0)), | ||||
|           //     ), | ||||
|           //     enabledBorder: OutlineInputBorder( | ||||
|           //       borderSide: BorderSide(color: AppColor.blueStatus(context), width: 1.0), | ||||
|           //       borderRadius: const BorderRadius.all(Radius.circular(12.0)), | ||||
|           //     ), | ||||
|           //     contentPadding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), | ||||
|           //   ), | ||||
|           // ), | ||||
|           8.height, | ||||
|           Expanded( | ||||
|             child: ListView.builder( | ||||
|                 itemCount: filteredList?.length, | ||||
|                 padding: EdgeInsets.zero, | ||||
|                 itemBuilder: (cxt, index) { | ||||
|                   bool isDisabledItem = widget.disableItem != null && widget.disableItem?.identifier == filteredList![index].identifier; | ||||
|                   return Theme( | ||||
|                     data: Theme.of(context).copyWith( | ||||
|                       radioTheme: RadioThemeData( | ||||
|                         fillColor: MaterialStateColor.resolveWith((states) { | ||||
|                           if (states.contains(MaterialState.selected)) { | ||||
|                             return AppColor.iconColor(context); // Active color | ||||
|                           } | ||||
|                           return Colors.grey; // Inactive color | ||||
|                         }), | ||||
|                       ), | ||||
|                     ), | ||||
|                     child: CheckboxListTile( | ||||
|                       value: checkItContains(filteredList![index]), | ||||
|                       dense: true, | ||||
|                       controlAffinity: ListTileControlAffinity.leading, | ||||
|                       activeColor: AppColor.textColor(context), | ||||
|                       contentPadding: const EdgeInsets.only(left: 16, right: 16), | ||||
|                       onChanged: (value) { | ||||
|                         // if (checkItContains(filteredList![index]) ?? false) { | ||||
|                         //   _selectedValue.remove(filteredList![index]); | ||||
|                         // } else { | ||||
|                         //   _selectedValue.add(filteredList![index]); | ||||
|                         // } | ||||
|                         if (value == true) { | ||||
|                           _selectedValue.add(filteredList![index]); | ||||
|                         } else if (value == false) { | ||||
|                           _selectedValue.remove(filteredList![index]); | ||||
|                         } | ||||
|                         searchFocusNode.unfocus(); | ||||
|                         setState(() {}); | ||||
|                       }, | ||||
|                       title: Text( | ||||
|                         widget.builderString(filteredList![index]).cleanupWhitespace.capitalizeFirstOfEach ?? "", | ||||
|                         style: Theme.of(context).textTheme.bodyLarge, | ||||
|                       ), | ||||
|                     ), | ||||
|                   ); | ||||
|                 }), | ||||
|           ), | ||||
|           8.height, | ||||
|           if (_selectedValue.isNotEmpty) | ||||
|             FooterActionButton.footerContainer( | ||||
|                 context: context, | ||||
|                 child: AppFilledButton( | ||||
|                   label: context.translation.select, | ||||
|                   maxWidth: true, | ||||
|                   onPressed: () { | ||||
|                     Navigator.pop(context); | ||||
|                     widget.onSelect(_selectedValue); | ||||
|                   }, | ||||
|                 )), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   bool? checkItContains(T) { | ||||
|     return _selectedValue.contains(T); | ||||
|   } | ||||
| } | ||||
					Loading…
					
					
				
		Reference in New Issue