Success dialog and Failed dialog changes with VisitorPasswordDialog

This commit is contained in:
mohammad
2024-08-23 22:16:48 +03:00
parent f5a7441b3c
commit cf1a21e121
19 changed files with 792 additions and 785 deletions

View File

@ -1,96 +0,0 @@
[
{
"accessUser": "Ali Doe",
"accessType": "Admin",
"startTime": "2023-08-01",
"endTime": "2023-08-02",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
}, {
"accessUser": "oamr Doe",
"accessType": "Admin",
"startTime": "2023-08-01",
"endTime": "2023-08-05",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
}, {
"accessUser": "John Doe",
"accessType": "Admin",
"startTime": "2023-08-01",
"endTime": "2023-08-10",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
},
{
"accessUser": "John Doe",
"accessType": "Admin",
"startTime": "2023-08-01",
"endTime": "2023-10-10",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
},
{
"accessUser": "John Doe",
"accessType": "Admin",
"startTime": "2023-03-01",
"endTime": "2023-05-10",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
},
{
"accessUser": "John Doe",
"accessType": "Admin",
"startTime": "2023-07-01",
"endTime": "2023-08-10",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
}, {
"accessUser": "John Doe",
"accessType": "Admin",
"startTime": "2023-01-01",
"endTime": "2023-09-05",
"accessibleDevice": "Smart Door",
"authorizationSource": "System",
"authorizer": "Jane Smith",
"authorizationTime": "2023-08-01 10:00 AM",
"accessStatus": "Granted",
"actions": "View"
},
{
"accessUser": "Alice Johnson",
"accessType": "User",
"startTime": "2023-08-01",
"endTime": "2023-08-10",
"accessibleDevice": "Smart Lock",
"authorizationSource": "Admin",
"authorizer": "John Doe",
"authorizationTime": "2023-08-02 11:00 AM",
"accessStatus": "Pending",
"actions": "Approve"
}
]

View File

@ -5,8 +5,7 @@ import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/auth/view/login_page.dart'; import 'package:syncrow_web/pages/auth/view/login_page.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/view/home_page.dart'; import 'package:syncrow_web/pages/home/view/home_page.dart';
import 'package:syncrow_web/pages/visitor_password/view/add_device_dialog.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart';
import 'package:syncrow_web/services/locator.dart'; import 'package:syncrow_web/services/locator.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
@ -30,6 +29,8 @@ class MyApp extends StatelessWidget {
return MultiBlocProvider( return MultiBlocProvider(
providers: [ providers: [
BlocProvider(create: (context) => HomeBloc()), BlocProvider(create: (context) => HomeBloc()),
BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(),)
], ],
child: MaterialApp( child: MaterialApp(
debugShowCheckedModeBanner: false, // Hide debug banner debugShowCheckedModeBanner: false, // Hide debug banner

View File

@ -39,12 +39,9 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
} }
} }
void updateTabsCount() { void updateTabsCount() {
// Count occurrences based on the type field
int toBeEffectiveCount = data.where((item) => item.passwordStatus.value== 'To Be Effective').length; int toBeEffectiveCount = data.where((item) => item.passwordStatus.value== 'To Be Effective').length;
int effectiveCount = data.where((item) => item.passwordStatus.value == 'Effective').length; int effectiveCount = data.where((item) => item.passwordStatus.value == 'Effective').length;
int expiredCount = data.where((item) => item.passwordStatus.value == 'Expired').length; int expiredCount = data.where((item) => item.passwordStatus.value == 'Expired').length;
// Update tab labels with counts
tabs[1] = 'To Be Effective ($toBeEffectiveCount)'; tabs[1] = 'To Be Effective ($toBeEffectiveCount)';
tabs[2] = 'Effective ($effectiveCount)'; tabs[2] = 'Effective ($effectiveCount)';
tabs[3] = 'Expired ($expiredCount)'; tabs[3] = 'Expired ($expiredCount)';
@ -53,7 +50,6 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
int selectedIndex = 0; int selectedIndex = 0;
final List<String> tabs = [ final List<String> tabs = [
'All', 'All',
'To Be Effective (0)', 'To Be Effective (0)',
@ -76,7 +72,6 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
Future<void> selectTime(SelectTime event, Emitter<AccessState> emit) async { Future<void> selectTime(SelectTime event, Emitter<AccessState> emit) async {
final DateTime? picked = await showDatePicker( final DateTime? picked = await showDatePicker(
context: event.context, context: event.context,
initialDate: DateTime.now(), initialDate: DateTime.now(),
@ -146,8 +141,6 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
try { try {
filteredData = data.where((item) { filteredData = data.where((item) {
bool matchesCriteria = true; bool matchesCriteria = true;
// Filter by password name if provided
if (event.passwordName != null && event.passwordName!.isNotEmpty) { if (event.passwordName != null && event.passwordName!.isNotEmpty) {
final bool matchesName = item.passwordName != null && final bool matchesName = item.passwordName != null &&
item.passwordName.contains(event.passwordName); item.passwordName.contains(event.passwordName);
@ -155,8 +148,6 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
matchesCriteria = false; matchesCriteria = false;
} }
} }
// Filter by date range if provided
if (event.startTime != null && event.endTime != null) { if (event.startTime != null && event.endTime != null) {
final int? effectiveTime = int.tryParse(item.effectiveTime.toString()); final int? effectiveTime = int.tryParse(item.effectiveTime.toString());
final int? invalidTime = int.tryParse(item.invalidTime.toString()); final int? invalidTime = int.tryParse(item.invalidTime.toString());
@ -170,8 +161,6 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
} }
} }
} }
// Filter by tab selection
if (event.selectedTabIndex == 1 && item.passwordStatus.value != 'To Be Effective') { if (event.selectedTabIndex == 1 && item.passwordStatus.value != 'To Be Effective') {
matchesCriteria = false; matchesCriteria = false;
} else if (event.selectedTabIndex == 2 && item.passwordStatus.value != 'Effective') { } else if (event.selectedTabIndex == 2 && item.passwordStatus.value != 'Effective') {
@ -179,7 +168,6 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
} else if (event.selectedTabIndex == 3 && item.passwordStatus.value != 'Expired') { } else if (event.selectedTabIndex == 3 && item.passwordStatus.value != 'Expired') {
matchesCriteria = false; matchesCriteria = false;
} }
return matchesCriteria; return matchesCriteria;
}).toList(); }).toList();
emit(TableLoaded(filteredData)); emit(TableLoaded(filteredData));
@ -206,7 +194,6 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
try { try {
emit(AccessLoaded()); emit(AccessLoaded());
selectedIndex = event.selectedIndex; selectedIndex = event.selectedIndex;
// Apply filtering based on selected tab
switch (selectedIndex) { switch (selectedIndex) {
case 0: // All case 0: // All
filteredData = data; filteredData = data;
@ -216,7 +203,6 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
break; break;
case 2: // Effective case 2: // Effective
filteredData = data.where((item) => item.passwordStatus.value == "Effective").toList(); filteredData = data.where((item) => item.passwordStatus.value == "Effective").toList();
break; break;
case 3: // Expired case 3: // Expired
filteredData = data.where((item) => item.passwordStatus.value == "Expired").toList(); filteredData = data.where((item) => item.passwordStatus.value == "Expired").toList();
@ -225,7 +211,7 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
filteredData = data; filteredData = data;
} }
add(FilterDataEvent( add(FilterDataEvent(
selectedTabIndex: selectedIndex, // Pass the selected tab index selectedTabIndex: selectedIndex,
passwordName: passwordName.text.toLowerCase(), passwordName: passwordName.text.toLowerCase(),
startTime: effectiveTimeTimeStamp, startTime: effectiveTimeTimeStamp,
endTime: expirationTimeTimeStamp endTime: expirationTimeTimeStamp

View File

@ -21,17 +21,14 @@ class AccessManagementPage extends StatelessWidget {
enableMenuSideba: false, enableMenuSideba: false,
appBarTitle: Row( appBarTitle: Row(
children: [ children: [
Text( Text('Access Management',
'Access Management',
style: Theme.of(context).textTheme.headlineLarge, style: Theme.of(context).textTheme.headlineLarge,
) )
], ],
), ),
appBarBody: [ appBarBody: [
Text( Text('Physical Access',
'Physical Access', style: Theme.of(context).textTheme
style: Theme.of(context)
.textTheme
.headlineMedium! .headlineMedium!
.copyWith(color: Colors.white), .copyWith(color: Colors.white),
), ),
@ -101,7 +98,9 @@ class AccessManagementPage extends StatelessWidget {
height: 20, height: 20,
), ),
Row( Row(
children: [ mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
textBaseline: TextBaseline.ideographic, children: [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -111,7 +110,7 @@ class AccessManagementPage extends StatelessWidget {
color: Colors.black,fontSize: 13),), color: Colors.black,fontSize: 13),),
const SizedBox(height: 5,), const SizedBox(height: 5,),
Container( Container(
height:size.height * 0.053, height:43,
width: size.width * 0.15, width: size.width * 0.15,
decoration: containerDecoration, decoration: containerDecoration,
child: TextFormField( child: TextFormField(
@ -145,11 +144,10 @@ class AccessManagementPage extends StatelessWidget {
), ),
SizedBox( SizedBox(
height:45,
width: size.width * 0.06, width: size.width * 0.06,
child: Column( child:Container(
children: [
Text(''),
Container(
decoration: containerDecoration, decoration: containerDecoration,
child: DefaultButton( child: DefaultButton(
onPressed: () { onPressed: () {
@ -161,18 +159,14 @@ class AccessManagementPage extends StatelessWidget {
)); ));
}, borderRadius: 9, }, borderRadius: 9,
child: const Text('Search'))), child: const Text('Search'))),
],
),
), ),
const SizedBox( const SizedBox(
width: 10, width: 10,
), ),
SizedBox( SizedBox(
height:45,
width: size.width * 0.06, width: size.width * 0.06,
child: Column( child: Container(
children: [
Text(''),
Container(
decoration: containerDecoration, decoration: containerDecoration,
child: DefaultButton( child: DefaultButton(
onPressed: () { onPressed: () {
@ -189,8 +183,6 @@ class AccessManagementPage extends StatelessWidget {
), ),
), ),
), ),
],
),
), ),
], ],
), ),

View File

@ -146,7 +146,6 @@ class LoginMobilePage extends StatelessWidget {
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
isDense: true, isDense: true,
style: const TextStyle(color: Colors.black), style: const TextStyle(color: Colors.black),
items:loginBloc.regionList!.map((RegionModel region) { items:loginBloc.regionList!.map((RegionModel region) {
@ -156,7 +155,6 @@ class LoginMobilePage extends StatelessWidget {
); );
}).toList(), }).toList(),
onChanged: (String? value) { onChanged: (String? value) {
print(value);
}, },
), ),
) )

View File

@ -0,0 +1,75 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/utils/color_manager.dart';
Future<void> showCustomDialog({
required BuildContext context,
required String message,
String? title,
String? iconPath,
double? dialogHeight,
double? iconHeight,
double? iconWidth,
VoidCallback? onOkPressed,
bool barrierDismissible = false, required actions,
}) {
return showDialog(
context: context,
barrierDismissible: barrierDismissible,
builder: (BuildContext context) {
final size = MediaQuery.of(context).size;
return AlertDialog(
alignment: Alignment.center,
content: SizedBox(
height: dialogHeight ?? size.height * 0.15,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (iconPath != null)
SvgPicture.asset(
iconPath,
height: iconHeight ?? 35,
width: iconWidth ?? 35,
),
if (title != null)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
title,
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
fontSize: 20,
fontWeight: FontWeight.w400,
color: Colors.black),
),
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
message,
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.black),
textAlign: TextAlign.center,
),
),
],
),
),
actionsAlignment: MainAxisAlignment.center,
actions: <Widget>[
TextButton(
onPressed: onOkPressed ?? () => Navigator.of(context).pop(),
child: Text(
'OK',
style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontWeight: FontWeight.w400,
color: ColorsManager.blackColor,
fontSize: 16),
),
),
],
);
},
);
}

View File

@ -15,7 +15,7 @@ class DynamicTable extends StatefulWidget {
final void Function(int, bool?)? onRowCheckboxChanged; final void Function(int, bool?)? onRowCheckboxChanged;
const DynamicTable({ const DynamicTable({
Key? key, super.key,
required this.headers, required this.headers,
required this.data, required this.data,
required this.size, required this.size,
@ -25,7 +25,7 @@ class DynamicTable extends StatefulWidget {
this.cellDecoration, this.cellDecoration,
this.selectAll, this.selectAll,
this.onRowCheckboxChanged, this.onRowCheckboxChanged,
}) : super(key: key); });
@override @override
_DynamicTableState createState() => _DynamicTableState(); _DynamicTableState createState() => _DynamicTableState();
@ -66,7 +66,7 @@ class _DynamicTableState extends State<DynamicTable> {
return Container( return Container(
decoration: widget.cellDecoration, decoration: widget.cellDecoration,
child: Padding( child: Padding(
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(2.0),
child: child:
ListView( ListView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
@ -102,7 +102,7 @@ class _DynamicTableState extends State<DynamicTable> {
SvgPicture.asset( SvgPicture.asset(
Assets.emptyTable Assets.emptyTable
), ),
SizedBox(height: 15,), const SizedBox(height: 15,),
Text('No Passwords',style: Theme.of(context).textTheme.bodySmall!.copyWith(color:ColorsManager.grayColor ),) Text('No Passwords',style: Theme.of(context).textTheme.bodySmall!.copyWith(color:ColorsManager.grayColor ),)
], ],
), ),
@ -122,9 +122,9 @@ class _DynamicTableState extends State<DynamicTable> {
return Row( return Row(
children: [ children: [
if (widget.withCheckBox) if (widget.withCheckBox)
_buildRowCheckbox(index), _buildRowCheckbox(index,widget.size.height*0.10),
...row.map((cell) => ...row.map((cell) =>
_buildTableCell(cell.toString())).toList(), _buildTableCell(cell.toString(),widget.size.height*0.10)).toList(),
], ],
); );
}, },
@ -141,8 +141,12 @@ class _DynamicTableState extends State<DynamicTable> {
} }
Widget _buildSelectAllCheckbox() { Widget _buildSelectAllCheckbox() {
return SizedBox( return Container(
width: 50, padding: const EdgeInsets.all(8.0),
decoration: const BoxDecoration(
border: Border.symmetric(
vertical: BorderSide(color: ColorsManager.boxDivider))),
child: Checkbox( child: Checkbox(
value: _selectAll, value: _selectAll,
onChanged: _toggleSelectAll, onChanged: _toggleSelectAll,
@ -150,16 +154,27 @@ class _DynamicTableState extends State<DynamicTable> {
); );
} }
Widget _buildRowCheckbox(int index) { Widget _buildRowCheckbox(int index,size) {
return SizedBox( return Container(
width: 50, padding: const EdgeInsets.all(8.0),
child: Checkbox(
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], value: _selected[index],
onChanged: (bool? value) { onChanged: (bool? value) {
_toggleRowSelection(index, value); _toggleRowSelection(index, value);
}, },
), ),)
); );
} }
Widget _buildTableHeaderCell(String title) { Widget _buildTableHeaderCell(String title) {
@ -177,11 +192,11 @@ class _DynamicTableState extends State<DynamicTable> {
); );
} }
Widget _buildTableCell(String content) { Widget _buildTableCell(String content,size) {
return Expanded( return Expanded(
child: Container( child: Container(
height: 80, height:size ,
padding: const EdgeInsets.all(15.0), padding: const EdgeInsets.all(5.0),
decoration: const BoxDecoration( decoration: const BoxDecoration(
border: Border( border: Border(
bottom: BorderSide( bottom: BorderSide(

View File

@ -33,8 +33,7 @@ class CustomWebTextField extends StatelessWidget {
children: [ children: [
Text('* ', Text('* ',
style: Theme.of(context) style: Theme.of(context)
.textTheme .textTheme.bodyMedium!
.bodyMedium!
.copyWith(color: Colors.red), .copyWith(color: Colors.red),
), ),
Text(textFieldName, style: Theme.of(context).textTheme.bodySmall!.copyWith( Text(textFieldName, style: Theme.of(context).textTheme.bodySmall!.copyWith(
@ -46,10 +45,8 @@ class CustomWebTextField extends StatelessWidget {
child: Text( child: Text(
description??'', description??'',
style: Theme.of(context) style: Theme.of(context)
.textTheme .textTheme.bodySmall!
.bodySmall! .copyWith(fontSize: 9,
.copyWith(
fontSize: 9,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: ColorsManager.textGray), color: ColorsManager.textGray),
), ),
@ -65,11 +62,10 @@ class CustomWebTextField extends StatelessWidget {
color: Colors.grey.withOpacity(0.3), color: Colors.grey.withOpacity(0.3),
spreadRadius:2, spreadRadius:2,
blurRadius: 3, blurRadius: 3,
offset: Offset(1, 1), // changes position of shadow offset: const Offset(1, 1), // changes position of shadow
), ),
] ]
), ),
child: Container(
child: TextFormField( child: TextFormField(
validator: validator, validator: validator,
controller: controller, controller: controller,
@ -81,7 +77,6 @@ class CustomWebTextField extends StatelessWidget {
hintText: 'Please enter'), hintText: 'Please enter'),
), ),
), ),
),
], ],
); );
} }

View File

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
class HourPickerDialog extends StatefulWidget { class HourPickerDialog extends StatefulWidget {
final TimeOfDay initialTime; final TimeOfDay initialTime;
HourPickerDialog({required this.initialTime}); const HourPickerDialog({super.key, required this.initialTime});
@override @override
_HourPickerDialogState createState() => _HourPickerDialogState(); _HourPickerDialogState createState() => _HourPickerDialogState();
@ -24,7 +24,7 @@ class _HourPickerDialogState extends State<HourPickerDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AlertDialog(
title: Text('Select Hour'), title: const Text('Select Hour'),
content: Row( content: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@ -46,7 +46,7 @@ class _HourPickerDialogState extends State<HourPickerDialog> {
SizedBox(width: 16.0), SizedBox(width: 16.0),
DropdownButton<bool>( DropdownButton<bool>(
value: _isPm, value: _isPm,
items: [ items: const [
DropdownMenuItem( DropdownMenuItem(
value: false, value: false,
child: Text('AM'), child: Text('AM'),
@ -67,14 +67,14 @@ class _HourPickerDialogState extends State<HourPickerDialog> {
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.of(context).pop(null), onPressed: () => Navigator.of(context).pop(null),
child: Text('Cancel'), child: const Text('Cancel'),
), ),
TextButton( TextButton(
onPressed: () { onPressed: () {
int hour = _isPm ? _selectedHour + 12 : _selectedHour; int hour = _isPm ? _selectedHour + 12 : _selectedHour;
Navigator.of(context).pop(TimeOfDay(hour: hour, minute: 0)); Navigator.of(context).pop(TimeOfDay(hour: hour, minute: 0));
}, },
child: Text('OK'), child: const Text('OK'),
), ),
], ],
); );

View File

@ -66,7 +66,7 @@ class InfoDialog extends StatelessWidget {
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
child: Text('OK'), child: const Text('OK'),
), ),
], ],
); );

View File

@ -2,13 +2,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_state.dart';
import 'package:syncrow_web/pages/home/view/home_card.dart'; import 'package:syncrow_web/pages/home/view/home_card.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart';
import '../bloc/home_state.dart';
class HomeWebPage extends StatelessWidget { class HomeWebPage extends StatelessWidget {
HomeWebPage({super.key}); HomeWebPage({super.key});
@override @override

View File

@ -2,6 +2,7 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:syncrow_web/pages/common/custom_dialog.dart';
import 'package:syncrow_web/pages/common/hour_picker_dialog.dart'; import 'package:syncrow_web/pages/common/hour_picker_dialog.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart';
@ -9,9 +10,9 @@ import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
import 'package:syncrow_web/pages/visitor_password/model/schedule_model.dart'; import 'package:syncrow_web/pages/visitor_password/model/schedule_model.dart';
import 'package:syncrow_web/services/access_mang_api.dart'; import 'package:syncrow_web/services/access_mang_api.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/snack_bar.dart'; import 'package:syncrow_web/utils/snack_bar.dart';
class VisitorPasswordBloc class VisitorPasswordBloc
extends Bloc<VisitorPasswordEvent, VisitorPasswordState> { extends Bloc<VisitorPasswordEvent, VisitorPasswordState> {
VisitorPasswordBloc() : super(VisitorPasswordInitial()) { VisitorPasswordBloc() : super(VisitorPasswordInitial()) {
@ -23,10 +24,8 @@ class VisitorPasswordBloc
on<ToggleDaySelectionEvent>(toggleDaySelection); on<ToggleDaySelectionEvent>(toggleDaySelection);
on<SelectDeviceEvent>(selectDevice); on<SelectDeviceEvent>(selectDevice);
on<UpdateFilteredDevicesEvent>(_onUpdateFilteredDevices); on<UpdateFilteredDevicesEvent>(_onUpdateFilteredDevices);
on<OnlineOneTimePasswordEvent>(postOnlineOneTimePassword); on<OnlineOneTimePasswordEvent>(postOnlineOneTimePassword);
on<OnlineMultipleTimePasswordEvent>(postOnlineMultipleTimePassword); on<OnlineMultipleTimePasswordEvent>(postOnlineMultipleTimePassword);
on<OfflineMultipleTimePasswordEvent>(postOfflineMultipleTimePassword); on<OfflineMultipleTimePasswordEvent>(postOfflineMultipleTimePassword);
on<OfflineOneTimePasswordEvent>(postOfflineOneTimePassword); on<OfflineOneTimePasswordEvent>(postOfflineOneTimePassword);
on<SelectTimeEvent>(selectTimeOfLinePassword); on<SelectTimeEvent>(selectTimeOfLinePassword);
@ -58,16 +57,12 @@ class VisitorPasswordBloc
int? effectiveTimeTimeStamp; int? effectiveTimeTimeStamp;
int? expirationTimeTimeStamp; int? expirationTimeTimeStamp;
int? repeatEffectiveTimeTimeStamp;
int? repeatExpirationTimeTimeStamp;
DateTime? startTime = DateTime.now(); DateTime? startTime = DateTime.now();
DateTime? endTime; DateTime? endTime;
String startTimeAccess = 'Start Time'; String startTimeAccess = 'Start Time';
String endTimeAccess = 'End Time'; String endTimeAccess = 'End Time';
selectAccessType( selectAccessType(
SelectPasswordType event, Emitter<VisitorPasswordState> emit) { SelectPasswordType event, Emitter<VisitorPasswordState> emit) {
accessTypeSelected = event.type; accessTypeSelected = event.type;
@ -122,7 +117,8 @@ class VisitorPasswordBloc
timePicked.minute, timePicked.minute,
); );
final selectedTimestamp = selectedDateTime.millisecondsSinceEpoch ~/ 1000; final selectedTimestamp =
selectedDateTime.millisecondsSinceEpoch ~/ 1000;
if (event.isStart) { if (event.isStart) {
if (expirationTimeTimeStamp != null && if (expirationTimeTimeStamp != null &&
@ -133,9 +129,7 @@ class VisitorPasswordBloc
return; return;
} }
effectiveTimeTimeStamp = selectedTimestamp; effectiveTimeTimeStamp = selectedTimestamp;
startTimeAccess = selectedDateTime.toString().split('.').first; startTimeAccess = selectedDateTime.toString().split('.').first;
} else { } else {
if (effectiveTimeTimeStamp != null && if (effectiveTimeTimeStamp != null &&
selectedTimestamp < effectiveTimeTimeStamp!) { selectedTimestamp < effectiveTimeTimeStamp!) {
@ -145,11 +139,8 @@ class VisitorPasswordBloc
return; return;
} }
expirationTimeTimeStamp = selectedTimestamp; expirationTimeTimeStamp = selectedTimestamp;
endTimeAccess = selectedDateTime.toString().split('.').first; endTimeAccess = selectedDateTime.toString().split('.').first;
} }
emit(ChangeTimeState()); emit(ChangeTimeState());
emit(VisitorPasswordInitial()); emit(VisitorPasswordInitial());
} }
@ -201,10 +192,10 @@ class VisitorPasswordBloc
} }
//online password //online password
Future<void> postOnlineOneTimePassword(OnlineOneTimePasswordEvent event, Future<void> postOnlineOneTimePassword(OnlineOneTimePasswordEvent event,
Emitter<VisitorPasswordState> emit) async { Emitter<VisitorPasswordState> emit) async {
try { try {
emit(LoadingInitialState());
generate7DigitNumber(); generate7DigitNumber();
bool res = await AccessMangApi().postOnlineOneTime( bool res = await AccessMangApi().postOnlineOneTime(
email: event.email, email: event.email,
@ -213,27 +204,37 @@ class VisitorPasswordBloc
passwordName: event.passwordName, passwordName: event.passwordName,
effectiveTime: effectiveTimeTimeStamp.toString(), effectiveTime: effectiveTimeTimeStamp.toString(),
invalidTime: expirationTimeTimeStamp.toString()); invalidTime: expirationTimeTimeStamp.toString());
if (res = true) { if (res == true) {
Navigator.pop(event.context!); emit(SuccessState());
} else {
throw Exception('Failed to create password');
} }
emit(TableLoaded(data)); emit(TableLoaded(data));
} catch (e) { } catch (e) {
emit(FailedState(e.toString())); emit(FailedState(e.toString()));
Navigator.pop(event.context!);
stateDialog(
context: event.context!,
message: e.toString(),
title: 'Something Wrong');
} }
} }
Future<void> postOnlineMultipleTimePassword( Future<void> postOnlineMultipleTimePassword(
OnlineMultipleTimePasswordEvent event, OnlineMultipleTimePasswordEvent event,
Emitter<VisitorPasswordState> emit) async { Emitter<VisitorPasswordState> emit) async {
try { try {
generate7DigitNumber(); emit(LoadingInitialState());
await generate7DigitNumber();
bool res = await AccessMangApi().postOnlineMultipleTime( bool res = await AccessMangApi().postOnlineMultipleTime(
scheduleList: [ scheduleList: [
if (repeat) if (repeat)
Schedule( Schedule(
effectiveTime: getTimeFromDateTimeString(expirationTime), effectiveTime: getTimeFromDateTimeString(expirationTime),
invalidTime: getTimeFromDateTimeString(effectiveTime).toString(), invalidTime:
getTimeFromDateTimeString(effectiveTime).toString(),
workingDay: selectedDays, workingDay: selectedDays,
), ),
], ],
@ -243,9 +244,11 @@ class VisitorPasswordBloc
email: event.email, email: event.email,
devicesUuid: selectedDevices, devicesUuid: selectedDevices,
passwordName: event.passwordName); passwordName: event.passwordName);
if(res==true){ if (res == true) {
Navigator.pop(event.context!); emit(SuccessState());
emit(TableLoaded(data));
} }
} catch (e) { } catch (e) {
emit(FailedState(e.toString())); emit(FailedState(e.toString()));
} }
@ -255,11 +258,16 @@ class VisitorPasswordBloc
Future<void> postOfflineOneTimePassword(OfflineOneTimePasswordEvent event, Future<void> postOfflineOneTimePassword(OfflineOneTimePasswordEvent event,
Emitter<VisitorPasswordState> emit) async { Emitter<VisitorPasswordState> emit) async {
try { try {
generate7DigitNumber(); emit(LoadingInitialState());
await AccessMangApi().postOffLineOneTime( await generate7DigitNumber();
bool res = await AccessMangApi().postOffLineOneTime(
email: event.email, email: event.email,
devicesUuid: selectedDevices, devicesUuid: selectedDevices,
passwordName: event.passwordName); passwordName: event.passwordName);
if (res == true) {
emit(SuccessState());
emit(TableLoaded(data));
}
} catch (e) { } catch (e) {
emit(FailedState(e.toString())); emit(FailedState(e.toString()));
} }
@ -269,15 +277,19 @@ class VisitorPasswordBloc
OfflineMultipleTimePasswordEvent event, OfflineMultipleTimePasswordEvent event,
Emitter<VisitorPasswordState> emit) async { Emitter<VisitorPasswordState> emit) async {
try { try {
generate7DigitNumber(); emit(LoadingInitialState());
await AccessMangApi().postOffLineMultipleTime( await generate7DigitNumber();
bool res = await AccessMangApi().postOffLineMultipleTime(
email: event.email, email: event.email,
devicesUuid: selectedDevices, devicesUuid: selectedDevices,
passwordName: event.passwordName, passwordName: event.passwordName,
invalidTime: expirationTimeTimeStamp.toString(), invalidTime: expirationTimeTimeStamp.toString(),
effectiveTime: effectiveTimeTimeStamp.toString(), effectiveTime: effectiveTimeTimeStamp.toString(),
); );
if (res == true) {
emit(SuccessState());
emit(TableLoaded(data));
}
} catch (e) { } catch (e) {
emit(FailedState(e.toString())); emit(FailedState(e.toString()));
} }
@ -300,13 +312,11 @@ class VisitorPasswordBloc
} }
Future generate7DigitNumber() async { Future generate7DigitNumber() async {
emit(LoadingInitialState());
passwordController = ''; passwordController = '';
Random random = Random(); Random random = Random();
int min = 1000000; int min = 1000000;
int max = 9999999; int max = 9999999;
passwordController = (min + random.nextInt(max - min + 1)).toString(); passwordController = (min + random.nextInt(max - min + 1)).toString();
emit(GeneratePasswordState());
return passwordController; return passwordController;
} }
@ -319,12 +329,10 @@ class VisitorPasswordBloc
final deviceName = deviceNameController.text.toLowerCase(); final deviceName = deviceNameController.text.toLowerCase();
final deviceId = deviceIdController.text.toLowerCase(); final deviceId = deviceIdController.text.toLowerCase();
final unitName = unitNameController.text.toLowerCase(); final unitName = unitNameController.text.toLowerCase();
final filteredData = data.where((device) { final filteredData = data.where((device) {
final matchesDeviceName = device.name.toLowerCase().contains(deviceName); final matchesDeviceName = device.name.toLowerCase().contains(deviceName);
final matchesDeviceId = device.uuid.toLowerCase().contains(deviceId); final matchesDeviceId = device.uuid.toLowerCase().contains(deviceId);
// final matchesUnitName = device.unitName.toLowerCase().contains(unitName); // Assuming unitName is a property of the device // final matchesUnitName = device.unitName.toLowerCase().contains(unitName); // Assuming unitName is a property of the device
return matchesDeviceName && matchesDeviceId; return matchesDeviceName && matchesDeviceId;
}).toList(); }).toList();
add(UpdateFilteredDevicesEvent(filteredData)); add(UpdateFilteredDevicesEvent(filteredData));
@ -334,7 +342,6 @@ class VisitorPasswordBloc
Stream<VisitorPasswordState> mapEventToState( Stream<VisitorPasswordState> mapEventToState(
VisitorPasswordEvent event) async* { VisitorPasswordEvent event) async* {
if (event is FetchDevice) { if (event is FetchDevice) {
// Fetching logic...
} else if (event is UpdateFilteredDevicesEvent) { } else if (event is UpdateFilteredDevicesEvent) {
yield TableLoaded(event.filteredData); yield TableLoaded(event.filteredData);
} }
@ -347,10 +354,11 @@ class VisitorPasswordBloc
addDeviceToList(context) { addDeviceToList(context) {
selectedDevices = selectedDeviceIds; selectedDevices = selectedDeviceIds;
Navigator.of(context).pop(selectedDevices); // Close the dialog Navigator.of(context).pop(selectedDevices);
} }
Future<void> selectTimeOfLinePassword(SelectTimeEvent event, Emitter<VisitorPasswordState> emit) async { Future<void> selectTimeOfLinePassword(
SelectTimeEvent event, Emitter<VisitorPasswordState> emit) async {
emit(ChangeTimeState()); emit(ChangeTimeState());
final DateTime? picked = await showDatePicker( final DateTime? picked = await showDatePicker(
context: event.context, context: event.context,
@ -416,11 +424,10 @@ class VisitorPasswordBloc
endTime = event.val; endTime = event.val;
} }
} }
DateTime? convertStringToDateTime(String dateTimeString) { DateTime? convertStringToDateTime(String dateTimeString) {
try { try {
// Define the input format. Adjust this pattern based on your input string format.
final DateFormat inputFormat = DateFormat('yyyy-MM-dd HH:mm:ss'); final DateFormat inputFormat = DateFormat('yyyy-MM-dd HH:mm:ss');
// Convert the string to a DateTime object.
DateTime dateTime = inputFormat.parse(dateTimeString); DateTime dateTime = inputFormat.parse(dateTimeString);
return dateTime; return dateTime;
} catch (e) { } catch (e) {
@ -432,8 +439,6 @@ class VisitorPasswordBloc
String getTimeFromDateTimeString(String dateTimeString) { String getTimeFromDateTimeString(String dateTimeString) {
DateTime? dateTime = convertStringToDateTime(dateTimeString); DateTime? dateTime = convertStringToDateTime(dateTimeString);
if (dateTime == null) return ''; if (dateTime == null) return '';
// Extract the time component from the DateTime object.
return DateFormat('HH:mm').format(dateTime); return DateFormat('HH:mm').format(dateTime);
} }
@ -444,4 +449,27 @@ class VisitorPasswordBloc
return null; return null;
} }
Future<void> stateDialog({
BuildContext? context,
String? message,
String? title,
dynamic actions,
}) {
return showCustomDialog(
context: context!,
message: message!,
iconPath: Assets.deviceNoteIcon,
title: title,
dialogHeight: 150,
actions: actions ??
<Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'),
),
],
);
}
} }

View File

@ -77,11 +77,12 @@ class OnlineMultipleTimePasswordEvent extends VisitorPasswordEvent {
//offline password //offline password
class OfflineOneTimePasswordEvent extends VisitorPasswordEvent { class OfflineOneTimePasswordEvent extends VisitorPasswordEvent {
final BuildContext? context;
final String? email; final String? email;
final String? passwordName; final String? passwordName;
const OfflineOneTimePasswordEvent({this.email,this.passwordName}); const OfflineOneTimePasswordEvent({this.email,this.passwordName,this.context});
@override @override
List<Object> get props => [email!,passwordName!,]; List<Object> get props => [email!,passwordName!,context!,];
} }
class OfflineMultipleTimePasswordEvent extends VisitorPasswordEvent { class OfflineMultipleTimePasswordEvent extends VisitorPasswordEvent {
@ -89,11 +90,12 @@ class OfflineMultipleTimePasswordEvent extends VisitorPasswordEvent {
final String? passwordName; final String? passwordName;
final String? invalidTime; final String? invalidTime;
final String? effectiveTime; final String? effectiveTime;
final BuildContext? context;
const OfflineMultipleTimePasswordEvent({this.email,this.passwordName,this.invalidTime,this.effectiveTime}); const OfflineMultipleTimePasswordEvent({this.context,this.email,this.passwordName,this.invalidTime,this.effectiveTime});
@override @override
List<Object> get props => [email!,passwordName!,invalidTime!,effectiveTime!]; List<Object> get props => [email!,passwordName!,invalidTime!,effectiveTime!,context!];
} }

View File

@ -43,7 +43,7 @@ class LoadingInitialState extends VisitorPasswordState {}
class ChangeTimeState extends VisitorPasswordState {} class ChangeTimeState extends VisitorPasswordState {}
class TimeSelectedState extends VisitorPasswordState {} class TimeSelectedState extends VisitorPasswordState {}
class DeviceLoaded extends VisitorPasswordState {} class DeviceLoaded extends VisitorPasswordState {}
class GeneratePasswordState extends VisitorPasswordState {} class SuccessState extends VisitorPasswordState {}
class FailedState extends VisitorPasswordState { class FailedState extends VisitorPasswordState {
final String message; final String message;

View File

@ -12,7 +12,6 @@ import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/constants/const.dart'; import 'package:syncrow_web/utils/constants/const.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
class AddDeviceDialog extends StatelessWidget { class AddDeviceDialog extends StatelessWidget {
const AddDeviceDialog({super.key}); const AddDeviceDialog({super.key});
@override @override
@ -54,7 +53,7 @@ class AddDeviceDialog extends StatelessWidget {
width: 15, width: 15,
), ),
), ),
SizedBox(width: 10,), const SizedBox(width: 10,),
Text('Only online accessible devices can be added', Text('Only online accessible devices can be added',
style: Theme.of(context).textTheme.bodySmall!.copyWith( style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
@ -63,13 +62,15 @@ class AddDeviceDialog extends StatelessWidget {
], ],
) )
), ),
SizedBox(height: 20,), const SizedBox(height: 20,),
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end,
textBaseline: TextBaseline.alphabetic,
children: [ children: [
Expanded( Expanded(
flex: 2, flex: 4,
child: CustomWebTextField( child: CustomWebTextField(
controller: visitorBloc.deviceNameController, controller: visitorBloc.deviceNameController,
isRequired: true, isRequired: true,
@ -79,7 +80,7 @@ class AddDeviceDialog extends StatelessWidget {
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Expanded( Expanded(
flex: 2, flex: 4,
child: CustomWebTextField( child: CustomWebTextField(
controller: visitorBloc.deviceIdController, controller: visitorBloc.deviceIdController,
isRequired: true, isRequired: true,
@ -89,7 +90,7 @@ class AddDeviceDialog extends StatelessWidget {
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Expanded( Expanded(
flex: 2, flex: 4,
child: CustomWebTextField( child: CustomWebTextField(
controller: visitorBloc.unitNameController, controller: visitorBloc.unitNameController,
isRequired: true, isRequired: true,
@ -97,21 +98,16 @@ class AddDeviceDialog extends StatelessWidget {
description: '', description: '',
), ),
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Column( Expanded(
crossAxisAlignment: CrossAxisAlignment.center, flex: 2,
children: [
const SizedBox(height: 25),
Center(
child: Container( child: Container(
height: 43, child: SizedBox(
width: 100, width: size.width * 0.06,
decoration: containerDecoration,
child: Center( child: Center(
child: DefaultButton( child: DefaultButton(
onPressed: () { onPressed: () {
visitorBloc.filterDevices(); // Call filter function visitorBloc.filterDevices();
}, },
borderRadius: 9, borderRadius: 9,
child: const Text('Search'), child: const Text('Search'),
@ -119,26 +115,16 @@ class AddDeviceDialog extends StatelessWidget {
), ),
), ),
), ),
],
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Column( Expanded(
crossAxisAlignment: CrossAxisAlignment.center, flex: 2,
children: [
const SizedBox(height: 25),
Center(
child: Container( child: Container(
height: 43, width: size.width * 0.06,
width: 100,
decoration: containerDecoration,
child: Center(
child: DefaultButton( child: DefaultButton(
backgroundColor: ColorsManager.whiteColors, backgroundColor: ColorsManager.whiteColors,
borderRadius: 9, borderRadius: 9,
child: Text( child: Text('Reset',
'Reset',
style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black), style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black),
), ),
onPressed: () { onPressed: () {
@ -149,15 +135,12 @@ class AddDeviceDialog extends StatelessWidget {
}, },
), ),
), ),
), )
),
],
),
], ],
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Expanded( Expanded(
flex: 3,
child: state is TableLoaded child: state is TableLoaded
? DynamicTable( ? DynamicTable(
cellDecoration: containerDecoration, cellDecoration: containerDecoration,

View File

@ -1,11 +1,10 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/common/custom_web_textfield.dart'; import 'package:syncrow_web/pages/common/custom_web_textfield.dart';
import 'package:syncrow_web/pages/common/date_time_widget.dart'; import 'package:syncrow_web/pages/common/date_time_widget.dart';
import 'package:syncrow_web/pages/common/default_button.dart'; import 'package:syncrow_web/pages/common/default_button.dart';
import 'package:syncrow_web/pages/common/info_dialog.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart';
@ -21,13 +20,31 @@ class VisitorPasswordDialog extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
var text = Theme.of(context).textTheme.bodySmall!.copyWith(
color: Colors.black,fontSize: 13);
return BlocProvider( return BlocProvider(
create: (context) => VisitorPasswordBloc(), create: (context) => VisitorPasswordBloc(),
child: BlocListener<VisitorPasswordBloc, VisitorPasswordState>(
listener: (context, state) {
final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context);
if (state is SuccessState) {
visitorBloc.stateDialog(
context: context,
message: 'Password Created Successfully',
title: 'Send Success',
);
} else if (state is FailedState) {
visitorBloc.stateDialog(
context: context,
message: state.message,
title: 'Something Wrong',
);
}
},
child: BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>( child: BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>(
builder: (BuildContext context, VisitorPasswordState state) { builder: (BuildContext context, VisitorPasswordState state) {
final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context); final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context);
bool isRepeat = bool isRepeat = state is IsRepeatState ? state.repeat : visitorBloc.repeat;
state is IsRepeatState ? state.repeat : visitorBloc.repeat;
return AlertDialog( return AlertDialog(
backgroundColor: Colors.white, backgroundColor: Colors.white,
title: Text( title: Text(
@ -37,7 +54,9 @@ class VisitorPasswordDialog extends StatelessWidget {
fontSize: 24, fontSize: 24,
color: Colors.black), color: Colors.black),
), ),
content: SingleChildScrollView( content:
state is LoadingInitialState ?const Center(child: CircularProgressIndicator()):
SingleChildScrollView(
child: Form( child: Form(
key: visitorBloc.forgetFormKey, key: visitorBloc.forgetFormKey,
child: Padding( child: Padding(
@ -83,14 +102,11 @@ class VisitorPasswordDialog extends StatelessWidget {
children: [ children: [
Text( Text(
'* ', '* ',
style: Theme.of(context) style: Theme.of(context).textTheme
.textTheme .bodyMedium!.copyWith(color: Colors.red),
.bodyMedium!
.copyWith(color: Colors.red),
), ),
Text('Access Type', Text('Access Type',
style: Theme.of(context).textTheme.bodySmall!.copyWith( style:text ),
color: Colors.black,fontSize: 13),),
], ],
), ),
Row( Row(
@ -99,30 +115,9 @@ class VisitorPasswordDialog extends StatelessWidget {
width: size.width * 0.15, width: size.width * 0.15,
child: RadioListTile<String>( child: RadioListTile<String>(
title: Text('Online Password', title: Text('Online Password',
style: Theme.of(context).textTheme.bodySmall!.copyWith( style: text,
color: Colors.black,fontSize: 13),
), ),
value: 'Online Password', value: 'Online Password',
groupValue: (state is PasswordTypeSelected)
? state.selectedType
: visitorBloc.accessTypeSelected,
onChanged: (String? value) {
if (value != null) {
print(value);
context
.read<VisitorPasswordBloc>()
.add(SelectPasswordType(value));
}
},
),
),
SizedBox(
width: size.width * 0.15,
child: RadioListTile<String>(
title: Text('Offline Password',
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Colors.black,fontSize: 13),),
value: 'Offline Password',
groupValue: (state is PasswordTypeSelected) groupValue: (state is PasswordTypeSelected)
? state.selectedType ? state.selectedType
: visitorBloc.accessTypeSelected, : visitorBloc.accessTypeSelected,
@ -134,20 +129,34 @@ class VisitorPasswordDialog extends StatelessWidget {
}, },
), ),
), ),
SizedBox(
width: size.width * 0.15,
child: RadioListTile<String>(
title: Text('Offline Password',
style:text ),
value: 'Offline Password',
groupValue: (state is PasswordTypeSelected)
? state.selectedType
: visitorBloc.accessTypeSelected,
onChanged: (String? value) {
if (value != null) {
context.read<VisitorPasswordBloc>().add(SelectPasswordType(value));
}
},
),
),
SizedBox( SizedBox(
width: size.width * 0.15, width: size.width * 0.15,
child: RadioListTile<String>( child: RadioListTile<String>(
title: Text('Dynamic Password', title: Text('Dynamic Password',
style: Theme.of(context).textTheme.bodySmall!.copyWith( style: text,),
color: Colors.black,fontSize: 13),),
value: 'Dynamic Password', value: 'Dynamic Password',
groupValue: (state is PasswordTypeSelected) groupValue: (state is PasswordTypeSelected)
? state.selectedType ? state.selectedType
: visitorBloc.accessTypeSelected, : visitorBloc.accessTypeSelected,
onChanged: (String? value) { onChanged: (String? value) {
if (value != null) { if (value != null) {
context context.read<VisitorPasswordBloc>()
.read<VisitorPasswordBloc>()
.add(SelectPasswordType(value)); .add(SelectPasswordType(value));
visitorBloc.usageFrequencySelected = ''; visitorBloc.usageFrequencySelected = '';
} }
@ -167,21 +176,17 @@ class VisitorPasswordDialog extends StatelessWidget {
], ],
), ),
visitorBloc.accessTypeSelected == 'Dynamic Password' visitorBloc.accessTypeSelected == 'Dynamic Password'
? SizedBox() ? const SizedBox()
: Column( : Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
children: [ children: [
Text( Text('* ',
'* ', style: Theme.of(context).textTheme.bodyMedium!
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.red), .copyWith(color: Colors.red),
), ),
Text('Usage Frequency',style: Theme.of(context).textTheme.bodySmall!.copyWith( Text('Usage Frequency',style:text ,),
color: Colors.black,fontSize: 13),),
], ],
), ),
Row( Row(
@ -190,8 +195,7 @@ class VisitorPasswordDialog extends StatelessWidget {
width: 200, width: 200,
child: RadioListTile<String>( child: RadioListTile<String>(
title: Text('One-Time', title: Text('One-Time',
style: Theme.of(context).textTheme.bodySmall!.copyWith( style:text ,),
color: Colors.black,fontSize: 13),),
value: 'One-Time', value: 'One-Time',
groupValue: groupValue:
(state is UsageFrequencySelected) (state is UsageFrequencySelected)
@ -209,16 +213,14 @@ class VisitorPasswordDialog extends StatelessWidget {
width: 200, width: 200,
child: RadioListTile<String>( child: RadioListTile<String>(
title: Text('Periodic', title: Text('Periodic',
style: Theme.of(context).textTheme.bodySmall!.copyWith( style: text),
color: Colors.black,fontSize: 13),),
value: 'Periodic', value: 'Periodic',
groupValue: (state is UsageFrequencySelected) groupValue: (state is UsageFrequencySelected)
? state.selectedFrequency ? state.selectedFrequency
: visitorBloc.usageFrequencySelected, : visitorBloc.usageFrequencySelected,
onChanged: (String? value) { onChanged: (String? value) {
if (value != null) { if (value != null) {
context context.read<VisitorPasswordBloc>()
.read<VisitorPasswordBloc>()
.add(SelectUsageFrequency(value)); .add(SelectUsageFrequency(value));
} }
}, },
@ -266,16 +268,12 @@ class VisitorPasswordDialog extends StatelessWidget {
children: [ children: [
Row( Row(
children: [ children: [
Text( Text('* ',
'* ', style: Theme.of(context).textTheme.bodyMedium!
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.red), .copyWith(color: Colors.red),
), ),
Text('Access Devices', Text('Access Devices',
style: Theme.of(context).textTheme.bodySmall!.copyWith( style:text ,),
color: Colors.black,fontSize: 13),),
], ],
), ),
Text( Text(
@ -293,8 +291,7 @@ class VisitorPasswordDialog extends StatelessWidget {
child: Column( child: Column(
children: [ children: [
Text('Repeat', Text('Repeat',
style: Theme.of(context).textTheme.bodySmall!.copyWith( style:text),
color: Colors.black,fontSize: 13),),
Transform.scale( Transform.scale(
scale: .8, scale: .8,
child: CupertinoSwitch( child: CupertinoSwitch(
@ -325,12 +322,9 @@ class VisitorPasswordDialog extends StatelessWidget {
}, },
).then((listDevice) { ).then((listDevice) {
if(listDevice!=null){ if(listDevice!=null){
print('selectedDevices==$listDevice');
visitorBloc.selectedDevices = listDevice; visitorBloc.selectedDevices = listDevice;
} }
}); });
}, },
borderRadius: 8, borderRadius: 8,
child: Text('+ Add Device',style: Theme.of(context).textTheme.bodySmall!.copyWith( child: Text('+ Add Device',style: Theme.of(context).textTheme.bodySmall!.copyWith(
@ -370,16 +364,99 @@ class VisitorPasswordDialog extends StatelessWidget {
child: DefaultButton( child: DefaultButton(
onPressed: () { onPressed: () {
if (visitorBloc.forgetFormKey.currentState!.validate()) { if (visitorBloc.forgetFormKey.currentState!.validate()) {
showDialog( if(visitorBloc.selectedDevices.isNotEmpty){
if(visitorBloc.effectiveTimeTimeStamp!=null&&visitorBloc.expirationTimeTimeStamp!=null) {
setPasswordFunction(context, size, visitorBloc);
}
else{
visitorBloc.stateDialog(context:
context,message: 'Please select Access Period to continue',title: 'Access Period');
}
}else{
visitorBloc.stateDialog(context:
context,message: 'Please select devices to continue',title: 'Select Devices');
}
}
},
borderRadius: 8,
child: Text('Ok', style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontWeight: FontWeight.w400,
color: ColorsManager.whiteColors,fontSize: 16),),
),
),
],
); },
),
),
);
}
Future<void> setPasswordFunction(
BuildContext context,
Size size,
VisitorPasswordBloc visitorBloc,
) {
return showDialog<void>(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
builder: (BuildContext context) { builder: (BuildContext context) {
return InfoDialog( return BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>(
size: size, builder: (context, state) {
title: 'Set Password', if (state is LoadingInitialState) {
content: // Show loading indicator while loading
return AlertDialog(
alignment: Alignment.center,
content: SizedBox(
height: size.height * 0.25,
child: Center(
child: CircularProgressIndicator(), // Display a loading spinner
),
),
);
}else{
return AlertDialog(
alignment: Alignment.center,
content: SizedBox(
height: size.height * 0.25,
child: Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
child: SvgPicture.asset(
Assets.deviceNoteIcon,
height: 35,
width: 35,
),
),
Text(
'Set Password',
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
fontSize: 30,
fontWeight: FontWeight.w400,
color: Colors.black,
),
),
],
),
const SizedBox(width: 15),
Text(
'This action will update all of the selected\n door locks passwords in the property.\n\nAre you sure you want to continue?', 'This action will update all of the selected\n door locks passwords in the property.\n\nAre you sure you want to continue?',
actions: [ textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.w400,
fontSize: 18,
),
),
],
),
),
actionsAlignment: MainAxisAlignment.center,
actions: <Widget>[
Container( Container(
decoration: containerDecoration, decoration: containerDecoration,
width: size.width * 0.1, width: size.width * 0.1,
@ -393,7 +470,9 @@ class VisitorPasswordDialog extends StatelessWidget {
'Cancel', 'Cancel',
style: Theme.of(context).textTheme.bodySmall!.copyWith( style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: ColorsManager.blackColor,fontSize: 16), color: ColorsManager.blackColor,
fontSize: 16,
),
), ),
), ),
), ),
@ -403,59 +482,55 @@ class VisitorPasswordDialog extends StatelessWidget {
child: DefaultButton( child: DefaultButton(
borderRadius: 8, borderRadius: 8,
onPressed: () { onPressed: () {
Navigator.pop(context);
if (visitorBloc.usageFrequencySelected == 'One-Time' && if (visitorBloc.usageFrequencySelected == 'One-Time' &&
visitorBloc.accessTypeSelected == 'Online Password') { visitorBloc.accessTypeSelected == 'Online Password') {
visitorBloc.add( visitorBloc.add(OnlineOneTimePasswordEvent(
OnlineOneTimePasswordEvent(
context: context, context: context,
passwordName: visitorBloc.userNameController.text, passwordName: visitorBloc.userNameController.text,
email: visitorBloc.emailController.text)); email: visitorBloc.emailController.text,
));
} else if (visitorBloc.usageFrequencySelected == 'Periodic' && } else if (visitorBloc.usageFrequencySelected == 'Periodic' &&
visitorBloc.accessTypeSelected == 'Online Password') { visitorBloc.accessTypeSelected == 'Online Password') {
visitorBloc.add(OnlineMultipleTimePasswordEvent( visitorBloc.add(OnlineMultipleTimePasswordEvent(
passwordName: visitorBloc.userNameController.text, passwordName: visitorBloc.userNameController.text,
email: visitorBloc.emailController.text, email: visitorBloc.emailController.text,
effectiveTime: visitorBloc.effectiveTimeTimeStamp.toString(), effectiveTime: visitorBloc.effectiveTimeTimeStamp.toString(),
invalidTime: visitorBloc.expirationTimeTimeStamp.toString())); invalidTime: visitorBloc.expirationTimeTimeStamp.toString(),
));
} else if (visitorBloc.usageFrequencySelected == 'One-Time' && } else if (visitorBloc.usageFrequencySelected == 'One-Time' &&
visitorBloc.accessTypeSelected == 'Offline Password') { visitorBloc.accessTypeSelected == 'Offline Password') {
visitorBloc.add(OfflineOneTimePasswordEvent( visitorBloc.add(OfflineOneTimePasswordEvent(
context: context,
passwordName: visitorBloc.userNameController.text, passwordName: visitorBloc.userNameController.text,
email: visitorBloc.emailController.text, email: visitorBloc.emailController.text,
)); ));
} else if (visitorBloc.usageFrequencySelected == 'Periodic' && } else if (visitorBloc.usageFrequencySelected == 'Periodic' &&
visitorBloc.accessTypeSelected == 'Offline Password') { visitorBloc.accessTypeSelected == 'Offline Password') {
visitorBloc.add( visitorBloc.add(OfflineMultipleTimePasswordEvent(
OfflineMultipleTimePasswordEvent(
passwordName: visitorBloc.userNameController.text, passwordName: visitorBloc.userNameController.text,
email: visitorBloc.emailController.text, email: visitorBloc.emailController.text,
effectiveTime: visitorBloc.effectiveTimeTimeStamp.toString(), effectiveTime: visitorBloc.effectiveTimeTimeStamp.toString(),
invalidTime: visitorBloc.expirationTimeTimeStamp.toString())); invalidTime: visitorBloc.expirationTimeTimeStamp.toString(),
));
} }
}, },
child: Text('Ok', child: Text(
'Ok',
style: Theme.of(context).textTheme.bodySmall!.copyWith( style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: ColorsManager.whiteColors,fontSize: 16), color: ColorsManager.whiteColors,
fontSize: 16,
),
), ),
), ),
), ),
], ],
); );
},
);
} }
}, },
borderRadius: 8,
child: Text('Ok', style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontWeight: FontWeight.w400,
color: ColorsManager.whiteColors,fontSize: 16),),
),
),
],
); );
}, },
),
); );
} }
} }

View File

@ -2,12 +2,11 @@ import 'dart:convert';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:syncrow_web/pages/access_management/model/password_model.dart'; import 'package:syncrow_web/pages/access_management/model/password_model.dart';
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
import 'package:syncrow_web/pages/visitor_password/model/schedule_model.dart'; import 'package:syncrow_web/pages/visitor_password/model/schedule_model.dart';
import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart'; import 'package:syncrow_web/utils/constants/api_const.dart';
import '../pages/visitor_password/model/device_model.dart';
class AccessMangApi{ class AccessMangApi{
Future<List<PasswordModel>> fetchVisitorPassword() async { Future<List<PasswordModel>> fetchVisitorPassword() async {
@ -17,7 +16,6 @@ class AccessMangApi{
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
List<dynamic> jsonData = json; List<dynamic> jsonData = json;
print('Password List: $json');
List<PasswordModel> passwordList = jsonData.map((jsonItem) { List<PasswordModel> passwordList = jsonData.map((jsonItem) {
return PasswordModel.fromJson(jsonItem); return PasswordModel.fromJson(jsonItem);
}).toList(); }).toList();
@ -38,7 +36,6 @@ class AccessMangApi{
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
List<dynamic> jsonData = json; List<dynamic> jsonData = json;
print('fetchDevices List: $json');
List<DeviceModel> passwordList = jsonData.map((jsonItem) { List<DeviceModel> passwordList = jsonData.map((jsonItem) {
return DeviceModel.fromJson(jsonItem); return DeviceModel.fromJson(jsonItem);
}).toList(); }).toList();
@ -60,18 +57,6 @@ class AccessMangApi{
String? invalidTime, String? invalidTime,
List<String>? devicesUuid}) async { List<String>? devicesUuid}) async {
try { try {
print('postOfflineOneTime List: ${
jsonEncode({
"email": email,
"passwordName": passwordName,
"password": password,
"devicesUuid": devicesUuid,
"effectiveTime":effectiveTime ,
"invalidTime": invalidTime
})
}');
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.sendOnlineOneTime, path: ApiEndpoints.sendOnlineOneTime,
body: jsonEncode({ body: jsonEncode({
@ -84,7 +69,6 @@ class AccessMangApi{
}), }),
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
print('postOfflineOneTime List: $json');
if(json['statusCode'].toString()=='201'){ if(json['statusCode'].toString()=='201'){
return true; return true;
}else{ }else{
@ -95,7 +79,6 @@ class AccessMangApi{
return response; return response;
} on DioException catch (e) { } on DioException catch (e) {
debugPrint('Error: ${e.message}'); debugPrint('Error: ${e.message}');
debugPrint('Error fetching ${e.response!.statusMessage}'); debugPrint('Error fetching ${e.response!.statusMessage}');
return false; return false;
} }
@ -121,15 +104,11 @@ class AccessMangApi{
if (scheduleList != null) { if (scheduleList != null) {
body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList(); body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList();
} }
print('createPassword =${jsonEncode(body)}');
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.sendOnlineMultipleTime, path: ApiEndpoints.sendOnlineMultipleTime,
body: jsonEncode(body), body: jsonEncode(body),
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
print('createPassword =${json}');
if(json['data']['successOperations'][0]['success'].toString()=='true'){ if(json['data']['successOperations'][0]['success'].toString()=='true'){
return true; return true;
}else{ }else{
@ -149,15 +128,6 @@ class AccessMangApi{
Future postOffLineOneTime({String? email,String? passwordName,List<String>? devicesUuid}) async { Future postOffLineOneTime({String? email,String? passwordName,List<String>? devicesUuid}) async {
try { try {
print('postOfflineOneTime List: ${
{
"email": email,
"passwordName": passwordName,
"devicesUuid": devicesUuid
}
}');
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.sendOffLineOneTime, path: ApiEndpoints.sendOffLineOneTime,
body: jsonEncode({ body: jsonEncode({
@ -167,9 +137,13 @@ class AccessMangApi{
}), }),
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
List<dynamic> jsonData = json; if (json['data']['successOperations'][0]['success'].toString() ==
print('postOfflineOneTime List: $json'); 'true') {
}, return true;
} else {
return false;
}
}
); );
return response; return response;
} catch (e) { } catch (e) {
@ -187,15 +161,6 @@ class AccessMangApi{
}) async { }) async {
try { try {
print('postOfflineOneTime List: ${
{
"email": email,
"passwordName": passwordName,
"devicesUuid": devicesUuid
}
}');
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.sendOffLineOneTime, path: ApiEndpoints.sendOffLineOneTime,
body: jsonEncode({ body: jsonEncode({
@ -207,9 +172,13 @@ class AccessMangApi{
}), }),
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
List<dynamic> jsonData = json; if (json['data']['successOperations'][0]['success'].toString() ==
print('postOfflineOneTime List: $json'); 'true') {
}, return true;
} else {
return false;
}
}
); );
return response; return response;
} catch (e) { } catch (e) {

View File

@ -1,4 +1,3 @@
import 'dart:convert';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -8,9 +7,7 @@ import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart'; import 'package:syncrow_web/utils/constants/api_const.dart';
class AuthenticationAPI { class AuthenticationAPI {
static Future<Token> loginWithEmail({required var model}) async { static Future<Token> loginWithEmail({required var model}) async {
print('model=$model');
final response = await HTTPService().post( final response = await HTTPService().post(
path: ApiEndpoints.login, path: ApiEndpoints.login,
body: model.toJson(), body: model.toJson(),
@ -46,19 +43,15 @@ class AuthenticationAPI {
}, },
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
print('object==$json');
return 30; return 30;
} }
); );
return 30; return 30;
} on DioException catch (e) { } on DioException catch (e) {
if (e.response != null) { if (e.response != null) {
if (e.response!.statusCode == 400) { if (e.response!.statusCode == 400) {
// Handle 400 Bad Request
final errorData = e.response!.data; final errorData = e.response!.data;
String errorMessage = errorData['message']; String errorMessage = errorData['message'];
debugPrint('Unexpected Error: $errorMessage');
if(errorMessage=='User not found'){ if(errorMessage=='User not found'){
return 1; return 1;
}else{ }else{
@ -87,8 +80,6 @@ class AuthenticationAPI {
body: {"email": email, "type": "PASSWORD", "otpCode": otpCode}, body: {"email": email, "type": "PASSWORD", "otpCode": otpCode},
showServerMessage: true, showServerMessage: true,
expectedResponseModel: (json) { expectedResponseModel: (json) {
print('json=$json');
if (json['message'] == 'Otp Verified Successfully') { if (json['message'] == 'Otp Verified Successfully') {
return true; return true;
} else { } else {
@ -99,12 +90,9 @@ class AuthenticationAPI {
}on DioException catch (e){ }on DioException catch (e){
if (e.response != null) { if (e.response != null) {
if (e.response!.statusCode == 400) { if (e.response!.statusCode == 400) {
// Handle 400 Bad Request
final errorData = e.response!.data; final errorData = e.response!.data;
String errorMessage = errorData['message']; String errorMessage = errorData['message'];
debugPrint('Unexpected Error: $errorMessage');
return errorMessage; return errorMessage;
} }
} else { } else {
debugPrint('Error: ${e.message}'); debugPrint('Error: ${e.message}');

View File

@ -3,14 +3,12 @@ import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/web_layout/web_app_bar.dart'; import 'package:syncrow_web/web_layout/web_app_bar.dart';
import 'menu_sidebar.dart'; import 'menu_sidebar.dart';
class WebScaffold extends StatelessWidget { class WebScaffold extends StatelessWidget {
final bool enableMenuSideba; final bool enableMenuSideba;
final Widget? appBarTitle; final Widget? appBarTitle;
final List<Widget>? appBarBody; final List<Widget>? appBarBody;
final Widget? scaffoldBody; final Widget? scaffoldBody;
const WebScaffold({super.key,this.appBarTitle,this.appBarBody,this.scaffoldBody,this.enableMenuSideba=true}); const WebScaffold({super.key,this.appBarTitle,this.appBarBody,this.scaffoldBody,this.enableMenuSideba=true});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(