alerts added and move asset tracking to asset page

main_production_asset_tracking
WaseemAbbasi22 4 days ago
parent caee6edf1b
commit 9c4fd1088c

@ -644,4 +644,9 @@ class URLs {
static get getPenguinTagStatusLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=3037";
static get getPenguinTagAssignmentLookup => "$_baseUrl/Lookups/GetLookup?lookupEnum=3033";
//Alerts module
static get getAlerts => "$_baseUrl/Alerts/GetAlerts"; // post - paginated alerts list
static get getAlertById => "$_baseUrl/Alerts/GetById"; // get - append /{id}
static get resolveAlert => "$_baseUrl/Alerts/Resolve"; // post - resolve an alert
}

@ -13,6 +13,7 @@ import 'package:test_sa/controllers/providers/api/departments_provider.dart';
import 'package:test_sa/controllers/providers/api/device_transfer_provider.dart';
import 'package:test_sa/controllers/providers/api/devices_provider.dart';
import 'package:test_sa/controllers/providers/api/gas_refill_provider.dart';
import 'package:test_sa/modules/alerts/alert_provider.dart';
import 'package:test_sa/controllers/providers/api/hospitals_provider.dart';
import 'package:test_sa/controllers/providers/api/notifications_provider.dart';
import 'package:test_sa/controllers/providers/api/oracle_code_provider.dart';
@ -273,6 +274,9 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => NotificationRecipientLookupProvider(), lazy: true),
ChangeNotifierProvider(create: (_) => ModulesWithSubModulesProvider(), lazy: true),
// ALERTS MODULE PROVIDER
ChangeNotifierProvider(create: (_) => AlertProvider(), lazy: true),
// GAS MODULE PROVIDERS (6)
ChangeNotifierProvider(create: (_) => GasTypesProvider(), lazy: true),
ChangeNotifierProvider(create: (_) => CylinderTypesProvider(), lazy: true),

@ -0,0 +1,129 @@
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/helper/utils.dart';
import 'package:test_sa/modules/alerts/alert_model.dart';
import 'package:test_sa/modules/alerts/alert_provider.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';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/views/widgets/item_views/info_date_widget.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';
import 'package:test_sa/views/widgets/requests/request_status.dart';
class AlertDetailPage extends StatefulWidget {
static const String id = "/alert-detail-page";
final int alertId;
const AlertDetailPage({Key? key, required this.alertId}) : super(key: key);
@override
State<AlertDetailPage> createState() => _AlertDetailPageState();
}
class _AlertDetailPageState extends State<AlertDetailPage> {
AlertModel _model = AlertModel();
late AlertProvider alertProvider;
bool isDataUpdated = false;
@override
Widget build(BuildContext context) {
alertProvider = Provider.of<AlertProvider>(context, listen: false);
return Scaffold(
appBar: const DefaultAppBar(title: "Alert Details"),
body: FutureBuilder(
future: alertProvider.getAlertById(widget.alertId, refresh: isDataUpdated),
builder: (context, snap) {
if (snap.connectionState == ConnectionState.waiting) {
return const ALoading();
} else if (snap.hasData && snap.data != null) {
_model = snap.data as AlertModel;
isDataUpdated = false;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: informationCard(_model),
).expanded,
if (!_model.isResolved)
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
label: "Resolve",
onPressed: () async {
Utils.showLoading(context);
bool success = await alertProvider.resolveAlert(_model.id!);
isDataUpdated = !success;
Utils.hideLoading(context);
setState(() {});
},
),
),
],
);
}
return NoDataFound(message: context.translation.noDataFound).center;
},
),
);
}
Widget informationCard(AlertModel alertModel) {
String zonesText = (alertModel.zones ?? [])
.map((z) => z.zoneName ?? (z.zoneId != null ? 'Zone ${z.zoneId}' : null))
.whereType<String>()
.join(', ');
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
StatusLabel(
label: alertModel.statusLabel,
textColor: AppColor.getRequestStatusTextColorByName(context, alertModel.statusLabel),
backgroundColor: AppColor.getRequestStatusColorByName(context, alertModel.statusLabel),
),
],
),
8.height,
InfoHeader16Widget("Alert Details"),
InfoTextWidget(label: "Alert No", value: alertModel.alertId ?? '-', copyValue: true),
InfoTextWidget(label: "Rule Name", value: alertModel.ruleName ?? '-'),
InfoTextWidget(label: "Trigger Type", value: alertModel.ruleTriggerType ?? '-'),
InfoTextWidget(label: "Severity", value: alertModel.severity ?? '-'),
if (alertModel.enter != null) InfoTextWidget(label: "Direction", value: alertModel.enter == true ? "Enter" : "Exit"),
const Divider().defaultStyle(context),
InfoHeader16Widget("Location"),
InfoTextWidget(label: context.translation.site, value: alertModel.campusName ?? '-'),
if (alertModel.venueName?.isNotEmpty ?? false) InfoTextWidget(label: "Venue", value: alertModel.venueName ?? '-'),
if (alertModel.floorName?.isNotEmpty ?? false) InfoTextWidget(label: context.translation.floor, value: alertModel.floorName ?? '-'),
if (zonesText.isNotEmpty) InfoTextWidget(label: "Zones", value: zonesText),
],
).expanded,
InfoDateWidget(alertModel.createdDate ?? alertModel.dateTime ?? ""),
],
),
],
).toShadowContainer(context, padding: 16);
}
}

@ -0,0 +1,50 @@
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/widget_extensions.dart';
import 'package:test_sa/modules/alerts/alert_detail_page.dart';
import 'package:test_sa/modules/alerts/alert_model.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/views/widgets/item_views/info_date_widget.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';
class AlertItemView extends StatelessWidget {
final AlertModel alert;
final bool showShadow;
const AlertItemView({Key? key, required this.alert, this.showShadow = true}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusLabel(
label: alert.statusLabel,
textColor: AppColor.getRequestStatusTextColorByName(context, alert.statusLabel),
backgroundColor: AppColor.getRequestStatusColorByName(context, alert.statusLabel),
),
1.width.expanded,
InfoDateWidget(alert.createdDate ?? alert.dateTime ?? ""),
],
),
InfoHeaderWidget(alert.ruleName ?? "Alert"),
InfoTextWidget(label: "Severity", value: alert.severity),
InfoTextWidget(label: context.translation.site, value: alert.campusName),
InfoTextWidget(label: "Alert No", value: alert.alertId, showArrow: true),
],
).toShadowContainer(context, withShadow: showShadow).onPress(() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => AlertDetailPage(alertId: alert.id!),
),
);
});
}
}

@ -0,0 +1,132 @@
class AlertZone {
int? zoneId;
String? zoneName;
AlertZone({this.zoneId, this.zoneName});
AlertZone.fromJson(Map<String, dynamic> json) {
zoneId = json['zoneId'];
zoneName = json['zoneName'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['zoneId'] = zoneId;
data['zoneName'] = zoneName;
return data;
}
}
class AlertModel {
int? id;
int? ruleId;
String? alertId;
String? ruleName;
String? ruleTriggerType;
String? severity;
String? dateTime;
bool? resolved;
bool? enter;
int? entityId;
int? campusId;
String? campusName;
int? venueId;
String? venueName;
int? floorId;
String? floorName;
List<AlertZone>? zones;
int? geofenceId;
dynamic idleThreshold;
dynamic count;
dynamic externalJson;
String? createdDate;
AlertModel({
this.id,
this.ruleId,
this.alertId,
this.ruleName,
this.ruleTriggerType,
this.severity,
this.dateTime,
this.resolved,
this.enter,
this.entityId,
this.campusId,
this.campusName,
this.venueId,
this.venueName,
this.floorId,
this.floorName,
this.zones,
this.geofenceId,
this.idleThreshold,
this.count,
this.externalJson,
this.createdDate,
});
AlertModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
ruleId = json['ruleId'];
alertId = json['alertId'];
ruleName = json['ruleName'];
ruleTriggerType = json['ruleTriggerType'];
severity = json['severity'];
dateTime = json['dateTime'];
resolved = json['resolved'];
enter = json['enter'];
entityId = json['entityId'];
campusId = json['campusId'];
campusName = json['campusName'];
venueId = json['venueId'];
venueName = json['venueName'];
floorId = json['floorId'];
floorName = json['floorName'];
geofenceId = json['geofenceId'];
idleThreshold = json['idleThreshold'];
count = json['count'];
externalJson = json['externalJson'];
createdDate = json['createdDate'];
if (json['zones'] != null) {
zones = <AlertZone>[];
(json['zones'] as List).forEach((v) {
zones!.add(AlertZone.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['ruleId'] = ruleId;
data['alertId'] = alertId;
data['ruleName'] = ruleName;
data['ruleTriggerType'] = ruleTriggerType;
data['severity'] = severity;
data['dateTime'] = dateTime;
data['resolved'] = resolved;
data['enter'] = enter;
data['entityId'] = entityId;
data['campusId'] = campusId;
data['campusName'] = campusName;
data['venueId'] = venueId;
data['venueName'] = venueName;
data['floorId'] = floorId;
data['floorName'] = floorName;
data['geofenceId'] = geofenceId;
data['idleThreshold'] = idleThreshold;
data['count'] = count;
data['externalJson'] = externalJson;
data['createdDate'] = createdDate;
if (zones != null) {
data['zones'] = zones!.map((v) => v.toJson()).toList();
}
return data;
}
bool get isResolved => resolved == true;
String get statusLabel => isResolved ? "Resolved" : "Open";
}

@ -0,0 +1,96 @@
import 'dart:convert';
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/modules/alerts/alert_model.dart';
class AlertProvider extends ChangeNotifier {
final pageItemNumber = 12;
void reset() {
items = null;
nextPage = true;
stateCode = null;
}
int? stateCode;
bool nextPage = true;
List<AlertModel>? items;
bool isLoading = false;
AlertModel? alertModel;
Future<int> getAlerts() async {
if (isLoading == true) return -2;
isLoading = true;
if (items?.isEmpty ?? true) notifyListeners();
late Response response;
Map<String, dynamic> body = {};
body["pageNumber"] = (items?.length ?? 0) ~/ pageItemNumber + 1;
body["pageSize"] = pageItemNumber;
try {
response = await ApiManager.instance.post(URLs.getAlerts, body: body);
stateCode = response.statusCode;
if (stateCode! >= 200 && stateCode! < 300) {
final decoded = json.decode(response.body);
final List requestsListJson = (decoded is Map && decoded.containsKey('data')) ? decoded['data'] : decoded;
List<AlertModel> itemsPage = requestsListJson.map((request) => AlertModel.fromJson(request)).toList();
items ??= [];
items!.addAll(itemsPage);
nextPage = itemsPage.length == pageItemNumber;
}
isLoading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
isLoading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
Future<AlertModel?> getAlertById(num id, {bool refresh = false}) async {
if (refresh) {
return alertModel;
}
try {
alertModel = null;
Response response = await ApiManager.instance.get("${URLs.getAlertById}/$id");
if (response.statusCode >= 200 && response.statusCode < 300) {
final decoded = json.decode(response.body);
final Map<String, dynamic> dataNode = (decoded is Map && decoded.containsKey('data')) ? decoded['data'] : decoded;
alertModel = AlertModel.fromJson(dataNode);
notifyListeners();
return alertModel;
}
return null;
} catch (error) {
return null;
}
}
Future<bool> resolveAlert(num id) async {
try {
Response response = await ApiManager.instance.post(URLs.resolveAlert, body: {"id": id});
if (response.statusCode >= 200 && response.statusCode < 300) {
if (items != null) {
reset();
}
return true;
}
return false;
} catch (error) {
return false;
}
}
}

@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/modules/alerts/alert_item_view.dart';
import 'package:test_sa/modules/alerts/alert_provider.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/views/widgets/loaders/lazy_loading.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
class AlertsListPage extends StatefulWidget {
final bool fromBottomBar;
const AlertsListPage({Key? key, this.fromBottomBar = false}) : super(key: key);
@override
State<AlertsListPage> createState() => _AlertsListPageState();
}
class _AlertsListPageState extends State<AlertsListPage> {
AlertProvider? _provider;
@override
Widget build(BuildContext context) {
_provider = Provider.of<AlertProvider>(context);
if (_provider!.items == null && !_provider!.isLoading) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_provider!.getAlerts();
});
}
return Scaffold(
appBar: DefaultAppBar(title: "Alerts", showBackButton: !widget.fromBottomBar),
body: Consumer<AlertProvider>(builder: (context, snapshot, _) {
final list = snapshot.items ?? [];
if (list.isEmpty && !snapshot.isLoading) {
return NoDataFound(message: context.translation.noDataFound).center;
}
return LazyLoading(
nextPage: snapshot.nextPage,
onLazyLoad: () async {
await snapshot.getAlerts();
},
child: ListView.separated(
padding: const EdgeInsets.all(16),
itemBuilder: (cxt, index) {
if (index >= list.length) return const SizedBox();
return AlertItemView(alert: list[index]);
},
separatorBuilder: (cxt, index) => const SizedBox(height: 12),
itemCount: list.length,
),
);
}),
);
}
}

@ -26,7 +26,8 @@ class AppBottomNavigationBar extends StatelessWidget {
if(!isHouseKeeper) NavItemModel(1, "request_icon", context.translation.workOrder),
if (!commissioningEng && !isHouseKeeper) NavItemModel(2, "add_icon", context.translation.calendar, showLabel: false),
if(!isHouseKeeper) NavItemModel(3, "assets", context.translation.assets),
NavItemModel(4, "contact", context.translation.contact),
NavItemModel(4, "warning", "Alerts"),
NavItemModel(5, "contact", context.translation.contact),
];
return Container(

@ -75,8 +75,8 @@ class AppDrawer extends StatelessWidget {
// 18.height,
// drawerItem("rate_us", context.translation.rateUs, context),
// 18.height,
drawerItem("asset_tracking", "Asset Tracking", context).onPress(() => Navigator.push(context, MaterialPageRoute(builder: (context) => AssetTrackingPage()))),
18.height,
// drawerItem("asset_tracking", "Asset Tracking", context).onPress(() => Navigator.push(context, MaterialPageRoute(builder: (context) => AssetTrackingPage()))),
// 18.height,
drawerItem("setting", context.translation.settings, context).onPress(() => Navigator.of(context).pushNamed(SettingsPage.id)),
18.height,
drawerItem("site_request_icon", "Site Request", context).onPress(() => Navigator.of(context).pushNamed(SiteRequestHistoryScreen.routeName)),

@ -15,6 +15,8 @@ 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/models/enums/user_types.dart';
import 'package:test_sa/modules/alerts/alert_provider.dart';
import 'package:test_sa/modules/alerts/alerts_list_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/pages/land_page/create_request_type_bottom_sheet.dart';
@ -130,6 +132,7 @@ class _LandPageState extends State<LandPage> {
const SizedBox(),
// if (_userProvider!.user!.type != UsersTypes.engineer) const CalendarPage(),
const MyAssetsPage(fromBottomBar: true),
const AlertsListPage(fromBottomBar: true),
];
if (isFCM) {
FirebaseNotificationManger.initialized(context);
@ -183,7 +186,7 @@ class _LandPageState extends State<LandPage> {
backgroundColor: Colors.white,
builder: (context) => CreateRequestTypeBottomSheet(),
);
} else if (index == 4) {
} else if (index == 5) {
showModalBottomSheet(
context: context,
useSafeArea: true,
@ -191,6 +194,10 @@ class _LandPageState extends State<LandPage> {
builder: (context) => const ContactUsBottomSheet(),
);
} else {
if (index == 4) {
Provider.of<AlertProvider>(context, listen: false).reset();
Provider.of<AlertProvider>(context, listen: false).getAlerts();
}
setState(() {
currentPageIndex = index;
});

@ -10,6 +10,7 @@ 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/asset_tracking/asset_tracking_page.dart';
import 'package:test_sa/modules/tm_module/device_transfer/device_filter_screen.dart';
import 'package:test_sa/modules/tm_module/device_transfer/device_search_screen.dart';
import 'package:test_sa/modules/export_module/pages/export_form_view.dart';
@ -131,7 +132,18 @@ class _MyAssetsPageState extends State<MyAssetsPage> with SingleTickerProviderSt
_searchableList.clear();
_searchableList.addAll(_devicesProvider.devices);
}).expanded,
12.width,
// 12.width,
IconButton(
icon:'asset_tracking'.toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
iconSize: 20,
visualDensity: VisualDensity.compact,
tooltip: "Asset Tracking",
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => AssetTrackingPage()));
},
),
// 12.width,
IconButton(
icon: Icon(!showListView ? Icons.view_list_sharp : Icons.grid_view_outlined, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,

Loading…
Cancel
Save