Merge pull request #11 from SyncrowIOT/aug_bug_fixes

Aug bug fixes
This commit is contained in:
Abdullah
2024-09-03 11:48:17 +03:00
committed by GitHub
18 changed files with 224 additions and 113 deletions

View File

@ -82,6 +82,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
const SizedBox(height: 20),
Expanded(
child: DynamicTable(
uuidIndex: 1,
isEmpty: filteredData.isEmpty,
withCheckBox: false,
size: MediaQuery.of(context).size,

View File

@ -158,6 +158,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
user = UserModel.fromToken(token);
loginEmailController.clear();
loginPasswordController.clear();
debugPrint("token " + token.accessToken);
emit(LoginSuccess());
} else {
emit(const LoginFailure(error: 'Something went wrong'));

View File

@ -14,6 +14,7 @@ class DynamicTable extends StatefulWidget {
final void Function(bool?)? selectAll;
final void Function(int, bool, dynamic)? onRowSelected;
final List<String>? initialSelectedIds;
final int uuidIndex;
const DynamicTable({
super.key,
required this.headers,
@ -26,6 +27,7 @@ class DynamicTable extends StatefulWidget {
this.selectAll,
this.onRowSelected,
this.initialSelectedIds,
required this.uuidIndex,
});
@override
@ -38,9 +40,24 @@ class _DynamicTableState extends State<DynamicTable> {
@override
void initState() {
super.initState();
_initializeSelection();
}
@override
void didUpdateWidget(DynamicTable oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.data != widget.data) {
_initializeSelection();
}
}
void _initializeSelection() {
_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(widget.data[index][1]);
widget.initialSelectedIds!.contains(deviceUuid);
});
}
@ -71,8 +88,7 @@ class _DynamicTableState extends State<DynamicTable> {
children: [
if (widget.withCheckBox) _buildSelectAllCheckbox(),
...widget.headers
.map((header) => _buildTableHeaderCell(header))
.toList(),
.map((header) => _buildTableHeaderCell(header)),
],
),
),
@ -119,11 +135,9 @@ class _DynamicTableState extends State<DynamicTable> {
if (widget.withCheckBox)
_buildRowCheckbox(
index, widget.size.height * 0.10),
...row
.map((cell) => _buildTableCell(
cell.toString(),
widget.size.height * 0.10))
.toList(),
...row.map((cell) => _buildTableCell(
cell.toString(),
widget.size.height * 0.10)),
],
);
},

View File

@ -28,14 +28,16 @@ class DeviceManagementBloc
emit(DeviceManagementLoading());
try {
final devices = await DevicesManagementApi().fetchDevices();
_selectedDevices.clear();
_devices = devices;
_calculateDeviceCounts();
emit(DeviceManagementLoaded(
devices: devices,
selectedIndex: _selectedIndex,
selectedIndex: 0,
onlineCount: _onlineCount,
offlineCount: _offlineCount,
lowBatteryCount: _lowBatteryCount,
selectedDevice: null,
));
} catch (e) {
emit(DeviceManagementInitial());
@ -63,6 +65,8 @@ class DeviceManagementBloc
onlineCount: _onlineCount,
offlineCount: _offlineCount,
lowBatteryCount: _lowBatteryCount,
selectedDevice:
_selectedDevices.isNotEmpty ? _selectedDevices.first : null,
));
}
}
@ -75,8 +79,10 @@ class DeviceManagementBloc
void _onSelectDevice(
SelectDevice event, Emitter<DeviceManagementState> emit) {
if (_selectedDevices.contains(event.selectedDevice)) {
_selectedDevices.remove(event.selectedDevice);
final selectedUuid = event.selectedDevice.uuid;
if (_selectedDevices.any((device) => device.uuid == selectedUuid)) {
_selectedDevices.removeWhere((device) => device.uuid == selectedUuid);
} else {
_selectedDevices.add(event.selectedDevice);
}
@ -129,6 +135,9 @@ class DeviceManagementBloc
void _onSearchDevices(
SearchDevices event, Emitter<DeviceManagementState> emit) {
if (_devices.isNotEmpty) {
_selectedDevices.clear();
_selectedIndex = 0;
final filteredDevices = _devices.where((device) {
final matchesCommunity = event.community == null ||
event.community!.isEmpty ||
@ -150,7 +159,6 @@ class DeviceManagementBloc
false);
return matchesCommunity && matchesUnit && matchesProductName;
}).toList();
_selectedDevices = [];
emit(DeviceManagementFiltered(
filteredDevices: filteredDevices,
selectedIndex: 0,

View File

@ -42,6 +42,10 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
offlineCount = state.offlineCount;
lowBatteryCount = state.lowBatteryCount;
isControlButtonEnabled = state.selectedDevice != null;
} else if (state is DeviceManagementInitial) {
devicesToShow = [];
selectedIndex = 0;
isControlButtonEnabled = false;
}
final tabs = [
@ -125,6 +129,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
},
withCheckBox: true,
size: context.screenSize,
uuidIndex: 2,
headers: const [
'Device Name',
'Product Name',
@ -153,10 +158,15 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
(device.updateTime ?? 0) * 1000)),
];
}).toList(),
initialSelectedIds: context
.read<DeviceManagementBloc>()
.selectedDevices
.map((device) => device.uuid!)
.toList(),
isEmpty: devicesToShow.isEmpty,
),
),
),
)
],
);
},

View File

@ -62,16 +62,19 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
deviceStatus.noBodyTime = event.value;
} else if (event.code == 'moving_max_dis') {
deviceStatus.maxDistance = event.value;
} else if (event.code == 'scene') {
deviceStatus.spaceType = getSpaceType(event.value);
}
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
await _runDeBouncer(
deviceId: deviceId, code: event.code, value: event.value);
deviceId: deviceId, code: event.code, value: event.value, emit: emit);
}
_runDeBouncer({
required String deviceId,
required String code,
required dynamic value,
required Emitter<CeilingSensorState> emit,
}) {
if (_timer != null) {
_timer!.cancel();
@ -84,6 +87,11 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
if (!response) {
add(CeilingInitialEvent());
}
if (response == true && code == 'scene') {
emit(CeilingLoadingInitialState());
await Future.delayed(const Duration(seconds: 1));
add(CeilingInitialEvent());
}
} catch (_) {
await Future.delayed(const Duration(milliseconds: 500));
add(CeilingInitialEvent());

View File

@ -10,7 +10,7 @@ class CeilingSensorModel {
String bodyMovement;
String noBodyTime;
int maxDistance;
String spaceType;
SpaceTypes spaceType;
CeilingSensorModel({
required this.presenceState,
@ -33,7 +33,7 @@ class CeilingSensorModel {
String _bodyMovement = 'none';
String _noBodyTime = 'none';
int _maxDis = 0;
String _spaceType = 'none';
SpaceTypes _spaceType = SpaceTypes.none;
try {
for (var status in jsonList) {
@ -42,7 +42,7 @@ class CeilingSensorModel {
_presenceState = status.value ?? 'none';
break;
case 'scene':
_spaceType = status.value ?? 'none';
_spaceType = getSpaceType(status.value ?? 'none');
break;
case 'sensitivity':
_sensitivity = status.value is int
@ -92,3 +92,27 @@ class CeilingSensorModel {
);
}
}
enum SpaceTypes {
none,
parlour,
area,
toilet,
bedroom,
}
SpaceTypes getSpaceType(String value) {
switch (value) {
case 'parlour':
return SpaceTypes.parlour;
case 'area':
return SpaceTypes.area;
case 'toilet':
return SpaceTypes.toilet;
case 'bedroom':
return SpaceTypes.bedroom;
case 'none':
default:
return SpaceTypes.none;
}
}

View File

@ -99,15 +99,14 @@ class CeilingSensorControls extends StatelessWidget
description: 'Detection Range',
),
PresenceSpaceType(
listOfIcons: const [
Assets.office,
Assets.parlour,
Assets.dyi,
Assets.bathroom,
Assets.bedroom,
],
description: 'Space Type',
value: model.spaceType,
action: (String value) => context.read<CeilingSensorBloc>().add(
CeilingChangeValueEvent(
code: 'scene',
value: value,
),
),
),
PresenceUpdateData(
value: model.sensitivity.toDouble(),

View File

@ -1,24 +1,33 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart';
class PresenceSpaceType extends StatelessWidget {
const PresenceSpaceType({
super.key,
required this.listOfIcons,
required this.description,
required this.value,
required this.action,
});
final List<String> listOfIcons;
final String description;
final String value;
final SpaceTypes value;
final void Function(String value) action;
@override
Widget build(BuildContext context) {
final Map<SpaceTypes, String> spaceTypeIcons = {
SpaceTypes.none: Assets.office,
SpaceTypes.parlour: Assets.parlour,
SpaceTypes.area: Assets.dyi,
SpaceTypes.toilet: Assets.bathroom,
SpaceTypes.bedroom: Assets.bedroom,
};
return DeviceControlsContainer(
child: Column(
mainAxisSize: MainAxisSize.min,
@ -38,23 +47,28 @@ class PresenceSpaceType extends StatelessWidget {
Wrap(
runSpacing: 8,
spacing: 16,
children: [
...listOfIcons.map((icon) => Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
border: Border.all(
color: icon.contains(value)
? ColorsManager.blueColor
: Colors.transparent,
),
children: spaceTypeIcons.entries.map((entry) {
final icon = entry.value;
final spaceType = entry.key;
return GestureDetector(
onTap: () => action(spaceType.name),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
border: Border.all(
color: value == spaceType
? ColorsManager.blueColor
: Colors.transparent,
),
child: SvgPicture.asset(
icon,
width: 40,
height: 40,
),
)),
],
),
child: SvgPicture.asset(
icon,
width: 40,
height: 40,
),
),
);
}).toList(),
),
],
),

View File

@ -6,6 +6,8 @@ import 'package:syncrow_web/pages/device_managment/shared/table/table_header.dar
class ReportsTable extends StatelessWidget {
final DeviceReport report;
final String? thirdColumnTitle;
final String? thirdColumnDescription;
final Function(int index) onRowTap;
final VoidCallback onClose;
@ -14,6 +16,8 @@ class ReportsTable extends StatelessWidget {
required this.report,
required this.onRowTap,
required this.onClose,
this.thirdColumnTitle,
this.thirdColumnDescription,
});
@override
@ -32,33 +36,34 @@ class ReportsTable extends StatelessWidget {
children: [
TableRow(
decoration: BoxDecoration(color: Colors.grey.shade200),
children: const [
TableHeader(title: 'Date'),
TableHeader(title: 'Time'),
TableHeader(title: 'Status'),
children: [
const TableHeader(title: 'Date'),
const TableHeader(title: 'Time'),
TableHeader(title: thirdColumnTitle ?? 'Status'),
],
),
...report.data!.asMap().entries.map((entry) {
int index = entry.key;
DeviceEvent data = entry.value;
if (report.data != null)
...report.data!.asMap().entries.map((entry) {
int index = entry.key;
DeviceEvent data = entry.value;
// Parse eventTime into Date and Time
DateTime eventDateTime =
DateTime.fromMillisecondsSinceEpoch(data.eventTime!);
String date = DateFormat('dd/MM/yyyy').format(eventDateTime);
String time = DateFormat('HH:mm').format(eventDateTime);
// Parse eventTime into Date and Time
DateTime eventDateTime =
DateTime.fromMillisecondsSinceEpoch(data.eventTime!);
String date = DateFormat('dd/MM/yyyy').format(eventDateTime);
String time = DateFormat('HH:mm').format(eventDateTime);
return TableRow(
children: [
TableCellWidget(value: date),
TableCellWidget(value: time),
TableCellWidget(
value: data.value!,
onTap: () => onRowTap(index),
),
],
);
}).toList(),
return TableRow(
children: [
TableCellWidget(value: date),
TableCellWidget(value: time),
TableCellWidget(
value: '${data.value!} $thirdColumnDescription',
onTap: () => onRowTap(index),
),
],
);
}),
],
),
),

View File

@ -25,16 +25,21 @@ class WallSensorControls extends StatelessWidget with HelperResponsiveLayout {
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
return BlocProvider(
create: (context) => WallSensorBloc(deviceId: device.uuid!)..add(WallSensorInitialEvent()),
create: (context) =>
WallSensorBloc(deviceId: device.uuid!)..add(WallSensorInitialEvent()),
child: BlocBuilder<WallSensorBloc, WallSensorState>(
builder: (context, state) {
if (state is WallSensorLoadingInitialState || state is DeviceReportsLoadingState) {
if (state is WallSensorLoadingInitialState ||
state is DeviceReportsLoadingState) {
return const Center(child: CircularProgressIndicator());
} else if (state is WallSensorUpdateState) {
return _buildGridView(context, state.wallSensorModel, isExtraLarge, isLarge, isMedium);
return _buildGridView(context, state.wallSensorModel, isExtraLarge,
isLarge, isMedium);
} else if (state is DeviceReportsState) {
return ReportsTable(
report: state.deviceReport,
thirdColumnTitle: "Value",
thirdColumnDescription: "Lux",
onRowTap: (index) {},
onClose: () {
context.read<WallSensorBloc>().add(BackToGridViewEvent());
@ -49,7 +54,8 @@ class WallSensorControls extends StatelessWidget with HelperResponsiveLayout {
);
} else if (state is DeviceReportsFailedState) {
final model = context.read<WallSensorBloc>().deviceStatus;
return _buildGridView(context, model, isExtraLarge, isLarge, isMedium);
return _buildGridView(
context, model, isExtraLarge, isLarge, isMedium);
}
return const Center(child: Text('Error fetching status'));
},
@ -57,8 +63,8 @@ class WallSensorControls extends StatelessWidget with HelperResponsiveLayout {
);
}
Widget _buildGridView(
BuildContext context, WallSensorModel model, bool isExtraLarge, bool isLarge, bool isMedium) {
Widget _buildGridView(BuildContext context, WallSensorModel model,
bool isExtraLarge, bool isLarge, bool isMedium) {
return GridView(
padding: const EdgeInsets.symmetric(horizontal: 50),
shrinkWrap: true,
@ -127,10 +133,11 @@ class WallSensorControls extends StatelessWidget with HelperResponsiveLayout {
maxValue: 10000,
steps: 1,
description: 'hr',
action: (int value) => context.read<WallSensorBloc>().add(WallSensorChangeValueEvent(
code: 'no_one_time',
value: value,
))),
action: (int value) =>
context.read<WallSensorBloc>().add(WallSensorChangeValueEvent(
code: 'no_one_time',
value: value,
))),
PresenceUpdateData(
value: model.farDetection.toDouble(),
title: 'Far Detection:',
@ -147,9 +154,8 @@ class WallSensorControls extends StatelessWidget with HelperResponsiveLayout {
),
GestureDetector(
onTap: () {
context
.read<WallSensorBloc>()
.add(GetDeviceReportsEvent(code: 'illuminance_value', deviceUuid: device.uuid!));
context.read<WallSensorBloc>().add(GetDeviceReportsEvent(
code: 'illuminance_value', deviceUuid: device.uuid!));
},
child: const PresenceStaticWidget(
icon: Assets.illuminanceRecordIcon,
@ -158,9 +164,8 @@ class WallSensorControls extends StatelessWidget with HelperResponsiveLayout {
),
GestureDetector(
onTap: () {
context
.read<WallSensorBloc>()
.add(GetDeviceReportsEvent(code: 'presence_state', deviceUuid: device.uuid!));
context.read<WallSensorBloc>().add(GetDeviceReportsEvent(
code: 'presence_state', deviceUuid: device.uuid!));
},
child: const PresenceStaticWidget(
icon: Assets.presenceRecordIcon,

View File

@ -16,11 +16,11 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
final BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration();
List<Node> sourcesList = [];
List<Node> destinationsList = [];
static UserModel? user;
UserModel? user;
HomeBloc() : super((HomeInitial())) {
on<CreateNewNode>(_createNode);
fetchUserInfo();
on<FetchUserInfo>(_fetchUserInfo);
}
void _createNode(CreateNewNode event, Emitter<HomeState> emit) async {
@ -39,10 +39,12 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
emit(HomeUpdateTree(graph: graph, builder: builder));
}
static Future fetchUserInfo() async {
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async {
try {
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
var uuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
user = await HomeApi().fetchUserInfo(uuid);
emit(HomeInitial());
} catch (e) {
return;
}

View File

@ -17,3 +17,7 @@ class CreateNewNode extends HomeEvent {
@override
List<Object> get props => [sourceNode, destinationNode];
}
class FetchUserInfo extends HomeEvent {
const FetchUserInfo();
}

View File

@ -1,11 +1,25 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/home/view/home_page_mobile.dart';
import 'package:syncrow_web/pages/home/view/home_page_web.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class HomePage extends StatelessWidget with HelperResponsiveLayout {
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with HelperResponsiveLayout {
@override
void initState() {
super.initState();
context.read<HomeBloc>().add(const FetchUserInfo());
}
@override
Widget build(BuildContext context) {
final isSmallScreen = isSmallScreenSize(context);

View File

@ -10,6 +10,7 @@ import 'package:syncrow_web/web_layout/web_scaffold.dart';
class HomeMobilePage extends StatelessWidget {
HomeMobilePage({super.key});
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
@ -79,7 +80,7 @@ class HomeMobilePage extends StatelessWidget {
);
}
dynamic homeItems = [
final dynamic homeItems = [
{
'title': 'Access',
'icon': Assets.accessIcon,

View File

@ -184,6 +184,7 @@ class AddDeviceDialog extends StatelessWidget {
},
withCheckBox: true,
size: size * 0.5,
uuidIndex: 1,
headers: const [
'Device Name',
'Device ID',

View File

@ -14,7 +14,7 @@ class Assets {
static const String google = "assets/images/google.svg";
static const String facebook = "assets/images/facebook.svg";
static const String invisiblePassword = "assets/images/Password_invisible.svg";
static const String visiblePassword = "assets/images/Password_visible.svg";
static const String visiblePassword = "assets/images/password_visible.svg";
static const String accessIcon = "assets/images/access_icon.svg";
static const String spaseManagementIcon = "assets/images/spase_management_icon.svg";
static const String devicesIcon = "assets/images/devices_icon.svg";

View File

@ -15,13 +15,14 @@ class WebAppBar extends StatelessWidget with HelperResponsiveLayout {
@override
Widget build(BuildContext context) {
bool isSmallScreen = isSmallScreenSize(context);
bool isHalfMediumScreen = isHafMediumScreenSize(context);
return BlocBuilder<HomeBloc, HomeState>(builder: (context, state) {
final user = context.read<HomeBloc>().user;
return Container(
height: isSmallScreen ? 130 : 100,
height: (isSmallScreen || isHalfMediumScreen) ? 130 : 100,
decoration: const BoxDecoration(color: ColorsManager.secondaryColor),
padding: const EdgeInsets.all(10),
child: isSmallScreen
child: isSmallScreen || isHalfMediumScreen
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -31,14 +32,11 @@ class WebAppBar extends StatelessWidget with HelperResponsiveLayout {
child: title!,
),
if (centerBody != null)
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: centerBody,
),
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: centerBody,
),
if (rightBody != null || HomeBloc.user != null)
if (rightBody != null || user != null)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -61,9 +59,9 @@ class WebAppBar extends StatelessWidget with HelperResponsiveLayout {
const SizedBox(
width: 10,
),
if (HomeBloc.user != null)
if (user != null)
Text(
'${HomeBloc.user!.firstName} ${HomeBloc.user!.lastName}',
'${user.firstName} ${user.lastName}',
style: Theme.of(context).textTheme.bodyLarge,
),
],
@ -76,16 +74,18 @@ class WebAppBar extends StatelessWidget with HelperResponsiveLayout {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Align(
alignment: Alignment.centerLeft,
child: title!,
),
if (centerBody != null)
Expanded(
child: Center(
child: centerBody,
),
Expanded(
child: Row(
children: [
title!,
if (centerBody != null)
Padding(
padding: const EdgeInsets.only(left: 80),
child: centerBody!,
),
],
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
@ -113,9 +113,9 @@ class WebAppBar extends StatelessWidget with HelperResponsiveLayout {
const SizedBox(
width: 10,
),
if (HomeBloc.user != null)
if (user != null)
Text(
'${HomeBloc.user!.firstName} ${HomeBloc.user!.lastName}',
'${user.firstName} ${user.lastName}',
style: Theme.of(context).textTheme.bodyLarge,
),
],