diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index c9cb187d..645db97d 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -632,8 +632,10 @@ class URLs { // Asset Tracking APIs static get getAssetsFromPenguin => "$_baseUrl/PenguinIN/GetAssetsFromPenguin"; - static get getPenguinFloors => "$_baseUrl/PenguinIN/floors/"; + static get getPenguinSites => "$_baseUrl/PenguinIN/sites/"; - static get getPenguinBuildings => "$_baseUrl/PenguinIN/buildings/"; - static get getPenguinDepartments => "$_baseUrl/PenguinIN/departments/"; + static get getPenguinDepartments => "$_baseUrl/PenguinIN/departments"; + static get getPenguinBuildings => "$_baseUrl/PenguinIN/buildings"; + static get getPenguinFloors => "$_baseUrl/PenguinIN/floors"; + } diff --git a/lib/modules/asset_tracking/asset_tracking_item_view.dart b/lib/modules/asset_tracking/asset_tracking_item_view.dart new file mode 100644 index 00000000..4b24b73a --- /dev/null +++ b/lib/modules/asset_tracking/asset_tracking_item_view.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/views/widgets/item_views/info_header_widget.dart'; +import 'package:test_sa/views/widgets/item_views/info_text_widget.dart'; +import 'package:test_sa/views/widgets/requests/request_status.dart'; + +import 'models/asset_tracking_model.dart'; + +class AssetTrackingListItemView extends StatelessWidget { + final AssetTrackingModel asset; + + AssetTrackingListItemView(this.asset, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + StatusLabel( + label: (asset.location?.isLive ?? false) ? "Live" : "Not Live", + radius: 4, + textColor: AppColor.getPriorityStatusTextColor(context, (asset.location?.isLive ?? false) ? 0 : 1), + backgroundColor: AppColor.getPriorityStatusColor(context, (asset.location?.isLive ?? false) ? 0 : 1), + ), + 4.height, + InfoHeader16Widget(asset.label ?? ""), + InfoTextWidget(label: "Serial No", value: asset.label, showEmptyValue: true), + InfoTextWidget(label: "Type", value: asset.type?.name, showEmptyValue: true), + InfoTextWidget(label: "Status", value: asset.status?.name, showEmptyValue: true), + InfoTextWidget(label: "Department", value: asset.department?.name, showEmptyValue: true), + InfoTextWidget( + label: "Last seen", + value: asset.location?.lastSeen?.toServiceRequestDetailsFormat, + showEmptyValue: true, + showArrow: true, + arrowMsg: "View on map", + ), + ], + ).toShadowContainer(context); + } +} diff --git a/lib/modules/asset_tracking/asset_tracking_page.dart b/lib/modules/asset_tracking/asset_tracking_page.dart index df7a0f89..d91081e7 100644 --- a/lib/modules/asset_tracking/asset_tracking_page.dart +++ b/lib/modules/asset_tracking/asset_tracking_page.dart @@ -18,6 +18,7 @@ import 'package:test_sa/views/widgets/item_views/info_text_widget.dart'; import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; import 'package:test_sa/views/widgets/requests/request_status.dart'; +import 'asset_tracking_item_view.dart'; import 'map_tracking_page.dart'; class AssetTrackingPage extends StatefulWidget { @@ -148,45 +149,12 @@ class _AssetTrackingPageState extends State with SingleTicker return FadeTransition( opacity: animation, - child: SlideTransition( - position: slideAnimation, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - StatusLabel( - label: (provider.assetsList[itemIndex].location?.isLive ?? false) ? "Live" : "Not Live", - radius: 4, - textColor: AppColor.getPriorityStatusTextColor(context, (provider.assetsList[itemIndex].location?.isLive ?? false) ? 0 : 1), - backgroundColor: AppColor.getPriorityStatusColor(context, (provider.assetsList[itemIndex].location?.isLive ?? false) ? 0 : 1), - ), - 4.height, - InfoHeader16Widget(provider.assetsList[itemIndex].label ?? ""), - InfoTextWidget(label: "Serial No", value: provider.assetsList[itemIndex].label, showEmptyValue: true), - InfoTextWidget(label: "Type", value: provider.assetsList[itemIndex].type?.name, showEmptyValue: true), - InfoTextWidget(label: "Status", value: provider.assetsList[itemIndex].status?.name, showEmptyValue: true), - InfoTextWidget(label: "Department", value: provider.assetsList[itemIndex].department?.name, showEmptyValue: true), - InfoTextWidget( - label: "Last seen", - value: provider.assetsList[itemIndex].location?.lastSeen?.toServiceRequestDetailsFormat, - showEmptyValue: true, - showArrow: true, - arrowMsg: "View on map", - ), - ], - ).toShadowContainer(context)) - .onPress(() { + child: SlideTransition(position: slideAnimation, child: AssetTrackingListItemView(provider.assetsList[itemIndex])).onPress(() { Navigator.push(context, MaterialPageRoute(builder: (context) => MapTrackingPage(assetTrackingData: provider.assetsList[itemIndex]))); }), ); }, ); }); - -// Selector( -// selector: (_, myModel) => myModel, // Selects only the userName -// builder: (_, isLoading, __) { -// -// }); } } diff --git a/lib/modules/asset_tracking/asset_tracking_provider.dart b/lib/modules/asset_tracking/asset_tracking_provider.dart index b580b655..6fbc64fc 100644 --- a/lib/modules/asset_tracking/asset_tracking_provider.dart +++ b/lib/modules/asset_tracking/asset_tracking_provider.dart @@ -6,6 +6,7 @@ import 'package:test_sa/controllers/api_routes/urls.dart'; import 'models/asset_tracking_model.dart'; import 'models/floor_map_model.dart'; +import 'models/penguin_site_model.dart'; class AssetTrackingProvider extends ChangeNotifier { bool isLoading = false; @@ -14,9 +15,30 @@ class AssetTrackingProvider extends ChangeNotifier { int pageNo = 1; List assetsList = []; + List assetsOnTheFloorList = []; + + List sitesList = []; + PenguinSiteModel? selectedSite; + + List buildList = []; + PenguinSiteModel? selectedBuilding; + List floorList = []; + PenguinSiteModel? selectedFloor; + + String get getExpansionHeader => + (selectedBuilding == null || selectedBuilding == null || selectedFloor == null) ? "Search Floor Map" : "${selectedSite!.label} > ${selectedBuilding!.label} > ${selectedFloor!.label}"; reset() { isLoading = false; + selectedFloor = null; + selectedBuilding = null; + selectedSite = null; + assetsList = []; + assetsOnTheFloorList = []; + notify(); + } + + notify() { notifyListeners(); } @@ -40,7 +62,6 @@ class AssetTrackingProvider extends ChangeNotifier { if (jsonData["totalRows"] <= assetsList.length) { // nextPage = true; } - print("assetsList:${assetsList.length}"); // if (loadMore) { // assetInventoryResponse?.assetList?.addAll(newResponse.assetList ?? []); // assetInventoryResponse?.totalRows = newResponse.totalRows; @@ -65,16 +86,104 @@ class AssetTrackingProvider extends ChangeNotifier { } } + Future getAssetsFromPenguinByFloorId(int floorId) async { + try { + assetsOnTheFloorList.clear(); + final response = await ApiManager.instance.post(URLs.getAssetsFromPenguin, body: { + "floorIds": [floorId] + }); + if (response.statusCode >= 200 && response.statusCode < 300) { + final Map jsonData = json.decode(response.body); + List list = jsonData["data"]; + assetsOnTheFloorList.addAll(list.map((item) => AssetTrackingModel.fromJson(item)).toList()); + } + notifyListeners(); + } catch (error) { + notifyListeners(); + } + } + Future getFloorMap(int floorId) async { FloorMapModel? floorMapModel; try { - final response = await ApiManager.instance.get(URLs.getPenguinFloors + "$floorId/maps"); + final response = await ApiManager.instance.get(URLs.getPenguinFloors + "/$floorId/maps"); if (response.statusCode >= 200 && response.statusCode < 300) { floorMapModel = FloorMapModel.fromJson(json.decode(response.body)["data"]); + final geoJsonResponse = await ApiManager.instance.get(floorMapModel.geojsonUrl!); + floorMapModel.geojsonData = json.decode(geoJsonResponse.body); } return floorMapModel; } catch (error) { return floorMapModel; } } + + Future getPenguinSites() async { + try { + sitesList.clear(); + buildList.clear(); + floorList.clear(); + selectedSite = null; + selectedBuilding = null; + selectedFloor = null; + final response = await ApiManager.instance.get(URLs.getPenguinSites); + if (response.statusCode >= 200 && response.statusCode < 300) { + final Map jsonData = json.decode(response.body); + List list = jsonData["data"]; + sitesList.addAll(list.map((item) => PenguinSiteModel.fromJson(item)).toList()); + } + notifyListeners(); + } catch (error) { + notifyListeners(); + } + } + + Future> getPenguinDepartments(int id) async { + List sitesList = []; + try { + final response = await ApiManager.instance.get(URLs.getPenguinDepartments + "?siteId=$id"); + if (response.statusCode >= 200 && response.statusCode < 300) { + final Map jsonData = json.decode(response.body); + List list = jsonData["data"]; + sitesList.addAll(list.map((item) => PenguinSiteModel.fromJson(item)).toList()); + } + return sitesList; + } catch (error) { + return sitesList; + } + } + + Future getPenguinBuildings(int id) async { + try { + buildList.clear(); + floorList.clear(); + selectedBuilding = null; + selectedFloor = null; + final response = await ApiManager.instance.get(URLs.getPenguinBuildings + "?siteId=$id"); + if (response.statusCode >= 200 && response.statusCode < 300) { + final Map jsonData = json.decode(response.body); + List list = jsonData["data"]; + buildList.addAll(list.map((item) => PenguinSiteModel.fromJson(item)).toList()); + } + notifyListeners(); + } catch (error) { + notifyListeners(); + } + } + + Future getPenguinFloors(int id) async { + try { + floorList.clear(); + selectedFloor = null; + final response = await ApiManager.instance.get(URLs.getPenguinFloors + "?buildingId=$id"); + if (response.statusCode >= 200 && response.statusCode < 300) { + final Map jsonData = json.decode(response.body); + List list = jsonData["data"]; + floorList.addAll(list.map((item) => PenguinSiteModel.fromJson(item)).toList()); + } + notifyListeners(); + } catch (error) { + notifyListeners(); + } + } } diff --git a/lib/modules/asset_tracking/map_tracking_page.dart b/lib/modules/asset_tracking/map_tracking_page.dart index 2607da2f..ea059f8f 100644 --- a/lib/modules/asset_tracking/map_tracking_page.dart +++ b/lib/modules/asset_tracking/map_tracking_page.dart @@ -5,15 +5,20 @@ import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_geojson2/flutter_map_geojson2.dart'; import 'package:latlong2/latlong.dart'; import 'package:provider/provider.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/helper/utils.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.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/views/widgets/item_views/info_text_widget.dart'; +import 'asset_tracking_item_view.dart'; import 'asset_tracking_provider.dart'; import 'models/asset_tracking_model.dart'; import 'models/floor_map_model.dart'; +import 'models/penguin_site_model.dart'; class MapTrackingPage extends StatefulWidget { final AssetTrackingModel? assetTrackingData; @@ -35,6 +40,7 @@ class _MapTrackingPageState extends State { FloorMapModel? floorMapModel; Map? _geojson; List markers = []; + LatLngBounds? _computedBounds; final ExpansibleController expansibleController = ExpansibleController(); @@ -48,10 +54,30 @@ class _MapTrackingPageState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { if (!inAppView) { getFloorMap(widget.assetTrackingData!.location!.floor!.id!); + } else { + getPenguinSites(); } }); } + void getPenguinSites() async { + Utils.showLoading(context); + await assetTrackingProvider.getPenguinSites(); + Utils.hideLoading(context); + } + + void getPenguinBuildings(int id) async { + Utils.showLoading(context); + await assetTrackingProvider.getPenguinBuildings(id); + Utils.hideLoading(context); + } + + void getPenguinFloors(int id) async { + Utils.showLoading(context); + await assetTrackingProvider.getPenguinFloors(id); + Utils.hideLoading(context); + } + void getFloorMap(int id) { Utils.showLoading(context); assetTrackingProvider.getFloorMap(id).then((floorMap) { @@ -62,6 +88,10 @@ class _MapTrackingPageState extends State { }); } + void getAssetsOnTheFloor(int id) { + assetTrackingProvider.getAssetsFromPenguinByFloorId(id); + } + void _onFloorMapLoaded(FloorMapModel? loadedFloorMap) { floorMapModel = loadedFloorMap; @@ -133,6 +163,11 @@ class _MapTrackingPageState extends State { ); } + double _dragPosition = 0.5; + late double _sheetPosition = _dragPosition; + final minChildSize = 0.25; + final maxChildSize = 1.0; + @override Widget build(BuildContext context) { Widget mapWidget = FlutterMap( @@ -149,6 +184,25 @@ class _MapTrackingPageState extends State { styleDefaults: GeoJsonStyleDefaults.leaflet, ), if (!inAppView) MarkerLayer(markers: markers), + Selector>( + selector: (_, myModel) => myModel.assetsOnTheFloorList, + builder: (_, assetsOnTheFloorList, __) { + return assetsOnTheFloorList.isEmpty + ? const SizedBox() + : DraggableScrollableSheet( + initialChildSize: _sheetPosition, + builder: (BuildContext context, ScrollController scrollController) { + return Flexible( + child: ListView.builder( + controller: scrollController, + itemCount: assetsOnTheFloorList.length, + itemBuilder: (BuildContext context, int index) { + return AssetTrackingListItemView(assetsOnTheFloorList[index]); + }, + ), + ); + }); + }), ] ], ); @@ -168,22 +222,75 @@ class _MapTrackingPageState extends State { ), child: Theme( data: ThemeData(splashColor: Colors.transparent, hoverColor: Colors.transparent, highlightColor: Colors.transparent), - child: ExpansionTile( - shape: Border.all(color: Colors.transparent, width: 0), - collapsedShape: Border.all(color: Colors.transparent, width: 0), - controller: expansibleController, - title: const InfoTextLabelWidget(label: "Search Floor Map"), - iconColor: AppColor.icon2Color(context), - collapsedIconColor: AppColor.icon2Color(context), - dense: true, - children: [ - - ], - childrenPadding: EdgeInsets.all(0), - tilePadding: EdgeInsets.all(0), - ), + child: Consumer(builder: (context, assetProvider, _) { + return ExpansionTile( + shape: Border.all(color: Colors.transparent, width: 0), + collapsedShape: Border.all(color: Colors.transparent, width: 0), + controller: expansibleController, + title: InfoTextLabelWidget(label: assetProvider.getExpansionHeader), + iconColor: AppColor.icon2Color(context), + collapsedIconColor: AppColor.icon2Color(context), + dense: true, + childrenPadding: const EdgeInsets.all(0), + tilePadding: const EdgeInsets.all(0), + children: [ + SingleItemDropDownMenu( + context: context, + title: "Select Site", + staticData: assetProvider.sitesList, + initialValue: assetProvider.selectedSite, + showShadow: false, + // backgroundColor: AppColor.fieldBgColor(context), + showAsBottomSheet: true, + onSelect: (value) { + if (value != null) { + assetProvider.selectedSite = value; + getPenguinBuildings(value.id!); + } + }, + ), + 4.height, + SingleItemDropDownMenu( + context: context, + title: "Select Building", + staticData: assetProvider.buildList, + initialValue: assetProvider.selectedBuilding, + showShadow: false, + enabled: assetProvider.selectedSite != null, + // backgroundColor: AppColor.fieldBgColor(context), + showAsBottomSheet: true, + onSelect: (value) { + if (value != null) { + assetProvider.selectedBuilding = value; + getPenguinFloors(value.id!); + } + }, + ), + 4.height, + SingleItemDropDownMenu( + context: context, + title: "Select Floor", + staticData: assetProvider.floorList, + initialValue: assetProvider.selectedFloor, + showShadow: false, + enabled: assetProvider.selectedBuilding != null, + // backgroundColor: AppColor.fieldBgColor(context), + showAsBottomSheet: true, + onSelect: (value) { + if (value != null) { + assetProvider.selectedFloor = value; + expansibleController.collapse(); + getFloorMap(value.id!); + getAssetsOnTheFloor(value.id!); + } + }, + ), + 16.height, + ], + ); + }), ), - ), + ) ], ) : Scaffold(appBar: const DefaultAppBar(title: "Live Asset Tracking"), body: mapWidget); diff --git a/lib/modules/asset_tracking/models/penguin_site_model.dart b/lib/modules/asset_tracking/models/penguin_site_model.dart new file mode 100644 index 00000000..cbb387ef --- /dev/null +++ b/lib/modules/asset_tracking/models/penguin_site_model.dart @@ -0,0 +1,25 @@ +import 'package:test_sa/models/base.dart'; + +class PenguinSiteModel extends Base { + int? id; + String? label; + bool? isMapped; + + PenguinSiteModel({this.id, this.label}) : super(identifier: id?.toString(), name: label); + + PenguinSiteModel.fromJson(Map json) { + id = json['id']; + identifier = json['id']?.toString(); + label = json['label']; + name = json['label']; + isMapped = json['isMapped']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['label'] = this.label; + data['isMapped'] = this.isMapped; + return data; + } +}