Compare commits

..
Author SHA1 Message Date
Faris Armoush 87574de0ac Revert "."
This reverts commit 9930736151.
2025-06-19 13:21:49 +03:00
Faris Armoush 14f4beced1 Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1593-rework 2025-06-19 13:20:52 +03:00
Faris Armoush 9930736151 . 2025-06-19 13:20:23 +03:00
10 changed files with 134 additions and 166 deletions
@@ -25,8 +25,8 @@ class AnalyticsDevice {
factory AnalyticsDevice.fromJson(Map<String, dynamic> json) { factory AnalyticsDevice.fromJson(Map<String, dynamic> json) {
return AnalyticsDevice( return AnalyticsDevice(
uuid: json['uuid'] as String? ?? '', uuid: json['uuid'] as String,
name: json['name'] as String? ?? '', name: json['name'] as String,
createdAt: json['createdAt'] != null createdAt: json['createdAt'] != null
? DateTime.parse(json['createdAt'] as String) ? DateTime.parse(json['createdAt'] as String)
: null, : null,
@@ -39,8 +39,8 @@ class AnalyticsDevice {
? ProductDevice.fromJson(json['productDevice'] as Map<String, dynamic>) ? ProductDevice.fromJson(json['productDevice'] as Map<String, dynamic>)
: null, : null,
spaceUuid: json['spaceUuid'] as String?, spaceUuid: json['spaceUuid'] as String?,
latitude: json['lat'] != null ? double.parse(json['lat'] as String? ?? '0.0') : null, latitude: json['lat'] != null ? double.parse(json['lat'] as String) : null,
longitude: json['lon'] != null ? double.parse(json['lon'] as String? ?? '0.0') : null, longitude: json['lon'] != null ? double.parse(json['lon'] as String) : null,
); );
} }
} }
@@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/analytics/modules/air_quality/blocs/air_qualit
import 'package:syncrow_web/pages/analytics/modules/air_quality/blocs/device_location/device_location_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/air_quality/blocs/device_location/device_location_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/air_quality/blocs/range_of_aqi/range_of_aqi_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/air_quality/blocs/range_of_aqi/range_of_aqi_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_type_dropdown.dart'; import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_type_dropdown.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_date_picker_bloc/analytics_date_picker_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_devices/analytics_devices_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_devices/analytics_devices_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/realtime_device_changes/realtime_device_changes_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/realtime_device_changes/realtime_device_changes_bloc.dart';
import 'package:syncrow_web/pages/analytics/params/get_air_quality_distribution_param.dart'; import 'package:syncrow_web/pages/analytics/params/get_air_quality_distribution_param.dart';
@@ -21,14 +22,13 @@ abstract final class FetchAirQualityDataHelper {
required String spaceUuid, required String spaceUuid,
bool shouldFetchAnalyticsDevices = true, bool shouldFetchAnalyticsDevices = true,
}) { }) {
final date = context.read<AnalyticsDatePickerBloc>().state.monthlyDate;
final aqiType = context.read<AirQualityDistributionBloc>().state.selectedAqiType; final aqiType = context.read<AirQualityDistributionBloc>().state.selectedAqiType;
if (shouldFetchAnalyticsDevices) { loadAnalyticsDevices(
loadAnalyticsDevices( context,
context, communityUuid: communityUuid,
communityUuid: communityUuid, spaceUuid: spaceUuid,
spaceUuid: spaceUuid, );
);
}
loadRangeOfAqi( loadRangeOfAqi(
context, context,
spaceUuid: spaceUuid, spaceUuid: spaceUuid,
@@ -65,7 +65,7 @@ class AqiDeviceInfo extends StatelessWidget {
); );
final tvocValue = _getValueForStatus( final tvocValue = _getValueForStatus(
status, status,
'voc_value', 'tvoc_value',
formatter: (value) => (value / 100).toStringAsFixed(2), formatter: (value) => (value / 100).toStringAsFixed(2),
); );
@@ -7,7 +7,7 @@ enum AqiType {
pm25('PM2.5', 'µg/m³', 'pm25'), pm25('PM2.5', 'µg/m³', 'pm25'),
pm10('PM10', 'µg/m³', 'pm10'), pm10('PM10', 'µg/m³', 'pm10'),
hcho('HCHO', 'mg/m³', 'cho2'), hcho('HCHO', 'mg/m³', 'cho2'),
tvoc('TVOC', 'mg/m³', 'voc'), tvoc('TVOC', 'µg/m³', 'voc'),
co2('CO2', 'ppm', 'co2'); co2('CO2', 'ppm', 'co2');
const AqiType(this.value, this.unit, this.code); const AqiType(this.value, this.unit, this.code);
@@ -81,7 +81,7 @@ abstract final class FetchOccupancyDataHelper {
param: GetAnalyticsDevicesParam( param: GetAnalyticsDevicesParam(
communityUuid: communityUuid, communityUuid: communityUuid,
spaceUuid: spaceUuid, spaceUuid: spaceUuid,
deviceTypes: ['WPS', 'CPS', 'NCPS'], deviceTypes: ['WPS', 'CPS'],
requestType: AnalyticsDeviceRequestType.occupancy, requestType: AnalyticsDeviceRequestType.occupancy,
), ),
onSuccess: (device) { onSuccess: (device) {
@@ -20,7 +20,7 @@ class AnalyticsOccupancyView extends StatelessWidget {
child: Column( child: Column(
spacing: 32, spacing: 32,
children: [ children: [
SizedBox(height: height * 0.8, child: const OccupancyEndSideBar()), SizedBox(height: height * 0.46, child: const OccupancyEndSideBar()),
SizedBox(height: height * 0.5, child: const OccupancyChartBox()), SizedBox(height: height * 0.5, child: const OccupancyChartBox()),
SizedBox(height: height * 0.5, child: const OccupancyHeatMapBox()), SizedBox(height: height * 0.5, child: const OccupancyHeatMapBox()),
], ],
@@ -26,6 +26,7 @@ class OccupancyEndSideBar extends StatelessWidget {
const AnalyticsSidebarHeader(title: 'Presnce Sensor'), const AnalyticsSidebarHeader(title: 'Presnce Sensor'),
Expanded( Expanded(
child: SizedBox( child: SizedBox(
// height: MediaQuery.sizeOf(context).height * 0.2,
child: PowerClampEnergyStatusWidget( child: PowerClampEnergyStatusWidget(
status: [ status: [
PowerClampEnergyStatus( PowerClampEnergyStatus(
+106 -78
View File
@@ -50,11 +50,20 @@ class _DynamicTableState extends State<DynamicTable> {
bool _selectAll = false; bool _selectAll = false;
final ScrollController _verticalScrollController = ScrollController(); final ScrollController _verticalScrollController = ScrollController();
final ScrollController _horizontalScrollController = ScrollController(); final ScrollController _horizontalScrollController = ScrollController();
late ScrollController _horizontalHeaderScrollController;
late ScrollController _horizontalBodyScrollController;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_initializeSelection(); _initializeSelection();
_horizontalHeaderScrollController = ScrollController();
_horizontalBodyScrollController = ScrollController();
// Synchronize horizontal scrolling
_horizontalBodyScrollController.addListener(() {
_horizontalHeaderScrollController
.jumpTo(_horizontalBodyScrollController.offset);
});
} }
@override @override
@@ -104,78 +113,108 @@ class _DynamicTableState extends State<DynamicTable> {
context.read<DeviceManagementBloc>().add(UpdateSelection(_selectedRows)); context.read<DeviceManagementBloc>().add(UpdateSelection(_selectedRows));
} }
@override
void dispose() {
_horizontalHeaderScrollController.dispose();
_horizontalBodyScrollController.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
decoration: widget.cellDecoration, decoration: widget.cellDecoration,
child: Scrollbar( child: Column(
controller: _verticalScrollController, children: [
thumbVisibility: true, Container(
trackVisibility: true, decoration: widget.headerDecoration ??
child: Scrollbar( const BoxDecoration(color: ColorsManager.boxColor),
//fixed the horizontal scrollbar issue
controller: _horizontalScrollController,
thumbVisibility: true,
trackVisibility: true,
notificationPredicate: (notif) => notif.depth == 1,
child: SingleChildScrollView(
controller: _verticalScrollController,
child: SingleChildScrollView( child: SingleChildScrollView(
controller: _horizontalScrollController,
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
physics: const NeverScrollableScrollPhysics(),
controller: _horizontalHeaderScrollController,
child: SizedBox( child: SizedBox(
width: widget.size.width, width: widget.size.width,
child: Column( child: Row(
children: [ children: [
Container( if (widget.withCheckBox) _buildSelectAllCheckbox(),
decoration: widget.headerDecoration ?? ...List.generate(widget.headers.length, (index) {
const BoxDecoration( return _buildTableHeaderCell(
color: ColorsManager.boxColor, widget.headers[index], index);
), }),
child: Row(
children: [
if (widget.withCheckBox) _buildSelectAllCheckbox(),
...List.generate(widget.headers.length, (index) {
return _buildTableHeaderCell(
widget.headers[index], index);
})
//...widget.headers.map((header) => _buildTableHeaderCell(header)),
],
),
),
SizedBox(
width: widget.size.width,
child: widget.isEmpty
? _buildEmptyState()
: Column(
children:
List.generate(widget.data.length, (rowIndex) {
final row = widget.data[rowIndex];
return Row(
children: [
if (widget.withCheckBox)
_buildRowCheckbox(
rowIndex, widget.size.height * 0.08),
...row.asMap().entries.map((entry) {
return _buildTableCell(
entry.value.toString(),
widget.size.height * 0.08,
rowIndex: rowIndex,
columnIndex: entry.key,
);
}).toList(),
],
);
}),
),
),
], ],
), ),
), ),
), ),
), ),
Expanded(
child: Scrollbar(
controller: _verticalScrollController,
thumbVisibility: true,
trackVisibility: true,
child: SingleChildScrollView(
controller: _verticalScrollController,
child: Scrollbar(
controller: _horizontalBodyScrollController,
thumbVisibility: false,
trackVisibility: false,
notificationPredicate: (notif) => notif.depth == 1,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
controller: _horizontalBodyScrollController,
child: Container(
color: ColorsManager.whiteColors,
child: SizedBox(
width: widget.size.width,
child: widget.isEmpty
? _buildEmptyState()
: Column(
children: List.generate(widget.data.length,
(rowIndex) {
final row = widget.data[rowIndex];
return Row(
children: [
if (widget.withCheckBox)
_buildRowCheckbox(rowIndex,
widget.size.height * 0.08),
...row.asMap().entries.map((entry) {
return _buildTableCell(
entry.value.toString(),
widget.size.height * 0.08,
rowIndex: rowIndex,
columnIndex: entry.key,
);
}).toList(),
],
);
}),
),
),
),
),
),
),
),
),
],
),
);
}
Widget _buildSelectAllCheckbox() {
return Container(
width: 50,
decoration: const BoxDecoration(
border: Border.symmetric(
vertical: BorderSide(color: ColorsManager.boxDivider),
), ),
), ),
child: Checkbox(
value: _selectAll,
onChanged: widget.withSelectAll && widget.data.isNotEmpty
? _toggleSelectAll
: null,
),
); );
} }
@@ -205,23 +244,6 @@ class _DynamicTableState extends State<DynamicTable> {
), ),
], ],
); );
Widget _buildSelectAllCheckbox() {
return Container(
width: 50,
decoration: const BoxDecoration(
border: Border.symmetric(
vertical: BorderSide(color: ColorsManager.boxDivider),
),
),
child: Checkbox(
value: _selectAll,
onChanged: widget.withSelectAll && widget.data.isNotEmpty
? _toggleSelectAll
: null,
),
);
}
Widget _buildRowCheckbox(int index, double size) { Widget _buildRowCheckbox(int index, double size) {
return Container( return Container(
width: 50, width: 50,
@@ -276,8 +298,12 @@ class _DynamicTableState extends State<DynamicTable> {
); );
} }
Widget _buildTableCell(String content, double size, Widget _buildTableCell(
{required int rowIndex, required int columnIndex}) { String content,
double size, {
required int rowIndex,
required int columnIndex,
}) {
bool isBatteryLevel = content.endsWith('%'); bool isBatteryLevel = content.endsWith('%');
double? batteryLevel; double? batteryLevel;
@@ -285,6 +311,7 @@ class _DynamicTableState extends State<DynamicTable> {
batteryLevel = double.tryParse(content.replaceAll('%', '').trim()); batteryLevel = double.tryParse(content.replaceAll('%', '').trim());
} }
bool isSettingsColumn = widget.headers[columnIndex] == 'Settings'; bool isSettingsColumn = widget.headers[columnIndex] == 'Settings';
if (isSettingsColumn) { if (isSettingsColumn) {
return buildSettingsIcon( return buildSettingsIcon(
width: 120, width: 120,
@@ -389,10 +416,11 @@ class _DynamicTableState extends State<DynamicTable> {
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Center( child: Center(
child: SvgPicture.asset( child: SvgPicture.asset(
Assets.settings, Assets.settings, // ضع المسار الصحيح هنا
width: 40, width: 40,
height: 22, height: 22,
color: ColorsManager.primaryColor, color: ColorsManager
.primaryColor, // نفس لون الأيقونة في الصورة
), ),
), ),
), ),
+11 -72
View File
@@ -34,9 +34,17 @@ class HomeCard extends StatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Expanded( Flexible(
child: SpliteNameHelperWidget( child: FittedBox(
name: name, fit: BoxFit.scaleDown,
child: Text(
name,
style: const TextStyle(
fontSize: 30,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
), ),
), ),
], ],
@@ -55,72 +63,3 @@ class HomeCard extends StatelessWidget {
); );
} }
} }
class SpliteNameHelperWidget extends StatelessWidget {
final String name;
const SpliteNameHelperWidget({
super.key,
required this.name,
});
@override
Widget build(BuildContext context) {
List<String> parts = name.split(' ');
if (parts.length == 2) {
// Two-word string
return Padding(
padding: const EdgeInsetsGeometry.only(top: 10, left: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Text(
parts[0],
style: const TextStyle(
fontSize: 30,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
Expanded(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Text(
parts[1],
style: const TextStyle(
fontSize: 30,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
);
} else {
// One-word string
return Text(
name,
style: const TextStyle(
fontSize: 30,
color: Colors.white,
fontWeight: FontWeight.bold,
),
);
}
}
}
// Text(
// name,
// style: const TextStyle(
// fontSize: 32,
// color: Colors.white,
// fontWeight: FontWeight.bold,
// ),
// )
+1 -1
View File
@@ -92,7 +92,7 @@ class _HomeWebPageState extends State<HomeWebPage> {
flex: 4, flex: 4,
child: SizedBox( child: SizedBox(
height: size.height * 0.6, height: size.height * 0.6,
width: size.width * 0.8, width: size.width * 0.68,
child: GridView.builder( child: GridView.builder(
itemCount: homeBloc.homeItems.length, itemCount: homeBloc.homeItems.length,
gridDelegate: gridDelegate: