diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 3fa70f2c..9f4f12c3 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -89,7 +89,7 @@ class URLs { static get getAssets => "$_baseUrl/Asset/GetAssets"; // get static get getFaultDescription => "$_baseUrl/Asset/GetDefectForModelDefinition"; // get static get getAssetById => "$_baseUrl/Asset/GetAssetById?assetId="; // get - static get assignTagToAsset => "$_baseUrl/Asset/AssignTagToAsset'"; // get + static get assignTagToAsset => "$_baseUrl/Asset/AssignTagToAsset"; // get static get getAssetColumnsMetadata => "$_baseUrl/Asset/columns/metadata"; // get asset export columns metadata static get saveAssetImages => "$_baseUrl/Asset/SaveAssetImages"; // get static get getModels => "$_baseUrl/ModelDefinition/GetModelDefinitionAsset"; // get ?client=2 diff --git a/lib/controllers/providers/api/devices_provider.dart b/lib/controllers/providers/api/devices_provider.dart index f3c85b5b..e2409311 100644 --- a/lib/controllers/providers/api/devices_provider.dart +++ b/lib/controllers/providers/api/devices_provider.dart @@ -258,17 +258,15 @@ class AssetProvider extends ChangeNotifier { } } - Future linkAssetWithPenguinTag(int assetId, int penguinAssetTagId) async { + Future linkAssetWithPenguinTag(int assetId, int penguinAssetTagId) async { try { final response = await ApiManager.instance.post(URLs.assignTagToAsset, body: {"assetId": assetId, "penguinAssetTagId": penguinAssetTagId}); if (response.statusCode >= 200 && response.statusCode < 300) { - final Map jsonData = json.decode(response.body); - List list = jsonData["data"]; - return list; + return true; } - return []; + return false; } catch (error) { - return []; + return false; } } } diff --git a/lib/modules/asset_tracking/asset_tracking_provider.dart b/lib/modules/asset_tracking/asset_tracking_provider.dart index f4b07add..e7dc5208 100644 --- a/lib/modules/asset_tracking/asset_tracking_provider.dart +++ b/lib/modules/asset_tracking/asset_tracking_provider.dart @@ -5,6 +5,7 @@ import 'package:test_sa/controllers/api_routes/api_manager.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/modules/asset_tracking/models/coordinates_model.dart'; import 'package:test_sa/modules/asset_tracking/models/asset_tracking_search_model.dart'; +import 'package:test_sa/views/widgets/equipment/asset_tag_model.dart'; import 'models/asset_tracking_model.dart'; import 'models/floor_map_model.dart'; @@ -260,19 +261,37 @@ class AssetTrackingProvider extends ChangeNotifier { } int assetTagPageNumber = 1; + int? totalAssetTags; + List tagList = []; - Future getPenguinAssetTags(int tagStatus) async { + bool isLoadingAssetTags = false; + + void resetAssetTagData() { + tagList.clear(); + assetTagPageNumber = 1; + totalAssetTags = null; + } + + bool get isCanLoadMore => tagList.length < (totalAssetTags ?? 0); + + Future getPenguinAssetTags() async { + if (isLoadingAssetTags) return; try { - final response = await ApiManager.instance.get(URLs.getPenguinAssetTags + "?pageNumber=$assetTagPageNumber&tagStatus=$tagStatus"); + isLoadingAssetTags = true; + notify(); + final response = await ApiManager.instance.get(URLs.getPenguinAssetTags + "?pageNumber=$assetTagPageNumber&tagStatus=1"); if (response.statusCode >= 200 && response.statusCode < 300) { final Map jsonData = json.decode(response.body); - List list = jsonData["data"]; - return list; + List list = jsonData["response"]["data"]; + totalAssetTags ??= jsonData["response"]["totalRows"]; + tagList.addAll(list.map((item) => AssetTagModel.fromJson(item)).toList()); + if (tagList.length < totalAssetTags!) { + assetTagPageNumber += 1; + } } - return []; - } catch (error) { - return []; - } + } catch (error) {} + isLoadingAssetTags = false; + notify(); } Future linkAssetWithPenguinTag(int assetId, int penguinTagId) async { diff --git a/lib/views/widgets/equipment/asset_detail_page.dart b/lib/views/widgets/equipment/asset_detail_page.dart index 882d0d71..87b3c113 100644 --- a/lib/views/widgets/equipment/asset_detail_page.dart +++ b/lib/views/widgets/equipment/asset_detail_page.dart @@ -349,7 +349,7 @@ class _AssetDetailPageState extends State with SingleTickerProv Row( mainAxisSize: MainAxisSize.min, children: [ - const InfoTextLabelWidget(label: '*Asset is not associated with tag', color: AppColor.red30).flexible, + const InfoTextLabelWidget(label: '*Asset not linked with tracking tag', color: AppColor.red30).flexible, Container( margin: const EdgeInsets.only(left: 12, right: 12), padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), @@ -359,15 +359,9 @@ class _AssetDetailPageState extends State with SingleTickerProv ), child: Text( "Link Tag", - style: AppTextStyles.bodyText2.copyWith(color: AppColor.textStyleColor(context)), + style: AppTextStyles.bodyText2.copyWith(color: AppColor.addItemButtonColor(context)), ), - ).onPress(() { - Navigator.of(context).push( - MaterialPageRoute( - builder: (_) => AssetTagListView(), - ), - ); - }), + ).onPress(_linkAssetTag), ], ), ] else ...[ @@ -444,4 +438,14 @@ class _AssetDetailPageState extends State with SingleTickerProv setState(() {}); } } + + void _linkAssetTag() async { + bool reload = + await Navigator.push(context, MaterialPageRoute(builder: (context) => AssetTagListView(assetId!, assetModel!.modelDefinition!.assetName!, assetModel!.multiAssets!.first.assetNumber!))) ?? + false; + if (reload) { + assetModel = null; + setState(() {}); + } + } } diff --git a/lib/views/widgets/equipment/asset_tag_listview.dart b/lib/views/widgets/equipment/asset_tag_listview.dart index 0f936bc3..6081475a 100644 --- a/lib/views/widgets/equipment/asset_tag_listview.dart +++ b/lib/views/widgets/equipment/asset_tag_listview.dart @@ -1,75 +1,151 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/devices_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/widget_extensions.dart'; +import 'package:test_sa/helper/utils.dart'; +import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/modules/asset_tracking/asset_tracking_provider.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/views/widgets/dialogs/dialog.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/loaders/app_loading.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; -class AssetTagListView extends StatelessWidget { - AssetTagListView({Key? key}) : super(key: key); +class AssetTagListView extends StatefulWidget { + int assetId; + String assetName; + String assetNumber; + + AssetTagListView(this.assetId, this.assetName, this.assetNumber, {Key? key}) : super(key: key); + + @override + _AssetTagListViewState createState() { + return _AssetTagListViewState(); + } +} + +class _AssetTagListViewState extends State with SingleTickerProviderStateMixin { + late AnimationController _animationController; + late AssetTrackingProvider assetTrackingProvider; + + @override + void initState() { + super.initState(); + assetTrackingProvider = Provider.of(context, listen: false); + assetTrackingProvider.resetAssetTagData(); + _animationController = AnimationController( + duration: const Duration(milliseconds: 1200), + vsync: this, + ); + _animationController.forward(); + WidgetsBinding.instance.addPostFrameCallback((_) { + assetTrackingProvider.getPenguinAssetTags(); + }); + } + + @override + void dispose() { + super.dispose(); + } @override Widget build(BuildContext context) { return Scaffold( - appBar: const DefaultAppBar(title: "Available Tags"), - body: SizedBox()); + appBar: const DefaultAppBar(title: "Available Tracking Tags"), + body: Consumer(builder: (context, provider, _) { + return provider.isLoadingAssetTags && provider.tagList.isEmpty + ? const Center(child: ALoading()) + : (provider.tagList.isEmpty && !provider.isLoading) + ? NoDataFound(message: context.translation.noDataFound).center + : NotificationListener( + onNotification: (scrollInfo) { + if (provider.isCanLoadMore && scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) { + assetTrackingProvider.getPenguinAssetTags(); + } + return false; + }, + child: SafeArea( + child: ListView.separated( + key: const ValueKey('asset-tag-list-view'), + padding: const EdgeInsets.all(16), + itemCount: provider.tagList.length + (provider.isLoadingAssetTags ? 1 : 0), + separatorBuilder: (listContext, itemIndex) => 12.height, + itemBuilder: (listContext, itemIndex) { + if (itemIndex == provider.tagList.length) { + return const Padding( + padding: EdgeInsets.symmetric(vertical: 16), + child: ALoading(), + ); + } + final delay = itemIndex * 0.08; + final animation = Tween(begin: 0.0, end: 1.0).animate( + CurvedAnimation( + parent: _animationController, + curve: Interval( + delay.clamp(0.0, 0.7), + (delay + 0.4).clamp(0.0, 1.0), + curve: Curves.easeOut, + ), + ), + ); + final slideAnimation = Tween( + begin: const Offset(0.0, 0.15), + end: Offset.zero, + ).animate( + CurvedAnimation( + parent: _animationController, + curve: Interval( + delay.clamp(0.0, 0.7), + (delay + 0.4).clamp(0.0, 1.0), + curve: Curves.easeOutCubic, + ), + ), + ); - // FutureBuilder( - // future: Provider.of(context, listen: false).getPenguinAssetTags(), - // builder: (BuildContext context, AsyncSnapshot snapshot) { - // if (snapshot.connectionState == ConnectionState.waiting) { - // return const Center(child: ALoading()); - // } else if (snapshot.hasData) {} - // - // return SizedBox(); - // - // // return ListView.separated( - // // key: const ValueKey('list_view'), - // // padding: const EdgeInsets.all(16), - // // itemCount: _searchableList.length, - // // separatorBuilder: (listContext, itemIndex) => 12.height, - // // itemBuilder: (listContext, itemIndex) { - // // // Calculate staggered animation delay - // // final delay = itemIndex * 0.08; - // // final animation = Tween(begin: 0.0, end: 1.0).animate( - // // CurvedAnimation( - // // parent: _animationController, - // // curve: Interval( - // // delay.clamp(0.0, 0.7), - // // (delay + 0.4).clamp(0.0, 1.0), - // // curve: Curves.easeOut, - // // ), - // // ), - // // ); - // // - // // final slideAnimation = Tween( - // // begin: const Offset(0.0, 0.15), - // // end: Offset.zero, - // // ).animate( - // // CurvedAnimation( - // // parent: _animationController, - // // curve: Interval( - // // delay.clamp(0.0, 0.7), - // // (delay + 0.4).clamp(0.0, 1.0), - // // curve: Curves.easeOutCubic, - // // ), - // // ), - // // ); - // // - // // return FadeTransition( - // // opacity: animation, - // // child: SlideTransition( - // // position: slideAnimation, - // // child: AssetItemListView( - // // device: _searchableList[itemIndex], - // // onPressed: (device) { - // // widget.fromBottomBar ? Navigator.of(context).pushNamed(AssetDetailPage.id, arguments: device.id) : Navigator.of(context).pop(device); - // // }, - // // ), - // // ), - // // ); - // // }, - // // ); - // })); + return FadeTransition( + opacity: animation, + child: SlideTransition( + position: slideAnimation, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InfoHeaderWidget(provider.tagList[itemIndex].label!), + InfoTextWidget(label: "TagID", value: provider.tagList[itemIndex].tagID, showEmptyValue: true), + InfoTextWidget(label: "Mac Address", value: provider.tagList[itemIndex].macAddress, showEmptyValue: true), + ], + ).toShadowContainer(context)) + .onPress(() async { + bool result = await showDialog( + context: context, + builder: (_) => AAlertDialog( + title: "Confirm".addTranslation, + content: 'You are assigning\n' + '"${provider.tagList[itemIndex].label!}" -> "${widget.assetName}(${widget.assetNumber})"' + .addTranslation, + button1Text: "Assign", + ), + ); + if (result) { + Utils.showLoading(context); + result = await Provider.of(context, listen: false).linkAssetWithPenguinTag(widget.assetId, provider.tagList[itemIndex].id!); + Utils.hideLoading(context); + if (result) { + Navigator.pop(context, result); + } + } + }), + ); + }, + ), + ), + ); + })); } } diff --git a/lib/views/widgets/equipment/asset_tag_model.dart b/lib/views/widgets/equipment/asset_tag_model.dart new file mode 100644 index 00000000..1886631e --- /dev/null +++ b/lib/views/widgets/equipment/asset_tag_model.dart @@ -0,0 +1,44 @@ +class AssetTagModel { + int? id; + String? tagID; + String? label; + String? macAddress; + String? type; + String? status; + String? assetName; + String? assetNumber; + + AssetTagModel( + {this.id, + this.tagID, + this.label, + this.macAddress, + this.type, + this.status, + this.assetName, + this.assetNumber}); + + AssetTagModel.fromJson(Map json) { + id = json['id']; + tagID = json['tagID']; + label = json['label']; + macAddress = json['macAddress']; + type = json['type']; + status = json['status']; + assetName = json['assetName']; + assetNumber = json['assetNumber']; + } + + Map toJson() { + final Map data = new Map(); + data['id'] = this.id; + data['tagID'] = this.tagID; + data['label'] = this.label; + data['macAddress'] = this.macAddress; + data['type'] = this.type; + data['status'] = this.status; + data['assetName'] = this.assetName; + data['assetNumber'] = this.assetNumber; + return data; + } +}