diff --git a/assets/icons/active_user.svg b/assets/icons/active_user.svg
new file mode 100644
index 00000000..5e0806e0
--- /dev/null
+++ b/assets/icons/active_user.svg
@@ -0,0 +1,18 @@
+
diff --git a/assets/icons/arrow_down.svg b/assets/icons/arrow_down.svg
new file mode 100644
index 00000000..2b4be77b
--- /dev/null
+++ b/assets/icons/arrow_down.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/arrow_forward.svg b/assets/icons/arrow_forward.svg
new file mode 100644
index 00000000..e5866360
--- /dev/null
+++ b/assets/icons/arrow_forward.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/atoz_icon.png b/assets/icons/atoz_icon.png
new file mode 100644
index 00000000..33a9c351
Binary files /dev/null and b/assets/icons/atoz_icon.png differ
diff --git a/assets/icons/box_checked.png b/assets/icons/box_checked.png
new file mode 100644
index 00000000..d93b9d76
Binary files /dev/null and b/assets/icons/box_checked.png differ
diff --git a/assets/icons/compleate_process_icon.svg b/assets/icons/compleate_process_icon.svg
new file mode 100644
index 00000000..a4159de2
--- /dev/null
+++ b/assets/icons/compleate_process_icon.svg
@@ -0,0 +1,9 @@
+
diff --git a/assets/icons/current_process_icon.svg b/assets/icons/current_process_icon.svg
new file mode 100644
index 00000000..967928e3
--- /dev/null
+++ b/assets/icons/current_process_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/deactive_user.svg b/assets/icons/deactive_user.svg
new file mode 100644
index 00000000..7011f5fb
--- /dev/null
+++ b/assets/icons/deactive_user.svg
@@ -0,0 +1,18 @@
+
diff --git a/assets/icons/empty_box.png b/assets/icons/empty_box.png
new file mode 100644
index 00000000..71e79875
Binary files /dev/null and b/assets/icons/empty_box.png differ
diff --git a/assets/icons/filter_table_icon.svg b/assets/icons/filter_table_icon.svg
new file mode 100644
index 00000000..d90e983e
--- /dev/null
+++ b/assets/icons/filter_table_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/invited_icon.svg b/assets/icons/invited_icon.svg
new file mode 100644
index 00000000..5563de14
--- /dev/null
+++ b/assets/icons/invited_icon.svg
@@ -0,0 +1,7 @@
+
diff --git a/assets/icons/link.svg b/assets/icons/link.svg
new file mode 100644
index 00000000..9cd75905
--- /dev/null
+++ b/assets/icons/link.svg
@@ -0,0 +1,12 @@
+
diff --git a/assets/icons/rectangle_check_box.png b/assets/icons/rectangle_check_box.png
new file mode 100644
index 00000000..3404c79c
Binary files /dev/null and b/assets/icons/rectangle_check_box.png differ
diff --git a/assets/icons/search_icon_user.svg b/assets/icons/search_icon_user.svg
new file mode 100644
index 00000000..61eca62d
--- /dev/null
+++ b/assets/icons/search_icon_user.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/uncompleate_process_icon.svg b/assets/icons/uncompleate_process_icon.svg
new file mode 100644
index 00000000..4ede6757
--- /dev/null
+++ b/assets/icons/uncompleate_process_icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/user_management.svg b/assets/icons/user_management.svg
new file mode 100644
index 00000000..3255117a
--- /dev/null
+++ b/assets/icons/user_management.svg
@@ -0,0 +1,7 @@
+
diff --git a/assets/icons/wrong_process_icon.svg b/assets/icons/wrong_process_icon.svg
new file mode 100644
index 00000000..de5b475c
--- /dev/null
+++ b/assets/icons/wrong_process_icon.svg
@@ -0,0 +1,10 @@
+
diff --git a/assets/icons/ztoa_icon.png b/assets/icons/ztoa_icon.png
new file mode 100644
index 00000000..003e5725
Binary files /dev/null and b/assets/icons/ztoa_icon.png differ
diff --git a/lib/common/dialog_dropdown.dart b/lib/common/dialog_dropdown.dart
new file mode 100644
index 00000000..7274b3c0
--- /dev/null
+++ b/lib/common/dialog_dropdown.dart
@@ -0,0 +1,138 @@
+import 'package:flutter/material.dart';
+import 'package:syncrow_web/utils/color_manager.dart';
+
+class DialogDropdown extends StatefulWidget {
+ final List items;
+ final ValueChanged onSelected;
+ final String? selectedValue;
+
+ const DialogDropdown({
+ Key? key,
+ required this.items,
+ required this.onSelected,
+ this.selectedValue,
+ }) : super(key: key);
+
+ @override
+ _DialogDropdownState createState() => _DialogDropdownState();
+}
+
+class _DialogDropdownState extends State {
+ bool _isOpen = false;
+ late OverlayEntry _overlayEntry;
+
+ @override
+ void initState() {
+ super.initState();
+ }
+
+ void _toggleDropdown() {
+ if (_isOpen) {
+ _closeDropdown();
+ } else {
+ _openDropdown();
+ }
+ }
+
+ void _openDropdown() {
+ _overlayEntry = _createOverlayEntry();
+ Overlay.of(context).insert(_overlayEntry);
+ _isOpen = true;
+ }
+
+ void _closeDropdown() {
+ _overlayEntry.remove();
+ _isOpen = false;
+ }
+
+ OverlayEntry _createOverlayEntry() {
+ final renderBox = context.findRenderObject() as RenderBox;
+ final size = renderBox.size;
+ final offset = renderBox.localToGlobal(Offset.zero);
+
+ return OverlayEntry(
+ builder: (context) {
+ return GestureDetector(
+ onTap: () {
+ _closeDropdown();
+ },
+ behavior: HitTestBehavior.translucent,
+ child: Stack(
+ children: [
+ Positioned(
+ left: offset.dx,
+ top: offset.dy + size.height,
+ width: size.width,
+ child: Material(
+ elevation: 4.0,
+ child: Container(
+ color: ColorsManager.whiteColors,
+ constraints: const BoxConstraints(
+ maxHeight: 200.0, // Set max height for dropdown
+ ),
+ child: ListView.builder(
+ shrinkWrap: true,
+ itemCount: widget.items.length,
+ itemBuilder: (context, index) {
+ final item = widget.items[index];
+ return Container(
+ decoration: const BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: ColorsManager.lightGrayBorderColor,
+ width: 1.0,
+ ),
+ ),
+ ),
+ child: ListTile(
+ title: Text(
+ item,
+ style: Theme.of(context)
+ .textTheme
+ .bodyMedium
+ ?.copyWith(
+ color: ColorsManager.textPrimaryColor,
+ ),
+ ),
+ onTap: () {
+ widget.onSelected(item);
+ _closeDropdown();
+ },
+ ),
+ );
+ },
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ },
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ onTap: _toggleDropdown,
+ child: Container(
+ padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
+ decoration: BoxDecoration(
+ border: Border.all(color: ColorsManager.transparentColor),
+ borderRadius: BorderRadius.circular(8.0),
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ widget.selectedValue ?? 'Select an item',
+ style: Theme.of(context).textTheme.bodyMedium,
+ ),
+ const Icon(Icons.arrow_drop_down),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/common/dialog_textfield_dropdown.dart b/lib/common/dialog_textfield_dropdown.dart
new file mode 100644
index 00000000..807f3417
--- /dev/null
+++ b/lib/common/dialog_textfield_dropdown.dart
@@ -0,0 +1,160 @@
+import 'package:flutter/material.dart';
+import 'package:syncrow_web/utils/color_manager.dart';
+
+class DialogTextfieldDropdown extends StatefulWidget {
+ final List items;
+ final ValueChanged onSelected;
+ final String? initialValue;
+
+ const DialogTextfieldDropdown({
+ Key? key,
+ required this.items,
+ required this.onSelected,
+ this.initialValue,
+ }) : super(key: key);
+
+ @override
+ _DialogTextfieldDropdownState createState() =>
+ _DialogTextfieldDropdownState();
+}
+
+class _DialogTextfieldDropdownState extends State {
+ bool _isOpen = false;
+ late OverlayEntry _overlayEntry;
+ final TextEditingController _controller = TextEditingController();
+ late List _filteredItems; // Filtered items list
+
+ @override
+ void initState() {
+ super.initState();
+ _controller.text = widget.initialValue ?? 'Select Tag';
+ _filteredItems = List.from(widget.items); // Initialize filtered items
+ }
+
+ void _toggleDropdown() {
+ if (_isOpen) {
+ _closeDropdown();
+ } else {
+ _openDropdown();
+ }
+ }
+
+ void _openDropdown() {
+ _overlayEntry = _createOverlayEntry();
+ Overlay.of(context).insert(_overlayEntry);
+ _isOpen = true;
+ }
+
+ void _closeDropdown() {
+ _overlayEntry.remove();
+ _isOpen = false;
+ }
+
+ OverlayEntry _createOverlayEntry() {
+ final renderBox = context.findRenderObject() as RenderBox;
+ final size = renderBox.size;
+ final offset = renderBox.localToGlobal(Offset.zero);
+
+ return OverlayEntry(
+ builder: (context) {
+ return GestureDetector(
+ onTap: () {
+ _closeDropdown();
+ },
+ behavior: HitTestBehavior.translucent,
+ child: Stack(
+ children: [
+ Positioned(
+ left: offset.dx,
+ top: offset.dy + size.height,
+ width: size.width,
+ child: Material(
+ elevation: 4.0,
+ child: Container(
+ color: ColorsManager.whiteColors,
+ constraints: const BoxConstraints(
+ maxHeight: 200.0,
+ ),
+ child: ListView.builder(
+ shrinkWrap: true,
+ itemCount: _filteredItems.length,
+ itemBuilder: (context, index) {
+ final item = _filteredItems[index];
+ return Container(
+ decoration: const BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: ColorsManager.lightGrayBorderColor,
+ width: 1.0,
+ ),
+ ),
+ ),
+ child: ListTile(
+ title: Text(item,
+ style: Theme.of(context)
+ .textTheme
+ .bodyMedium
+ ?.copyWith(
+ color: ColorsManager.textPrimaryColor)),
+ onTap: () {
+ _controller.text = item;
+ widget.onSelected(item);
+ setState(() {
+ _filteredItems
+ .remove(item); // Remove selected item
+ });
+ _closeDropdown();
+ },
+ ),
+ );
+ },
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ },
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ onTap: _toggleDropdown,
+ child: Container(
+ padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
+ decoration: BoxDecoration(
+ border: Border.all(color: ColorsManager.transparentColor),
+ borderRadius: BorderRadius.circular(8.0),
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: TextFormField(
+ controller: _controller,
+ onChanged: (value) {
+ setState(() {
+ _filteredItems = widget.items
+ .where((item) =>
+ item.toLowerCase().contains(value.toLowerCase()))
+ .toList(); // Filter items dynamically
+ });
+ widget.onSelected(value);
+ },
+ style: Theme.of(context).textTheme.bodyMedium,
+ decoration: const InputDecoration(
+ hintText: 'Enter or Select tag',
+ border: InputBorder.none,
+ ),
+ ),
+ ),
+ const Icon(Icons.arrow_drop_down),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/pages/auth/model/user_model.dart b/lib/pages/auth/model/user_model.dart
index 84d4661f..5090b0e0 100644
--- a/lib/pages/auth/model/user_model.dart
+++ b/lib/pages/auth/model/user_model.dart
@@ -10,6 +10,10 @@ class UserModel {
final String? phoneNumber;
final bool? isEmailVerified;
final bool? isAgreementAccepted;
+ final bool? hasAcceptedWebAgreement;
+ final DateTime? webAgreementAcceptedAt;
+ final UserRole? role;
+
UserModel({
required this.uuid,
required this.email,
@@ -19,6 +23,9 @@ class UserModel {
required this.phoneNumber,
required this.isEmailVerified,
required this.isAgreementAccepted,
+ required this.hasAcceptedWebAgreement,
+ required this.webAgreementAcceptedAt,
+ required this.role,
});
factory UserModel.fromJson(Map json) {
@@ -31,6 +38,11 @@ class UserModel {
phoneNumber: json['phoneNumber'],
isEmailVerified: json['isEmailVerified'],
isAgreementAccepted: json['isAgreementAccepted'],
+ hasAcceptedWebAgreement: json['hasAcceptedWebAgreement'],
+ webAgreementAcceptedAt: json['webAgreementAcceptedAt'] != null
+ ? DateTime.parse(json['webAgreementAcceptedAt'])
+ : null,
+ role: json['role'] != null ? UserRole.fromJson(json['role']) : null,
);
}
@@ -41,6 +53,9 @@ class UserModel {
Map tempJson = Token.decodeToken(token.accessToken);
return UserModel(
+ hasAcceptedWebAgreement: null,
+ role: null,
+ webAgreementAcceptedAt: null,
uuid: tempJson['uuid'].toString(),
email: tempJson['email'],
firstName: null,
@@ -65,3 +80,26 @@ class UserModel {
};
}
}
+
+class UserRole {
+ final String uuid;
+ final DateTime createdAt;
+ final DateTime updatedAt;
+ final String type;
+
+ UserRole({
+ required this.uuid,
+ required this.createdAt,
+ required this.updatedAt,
+ required this.type,
+ });
+
+ factory UserRole.fromJson(Map json) {
+ return UserRole(
+ uuid: json['uuid'],
+ createdAt: DateTime.parse(json['createdAt']),
+ updatedAt: DateTime.parse(json['updatedAt']),
+ type: json['type'],
+ );
+ }
+}
diff --git a/lib/pages/common/custom_dialog.dart b/lib/pages/common/custom_dialog.dart
index a40ef10f..9899bda4 100644
--- a/lib/pages/common/custom_dialog.dart
+++ b/lib/pages/common/custom_dialog.dart
@@ -12,7 +12,7 @@ Future showCustomDialog({
double? iconWidth,
VoidCallback? onOkPressed,
bool barrierDismissible = false,
- required List actions,
+ List? actions,
}) {
return showDialog(
context: context,
diff --git a/lib/pages/device_managment/sos/view/sos_batch_control_view.dart b/lib/pages/device_managment/sos/view/sos_batch_control_view.dart
index bc66d69f..9082c8bd 100644
--- a/lib/pages/device_managment/sos/view/sos_batch_control_view.dart
+++ b/lib/pages/device_managment/sos/view/sos_batch_control_view.dart
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
-import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
import 'package:syncrow_web/pages/device_managment/sos/bloc/sos_device_bloc.dart';
class SOSBatchControlView extends StatelessWidget {
diff --git a/lib/pages/home/bloc/home_bloc.dart b/lib/pages/home/bloc/home_bloc.dart
index 6863236b..08d35a24 100644
--- a/lib/pages/home/bloc/home_bloc.dart
+++ b/lib/pages/home/bloc/home_bloc.dart
@@ -18,10 +18,15 @@ class HomeBloc extends Bloc {
// List sourcesList = [];
// List destinationsList = [];
UserModel? user;
+ String terms = '';
+ String policy = '';
HomeBloc() : super((HomeInitial())) {
// on(_createNode);
on(_fetchUserInfo);
+ on(_fetchTerms);
+ on(_fetchPolicy);
+ on(_confirmUserAgreement);
}
// void _createNode(CreateNewNode event, Emitter emit) async {
@@ -42,14 +47,48 @@ class HomeBloc extends Bloc {
Future _fetchUserInfo(FetchUserInfo event, Emitter emit) async {
try {
- var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
+ var uuid =
+ await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
user = await HomeApi().fetchUserInfo(uuid);
+ add(FetchTermEvent());
emit(HomeInitial());
} catch (e) {
return;
}
}
+ Future _fetchTerms(FetchTermEvent event, Emitter emit) async {
+ try {
+ emit(LoadingHome());
+ terms = await HomeApi().fetchTerms();
+ add(FetchPolicyEvent());
+ } catch (e) {
+ return;
+ }
+ }
+
+ Future _fetchPolicy(FetchPolicyEvent event, Emitter emit) async {
+ try {
+ emit(LoadingHome());
+ policy = await HomeApi().fetchPolicy();
+ } catch (e) {
+ return;
+ }
+ }
+
+ Future _confirmUserAgreement(
+ ConfirmUserAgreementEvent event, Emitter emit) async {
+ try {
+ emit(LoadingHome());
+ var uuid =
+ await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
+ policy = await HomeApi().confirmUserAgreements(uuid);
+ emit(PolicyAgreement());
+ } catch (e) {
+ return;
+ }
+ }
+
// static Future fetchUserInfo() async {
// try {
// var uuid =
diff --git a/lib/pages/home/bloc/home_event.dart b/lib/pages/home/bloc/home_event.dart
index 50480602..b90ae5de 100644
--- a/lib/pages/home/bloc/home_event.dart
+++ b/lib/pages/home/bloc/home_event.dart
@@ -21,3 +21,9 @@ abstract class HomeEvent extends Equatable {
class FetchUserInfo extends HomeEvent {
const FetchUserInfo();
}
+
+class FetchTermEvent extends HomeEvent {}
+
+class FetchPolicyEvent extends HomeEvent {}
+
+class ConfirmUserAgreementEvent extends HomeEvent {}
diff --git a/lib/pages/home/bloc/home_state.dart b/lib/pages/home/bloc/home_state.dart
index 64c840ab..832820fd 100644
--- a/lib/pages/home/bloc/home_state.dart
+++ b/lib/pages/home/bloc/home_state.dart
@@ -8,8 +8,14 @@ abstract class HomeState extends Equatable {
List