Merge pull request #40 from SyncrowIOT/dev

Dev
This commit is contained in:
Abdullah
2024-10-09 21:36:15 +03:00
committed by GitHub
12 changed files with 541 additions and 283 deletions

View File

@ -5,7 +5,6 @@ import 'package:syncrow_web/pages/access_management/bloc/access_state.dart';
import 'package:syncrow_web/pages/access_management/model/password_model.dart';
import 'package:syncrow_web/pages/common/hour_picker_dialog.dart';
import 'package:syncrow_web/services/access_mang_api.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart';
import 'package:syncrow_web/utils/snack_bar.dart';

View File

@ -24,7 +24,6 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
@override
Widget build(BuildContext context) {
final isLargeScreen = isLargeScreenSize(context);
final isSmallScreen = isSmallScreenSize(context);
final isHalfMediumScreen = isHafMediumScreenSize(context);
@ -169,7 +168,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
}
Row _buildNormalSearchWidgets(BuildContext context, AccessBloc accessBloc) {
TimeOfDay _selectedTime = TimeOfDay.now();
// TimeOfDay _selectedTime = TimeOfDay.now();
return Row(
mainAxisSize: MainAxisSize.min,
@ -218,7 +217,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
SearchResetButtons(
onSearch: () {
accessBloc.add(FilterDataEvent(
emailAuthorizer:accessBloc.emailAuthorizer.text.toLowerCase() ,
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex,
passwordName: accessBloc.passwordName.text.toLowerCase(),
startTime: accessBloc.effectiveTimeTimeStamp,
@ -264,12 +263,11 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
SearchResetButtons(
onSearch: () {
accessBloc.add(FilterDataEvent(
emailAuthorizer:accessBloc.emailAuthorizer.text.toLowerCase() ,
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex,
passwordName: accessBloc.passwordName.text.toLowerCase(),
startTime: accessBloc.effectiveTimeTimeStamp,
endTime: accessBloc.expirationTimeTimeStamp
));
endTime: accessBloc.expirationTimeTimeStamp));
},
onReset: () {
accessBloc.add(ResetSearch());
@ -278,6 +276,4 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
],
);
}
}

View File

@ -108,20 +108,9 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
}
} on DioException catch (e) {
final errorData = e.response!.data;
String errorMessage = errorData['message'];
if (errorMessage == 'this email is not registered') {
validate = 'Invalid Credentials!';
String errorMessage = errorData['error']['message'] ?? 'something went wrong';
validate = errorMessage;
emit(AuthInitialState());
} else if (errorMessage == "You entered wrong otp") {
forgetValidate = 'Wrong one time password.';
emit(AuthInitialState());
} else if (errorMessage == "OTP expired") {
forgetValidate = 'One time password has been expired.';
emit(AuthInitialState());
} else {
validate = '';
emit(AuthInitialState());
}
}
}

View File

@ -48,8 +48,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
late ScrollController _scrollController;
_scrollController = ScrollController();
void _scrollToCenter() {
final double middlePosition =
_scrollController.position.maxScrollExtent / 2;
final double middlePosition = _scrollController.position.maxScrollExtent / 2;
_scrollController.animateTo(
middlePosition,
duration: const Duration(seconds: 1),
@ -66,8 +65,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
second: Center(
child: Stack(
children: [
if (state is AuthLoading)
const Center(child: CircularProgressIndicator()),
if (state is AuthLoading) const Center(child: CircularProgressIndicator()),
ListView(
shrinkWrap: true,
controller: _scrollController,
@ -97,21 +95,16 @@ class ForgetPasswordWebPage extends StatelessWidget {
child: Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius:
const BorderRadius.all(Radius.circular(30)),
border: Border.all(
color:
ColorsManager.graysColor.withOpacity(0.2)),
borderRadius: const BorderRadius.all(Radius.circular(30)),
border: Border.all(color: ColorsManager.graysColor.withOpacity(0.2)),
),
child: Form(
key: forgetBloc.forgetFormKey,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: size.width * 0.02,
vertical: size.width * 0.003),
horizontal: size.width * 0.02, vertical: size.width * 0.003),
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(height: 10),
@ -128,66 +121,55 @@ class ForgetPasswordWebPage extends StatelessWidget {
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400),
.copyWith(fontSize: 14, fontWeight: FontWeight.w400),
),
const SizedBox(height: 10),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(height: 10),
Form(
key: forgetBloc.forgetRegionKey,
child: SizedBox(
child: _buildDropdownField(
context, forgetBloc, size)))
child:
_buildDropdownField(context, forgetBloc, size)))
],
),
const SizedBox(height: 20),
Form(
key: forgetBloc.forgetEmailKey,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Account",
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
fontSize: 14,
fontWeight:
FontWeight.w400),
style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontSize: 14, fontWeight: FontWeight.w400),
),
const SizedBox(height: 10),
SizedBox(
child: TextFormField(
controller: forgetBloc.forgetEmailController,
validator: forgetBloc.validateEmail,
decoration:
textBoxDecoration()!.copyWith(
decoration: textBoxDecoration()!.copyWith(
hintText: 'Enter your email',
hintStyle: Theme.of(context)
.textTheme.bodySmall!.copyWith(
.textTheme
.bodySmall!
.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.w400),
),
style: const TextStyle(
color: Colors.black),
style: const TextStyle(color: Colors.black),
),
),
],
)),
const SizedBox(height: 20.0),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
@ -195,22 +177,20 @@ class ForgetPasswordWebPage extends StatelessWidget {
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400),
.copyWith(fontSize: 14, fontWeight: FontWeight.w400),
),
const SizedBox(height: 10),
SizedBox(
child: TextFormField(
validator: forgetBloc.validateCode,
keyboardType:
TextInputType.visiblePassword,
keyboardType: TextInputType.visiblePassword,
controller: forgetBloc.forgetOtp,
decoration:
textBoxDecoration()!.copyWith(
decoration: textBoxDecoration()!.copyWith(
hintText: 'Enter Code',
hintStyle: Theme.of(context).textTheme
.bodySmall!.copyWith(
hintStyle: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.w400),
suffixIcon: SizedBox(
@ -222,8 +202,15 @@ class ForgetPasswordWebPage extends StatelessWidget {
state.remainingTime != 1
? null
: () {
if (forgetBloc.forgetEmailKey.currentState!.validate()||forgetBloc.forgetRegionKey.currentState!.validate()) {
if(forgetBloc.forgetRegionKey.currentState!.validate()){
if (forgetBloc
.forgetEmailKey.currentState!
.validate() ||
forgetBloc
.forgetRegionKey.currentState!
.validate()) {
if (forgetBloc
.forgetRegionKey.currentState!
.validate()) {
forgetBloc.add(StartTimerEvent());
}
}
@ -231,28 +218,23 @@ class ForgetPasswordWebPage extends StatelessWidget {
child: Text(
'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}',
style: TextStyle(
color: state
is TimerState &&
!state
.isButtonEnabled
color: state is TimerState &&
!state.isButtonEnabled
? Colors.grey
: ColorsManager
.btnColor,
: ColorsManager.btnColor,
),
),
),
),
),
),
style: const TextStyle(
color: Colors.black),
style: const TextStyle(color: Colors.black),
),
),
if (forgetBloc.forgetValidate !=
'') // Check if there is a validation message
Padding(
padding:
const EdgeInsets.only(top: 8.0),
padding: const EdgeInsets.only(top: 8.0),
child: Text(
forgetBloc.forgetValidate,
style: const TextStyle(
@ -265,8 +247,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
),
const SizedBox(height: 20.0),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
@ -274,35 +255,26 @@ class ForgetPasswordWebPage extends StatelessWidget {
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400),
.copyWith(fontSize: 14, fontWeight: FontWeight.w400),
),
const SizedBox(height: 10),
SizedBox(
child: TextFormField(
obscureText: forgetBloc.obscureText,
keyboardType:
TextInputType.visiblePassword,
validator:
forgetBloc.passwordValidator,
controller: forgetBloc
.forgetPasswordController,
decoration:
textBoxDecoration()!.copyWith(
keyboardType: TextInputType.visiblePassword,
validator: forgetBloc.passwordValidator,
controller: forgetBloc.forgetPasswordController,
decoration: textBoxDecoration()!.copyWith(
suffixIcon: IconButton(
onPressed: () {
forgetBloc.add(
PasswordVisibleEvent(
newValue: forgetBloc
.obscureText));
forgetBloc.add(PasswordVisibleEvent(
newValue: forgetBloc.obscureText));
},
icon: SizedBox(
child: SvgPicture.asset(
forgetBloc.obscureText
? Assets.visiblePassword
: Assets
.invisiblePassword,
: Assets.invisiblePassword,
height: 15,
width: 15,
),
@ -313,13 +285,10 @@ class ForgetPasswordWebPage extends StatelessWidget {
.textTheme
.bodySmall!
.copyWith(
color:
ColorsManager.grayColor,
fontWeight:
FontWeight.w400),
color: ColorsManager.grayColor,
fontWeight: FontWeight.w400),
),
style: const TextStyle(
color: Colors.black),
style: const TextStyle(color: Colors.black),
),
),
],
@ -329,21 +298,22 @@ class ForgetPasswordWebPage extends StatelessWidget {
),
const SizedBox(height: 20.0),
Row(
crossAxisAlignment:
CrossAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: size.width * 0.2,
child: DefaultButton(
backgroundColor:
ColorsManager.btnColor,
backgroundColor: ColorsManager.btnColor,
child: const Text('Submit'),
onPressed: () {
if (forgetBloc.forgetFormKey.currentState!.validate() ||
forgetBloc.forgetEmailKey.currentState!.validate() ) {
if( forgetBloc.forgetEmailKey.currentState!.validate()
&&forgetBloc.forgetFormKey.currentState!.validate() ){
forgetBloc.forgetEmailKey.currentState!
.validate()) {
if (forgetBloc.forgetEmailKey.currentState!
.validate() &&
forgetBloc.forgetFormKey.currentState!
.validate()) {
forgetBloc.add(ChangePasswordEvent());
}
}
@ -358,8 +328,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
child: Text(
forgetBloc.validate,
style: const TextStyle(
fontWeight: FontWeight.w700,
color: ColorsManager.red),
fontWeight: FontWeight.w700, color: ColorsManager.red),
),
),
),
@ -372,8 +341,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
children: [
const Text(
"Do you have an account? ",
style:
TextStyle(color: Colors.white),
style: TextStyle(color: Colors.white),
),
InkWell(
onTap: () {
@ -407,8 +375,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
));
}
Widget _buildDropdownField(
BuildContext context, AuthBloc loginBloc, Size size) {
Widget _buildDropdownField(BuildContext context, AuthBloc loginBloc, Size size) {
final TextEditingController textEditingController = TextEditingController();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -434,13 +401,10 @@ class ForgetPasswordWebPage extends StatelessWidget {
builder: (FormFieldState<String> field) {
return InputDecorator(
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(horizontal: 2, vertical: 10),
contentPadding: const EdgeInsets.symmetric(horizontal: 2, vertical: 10),
errorText: field.errorText,
filled:
true, // Ensure the dropdown is filled with the background color
fillColor: ColorsManager
.boxColor, // Match the dropdown container color
filled: true, // Ensure the dropdown is filled with the background color
fillColor: ColorsManager.boxColor, // Match the dropdown container color
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
@ -451,22 +415,20 @@ class ForgetPasswordWebPage extends StatelessWidget {
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color:
field.hasError ? Colors.red : ColorsManager.grayColor,
color: field.hasError ? Colors.red : ColorsManager.grayColor,
width: 1.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
color:
field.hasError ? Colors.red : ColorsManager.grayColor,
color: field.hasError ? Colors.red : ColorsManager.grayColor,
width: 1.5,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
borderSide: const BorderSide(
color: Colors.red,
width: 1.5,
),
@ -497,15 +459,13 @@ class ForgetPasswordWebPage extends StatelessWidget {
),
);
}).toList(),
value: loginBloc.regionList!
.any((region) => region.id == loginBloc.regionUuid)
value: loginBloc.regionList!.any((region) => region.id == loginBloc.regionUuid)
? loginBloc.regionUuid
: null,
onChanged: (String? value) {
if (value != null) {
loginBloc.add(SelectRegionEvent(val: value));
field.didChange(
value); // Notify the form field of the change
field.didChange(value); // Notify the form field of the change
}
},
buttonStyleData: const ButtonStyleData(
@ -529,8 +489,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
searchInnerWidgetHeight: 50,
searchInnerWidget: Container(
height: 50,
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: TextFormField(
style: const TextStyle(color: Colors.black),
controller: textEditingController,
@ -544,8 +503,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
),
),
searchMatchFn: (item, searchValue) {
final regionName =
(item.child as Text).data?.toLowerCase() ?? '';
final regionName = (item.child as Text).data?.toLowerCase() ?? '';
final search = searchValue.toLowerCase().trim();
return regionName.contains(search);
},
@ -564,6 +522,4 @@ class ForgetPasswordWebPage extends StatelessWidget {
],
);
}
}

View File

@ -24,8 +24,7 @@ class LoginWebPage extends StatefulWidget {
State<LoginWebPage> createState() => _LoginWebPageState();
}
class _LoginWebPageState extends State<LoginWebPage>
with HelperResponsiveLayout {
class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout {
@override
Widget build(BuildContext context) {
return Scaffold(
@ -60,8 +59,7 @@ class _LoginWebPageState extends State<LoginWebPage>
_scrollController = ScrollController();
void _scrollToCenter() {
final double middlePosition =
_scrollController.position.maxScrollExtent / 2;
final double middlePosition = _scrollController.position.maxScrollExtent / 2;
_scrollController.animateTo(
middlePosition,
duration: const Duration(seconds: 1),
@ -123,8 +121,7 @@ class _LoginWebPageState extends State<LoginWebPage>
const Spacer(),
Expanded(
flex: 2,
child: _buildLoginFormFields(
context, loginBloc, size),
child: _buildLoginFormFields(context, loginBloc, size),
),
const Spacer(),
],
@ -135,14 +132,12 @@ class _LoginWebPageState extends State<LoginWebPage>
),
),
),
if (state is AuthLoading)
const Center(child: CircularProgressIndicator())
if (state is AuthLoading) const Center(child: CircularProgressIndicator())
],
);
}
Widget _buildLoginFormFields(
BuildContext context, AuthBloc loginBloc, Size size) {
Widget _buildLoginFormFields(BuildContext context, AuthBloc loginBloc, Size size) {
return Container(
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
@ -152,8 +147,8 @@ class _LoginWebPageState extends State<LoginWebPage>
child: Form(
key: loginBloc.loginFormKey,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: size.width * 0.02, vertical: size.width * 0.003),
padding:
EdgeInsets.symmetric(horizontal: size.width * 0.02, vertical: size.width * 0.003),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
@ -181,9 +176,7 @@ class _LoginWebPageState extends State<LoginWebPage>
);
}
Widget _buildDropdownField(
BuildContext context, AuthBloc loginBloc, Size size) {
Widget _buildDropdownField(BuildContext context, AuthBloc loginBloc, Size size) {
final TextEditingController textEditingController = TextEditingController();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -227,7 +220,8 @@ class _LoginWebPageState extends State<LoginWebPage>
);
}).toList(),
value: loginBloc.regionList!.any(
(region) => region.id == loginBloc.regionUuid,)
(region) => region.id == loginBloc.regionUuid,
)
? loginBloc.regionUuid
: null,
onChanged: (String? value) {
@ -286,7 +280,6 @@ class _LoginWebPageState extends State<LoginWebPage>
);
}
Widget _buildEmailField(BuildContext context, AuthBloc loginBloc) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -310,9 +303,10 @@ class _LoginWebPageState extends State<LoginWebPage>
decoration: textBoxDecoration()!.copyWith(
errorStyle: const TextStyle(height: 0),
hintText: 'Enter your email address',
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.w400)),
hintStyle: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400)),
style: const TextStyle(color: Colors.black),
),
),
@ -344,18 +338,17 @@ class _LoginWebPageState extends State<LoginWebPage>
controller: loginBloc.loginPasswordController,
decoration: textBoxDecoration()!.copyWith(
hintText: 'At least 8 characters',
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
color: ColorsManager.grayColor, fontWeight: FontWeight.w400),
hintStyle: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400),
suffixIcon: IconButton(
onPressed: () {
loginBloc.add(
PasswordVisibleEvent(newValue: loginBloc.obscureText));
loginBloc.add(PasswordVisibleEvent(newValue: loginBloc.obscureText));
},
icon: SizedBox(
child: SvgPicture.asset(
loginBloc.obscureText
? Assets.visiblePassword
: Assets.invisiblePassword,
loginBloc.obscureText ? Assets.visiblePassword : Assets.invisiblePassword,
height: 15,
width: 15,
),
@ -383,10 +376,10 @@ class _LoginWebPageState extends State<LoginWebPage>
},
child: Text(
"Forgot Password?",
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w400),
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400),
),
),
],
@ -450,8 +443,7 @@ class _LoginWebPageState extends State<LoginWebPage>
);
}
Widget _buildSignInButton(
BuildContext context, AuthBloc loginBloc, Size size) {
Widget _buildSignInButton(BuildContext context, AuthBloc loginBloc, Size size) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
@ -492,8 +484,7 @@ class _LoginWebPageState extends State<LoginWebPage>
SizedBox(
child: Text(
loginBloc.validate,
style: const TextStyle(
fontWeight: FontWeight.w700, color: ColorsManager.red),
style: const TextStyle(fontWeight: FontWeight.w700, color: ColorsManager.red),
),
)
],

View File

@ -0,0 +1,304 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class AccessDeviceTable extends StatefulWidget {
final List<String> headers;
final String? tableName;
final List<List<dynamic>> data;
final BoxDecoration? headerDecoration;
final BoxDecoration? cellDecoration;
final Size size;
final bool withCheckBox;
final bool withSelectAll;
final bool isEmpty;
final void Function(bool?)? selectAll;
final void Function(int, bool, dynamic)? onRowSelected;
final List<String>? initialSelectedIds;
final int uuidIndex;
const AccessDeviceTable({
super.key,
required this.headers,
required this.data,
required this.size,
this.tableName,
required this.isEmpty,
required this.withCheckBox,
required this.withSelectAll,
this.headerDecoration,
this.cellDecoration,
this.selectAll,
this.onRowSelected,
this.initialSelectedIds,
required this.uuidIndex,
});
@override
_DynamicTableState createState() => _DynamicTableState();
}
class _DynamicTableState extends State<AccessDeviceTable> {
late List<bool> _selected;
bool _selectAll = false;
@override
void initState() {
super.initState();
_initializeSelection();
}
@override
void didUpdateWidget(AccessDeviceTable oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.data != widget.data) {
_initializeSelection();
}
}
void _initializeSelection() {
if (widget.data.isEmpty) {
_selected = [];
_selectAll = false;
} else {
_selected = List<bool>.generate(widget.data.length, (index) {
// Check if the initialSelectedIds contains the deviceUuid
// uuidIndex is the index of the column containing the deviceUuid
final deviceUuid = widget.data[index][widget.uuidIndex];
return widget.initialSelectedIds != null &&
widget.initialSelectedIds!.contains(deviceUuid);
});
_selectAll = _selected.every((element) => element == true);
}
}
void _toggleRowSelection(int index) {
setState(() {
_selected[index] = !_selected[index];
if (widget.onRowSelected != null) {
widget.onRowSelected!(index, _selected[index], widget.data[index]);
}
});
}
void _toggleSelectAll(bool? value) {
setState(() {
_selectAll = value ?? false;
_selected = List<bool>.filled(widget.data.length, _selectAll);
if (widget.selectAll != null) {
widget.selectAll!(_selectAll);
}
});
}
@override
Widget build(BuildContext context) {
return Container(
decoration: widget.cellDecoration,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: widget.size.width,
child: Column(
children: [
Container(
decoration: widget.headerDecoration ??
BoxDecoration(color: Colors.grey[200]),
child: Row(
children: [
if (widget.withCheckBox) _buildSelectAllCheckbox(),
...widget.headers
.map((header) => _buildTableHeaderCell(header)),
],
),
),
widget.isEmpty
? Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
SvgPicture.asset(Assets.emptyTable),
const SizedBox(
height: 15,
),
Text(
// no password
widget.tableName == 'AccessManagement'
? 'No Password '
: 'No Devices',
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: ColorsManager.grayColor),
)
],
),
],
),
],
),
)
: Expanded(
child: Container(
color: Colors.white,
child: ListView.builder(
shrinkWrap: true,
itemCount: widget.data.length,
itemBuilder: (context, index) {
final row = widget.data[index];
return Row(
children: [
if (widget.withCheckBox)
_buildRowCheckbox(
index, widget.size.height * 0.10),
...row.map((cell) => _buildTableCell(
cell.toString(),
widget.size.height * 0.10)),
],
);
},
),
),
),
],
),
),
),
);
}
Widget _buildSelectAllCheckbox() {
return Container(
width: 50,
padding: const EdgeInsets.all(8.0),
decoration: const BoxDecoration(
border: Border.symmetric(
vertical: BorderSide(color: ColorsManager.boxDivider),
),
),
child: Checkbox(
value: widget.data.isNotEmpty &&
_selected.every((element) => element == true),
onChanged: widget.withSelectAll && widget.data.isNotEmpty
? _toggleSelectAll
: null,
),
);
}
Widget _buildRowCheckbox(int index, double size) {
return Container(
width: 50,
padding: const EdgeInsets.all(8.0),
height: size,
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: ColorsManager.boxDivider,
width: 1.0,
),
),
),
alignment: Alignment.centerLeft,
child: Center(
child: Checkbox(
value: _selected[index],
onChanged: (bool? value) {
_toggleRowSelection(index);
},
),
),
);
}
Widget _buildTableHeaderCell(String title) {
return Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border.symmetric(
vertical: BorderSide(color: ColorsManager.boxDivider),
),
),
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
title,
style: const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 13,
color: Color(0xFF999999),
),
maxLines: 2,
),
),
),
);
}
Widget _buildTableCell(String content, double size) {
bool isBatteryLevel = content.endsWith('%');
double? batteryLevel;
if (isBatteryLevel) {
batteryLevel = double.tryParse(content.replaceAll('%', '').trim());
}
Color? statusColor;
switch (content) {
case 'Effective':
statusColor = ColorsManager.textGreen;
break;
case 'Expired':
statusColor = ColorsManager.red;
break;
case 'To be effective':
statusColor = ColorsManager.yaGreen;
break;
case 'Online':
statusColor = ColorsManager.green;
break;
case 'Offline':
statusColor = ColorsManager.red;
break;
default:
statusColor = Colors.black;
}
return Expanded(
child: Container(
height: size,
padding: const EdgeInsets.all(5.0),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: ColorsManager.boxDivider,
width: 1.0,
),
),
),
alignment: Alignment.centerLeft,
child: Text(
content,
style: TextStyle(
color: (batteryLevel != null && batteryLevel < 20)
? ColorsManager.red
: (batteryLevel != null && batteryLevel > 20)
? ColorsManager.green
: statusColor,
fontSize: 10,
fontWeight: FontWeight.w400),
maxLines: 2,
),
),
);
}
}

View File

@ -14,10 +14,9 @@ class DeviceManagementBloc extends Bloc<DeviceManagementEvent, DeviceManagementS
int _lowBatteryCount = 0;
List<AllDevicesModel> _selectedDevices = [];
List<AllDevicesModel> _filteredDevices = [];
String productName = '';
String currentProductName = '';
String? currentCommunity;
String? currentUnitName;
String? currentProductName;
DeviceManagementBloc() : super(DeviceManagementInitial()) {
on<FetchDevices>(_onFetchDevices);
@ -77,14 +76,14 @@ class DeviceManagementBloc extends Bloc<DeviceManagementEvent, DeviceManagementS
isControlButtonEnabled: _selectedDevices.isNotEmpty,
));
if (productName.isNotEmpty) {
add(SearchDevices(productName: productName));
if (currentProductName.isNotEmpty) {
add(SearchDevices(productName: currentProductName));
}
}
}
Future<void> _onResetFilters(ResetFilters event, Emitter<DeviceManagementState> emit) async {
productName = '';
currentProductName = '';
_selectedDevices.clear();
_filteredDevices = List.from(_devices);
_selectedIndex = 0;
@ -238,20 +237,10 @@ class DeviceManagementBloc extends Bloc<DeviceManagementEvent, DeviceManagementS
}
void _onSearchDevices(SearchDevices event, Emitter<DeviceManagementState> emit) {
if (event.productName == currentProductName &&
event.community == currentCommunity &&
event.unitName == currentUnitName) {
return;
}
currentProductName = event.productName;
currentCommunity = event.community;
currentUnitName = event.unitName;
if ((event.community == null || event.community!.isEmpty) &&
(event.unitName == null || event.unitName!.isEmpty) &&
(event.productName == null || event.productName!.isEmpty)) {
productName = '';
currentProductName = '';
if (state is DeviceManagementFiltered) {
add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
} else {
@ -259,7 +248,17 @@ class DeviceManagementBloc extends Bloc<DeviceManagementEvent, DeviceManagementS
}
}
productName = event.productName ?? '';
if (event.productName == currentProductName &&
event.community == currentCommunity &&
event.unitName == currentUnitName &&
event.searchField) {
return;
}
currentProductName = event.productName ?? '';
currentCommunity = event.community;
currentUnitName = event.unitName;
List<AllDevicesModel> devicesToSearch = _filteredDevices;
if (devicesToSearch.isNotEmpty) {

View File

@ -31,11 +31,13 @@ class SearchDevices extends DeviceManagementEvent {
final String? community;
final String? unitName;
final String? productName;
final bool searchField;
const SearchDevices({
this.community,
this.unitName,
this.productName,
this.searchField = false,
});
@override

View File

@ -69,7 +69,8 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters> with HelperRe
context.read<DeviceManagementBloc>().add(SearchDevices(
productName: productNameController.text,
unitName: unitNameController.text,
));
community: communityController.text,
searchField: true));
},
);
}
@ -81,7 +82,7 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters> with HelperRe
community: communityController.text,
unitName: unitNameController.text,
productName: productNameController.text,
));
searchField: true));
},
onReset: () {
communityController.clear();

View File

@ -23,7 +23,7 @@ class _NotificationDialogState extends State<NotificationDialog> {
borderRadius: BorderRadius.circular(20),
),
child: SizedBox(
width: 798,
width: 660,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
@ -70,8 +70,11 @@ class _NotificationDialogState extends State<NotificationDialog> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ToggleWidget(
value: true,
SizedBox(
width: 170,
height: 135,
child: ToggleWidget(
value: isLowBatteryNotificationEnabled,
code: 'notification',
deviceId: '',
label: 'Low Battery',
@ -82,8 +85,12 @@ class _NotificationDialogState extends State<NotificationDialog> {
},
icon: '-1',
),
ToggleWidget(
value: true,
),
SizedBox(
width: 170,
height: 135,
child: ToggleWidget(
value: isClosingRemindersEnabled,
code: 'notification',
deviceId: '',
label: 'Closing\nReminders',
@ -94,8 +101,12 @@ class _NotificationDialogState extends State<NotificationDialog> {
},
icon: '-1',
),
ToggleWidget(
value: true,
),
SizedBox(
width: 170,
height: 135,
child: ToggleWidget(
value: isDoorAlarmEnabled,
code: 'notification',
deviceId: '',
label: 'Door Alarm',
@ -106,6 +117,7 @@ class _NotificationDialogState extends State<NotificationDialog> {
},
icon: '-1',
),
),
],
),
],

View File

@ -6,12 +6,13 @@ class WaterLeakNotificationDialog extends StatefulWidget {
const WaterLeakNotificationDialog({super.key});
@override
State<WaterLeakNotificationDialog> createState() => _WaterLeakNotificationDialogState();
State<WaterLeakNotificationDialog> createState() => _NotificationDialogState();
}
class _WaterLeakNotificationDialogState extends State<WaterLeakNotificationDialog> {
class _NotificationDialogState extends State<WaterLeakNotificationDialog> {
bool isLowBatteryNotificationEnabled = true;
bool isWaterLeakageNotificationEnabled = true;
bool isClosingRemindersEnabled = true;
bool isWaterLeakage = true;
@override
Widget build(BuildContext context) {
@ -22,7 +23,7 @@ class _WaterLeakNotificationDialogState extends State<WaterLeakNotificationDialo
borderRadius: BorderRadius.circular(20),
),
child: SizedBox(
width: 400,
width: 560,
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
@ -69,8 +70,11 @@ class _WaterLeakNotificationDialogState extends State<WaterLeakNotificationDialo
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ToggleWidget(
value: true,
SizedBox(
width: 170,
height: 135,
child: ToggleWidget(
value: isLowBatteryNotificationEnabled,
code: 'notification',
deviceId: '',
label: 'Low Battery',
@ -81,18 +85,23 @@ class _WaterLeakNotificationDialogState extends State<WaterLeakNotificationDialo
},
icon: '-1',
),
ToggleWidget(
value: true,
),
SizedBox(
width: 170,
height: 135,
child: ToggleWidget(
value: isWaterLeakage,
code: 'notification',
deviceId: '',
label: 'Water Leakage',
onChange: (v) {
setState(() {
isWaterLeakageNotificationEnabled = v;
isWaterLeakage = v;
});
},
icon: '-1',
),
),
],
),
],

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/common/custom_table.dart';
import 'package:syncrow_web/pages/common/access_device_table.dart';
import 'package:syncrow_web/pages/common/text_field/custom_web_textfield.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
@ -49,7 +49,7 @@ class AddDeviceDialog extends StatelessWidget {
children: <Widget>[
Container(
width: size.width,
padding: EdgeInsets.all(15),
padding: const EdgeInsets.all(15),
decoration: containerDecoration.copyWith(
color: ColorsManager.worningColor,
border: Border.all(color: Color(0xffFFD22F)),
@ -163,7 +163,7 @@ class AddDeviceDialog extends StatelessWidget {
Expanded(
flex: 3,
child: state is TableLoaded
? DynamicTable(
? AccessDeviceTable(
uuidIndex: 1,
withSelectAll: true,
initialSelectedIds: selectedDeviceIds,