mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-11 07:38:05 +00:00
Compare commits
38 Commits
SP-1464-FE
...
bugfix/cle
Author | SHA1 | Date | |
---|---|---|---|
58755eafe1 | |||
ce225818fb | |||
8762a7aaa8 | |||
8dc833b2c3 | |||
13cef151aa | |||
ab23be9828 | |||
687b68ab22 | |||
25614c3dd0 | |||
7cbe20ae88 | |||
349fe6c555 | |||
9779f3783c | |||
fe3db663b6 | |||
888d444752 | |||
bab5e06968 | |||
d7b6174dee | |||
6ef0b2f9d1 | |||
3ceb03826e | |||
52608b1f35 | |||
ac2996629e | |||
51c52c66cb | |||
0f56273d99 | |||
34d4d892d9 | |||
3193fd26fe | |||
43802a9fad | |||
6e0b1775f0 | |||
233fb2ee2c | |||
b26928b3d5 | |||
6fc35a7b9a | |||
756457927c | |||
f30d7c0117 | |||
976d6e385a | |||
ff07e7509d | |||
17a582ab99 | |||
09fb604acc | |||
2068df173d | |||
bfc2a381d2 | |||
c03b8f290c | |||
2c684a9495 |
@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class SearchResetButtons extends StatelessWidget {
|
class SearchResetButtons extends StatelessWidget {
|
||||||
const SearchResetButtons({
|
const SearchResetButtons({
|
||||||
@ -17,8 +17,10 @@ class SearchResetButtons extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
|
@ -108,7 +108,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
return DeviceManagementBody(
|
return DeviceManagementBody(
|
||||||
devices: deviceState.filteredDevices);
|
devices: deviceState.filteredDevices);
|
||||||
} else {
|
} else {
|
||||||
return const Center(child: Text('Error fetching Devices'));
|
return const DeviceManagementBody(devices: []);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -72,6 +72,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
child: state is DeviceManagementLoading
|
child: state is DeviceManagementLoading
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: Column(
|
: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
padding: isLargeScreenSize(context)
|
padding: isLargeScreenSize(context)
|
||||||
|
@ -14,29 +14,29 @@ class DeviceSearchFilters extends StatefulWidget {
|
|||||||
|
|
||||||
class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
|
class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
|
||||||
with HelperResponsiveLayout {
|
with HelperResponsiveLayout {
|
||||||
final _unitNameController = TextEditingController();
|
late final TextEditingController _unitNameController;
|
||||||
final _productNameController = TextEditingController();
|
late final TextEditingController _productNameController;
|
||||||
|
|
||||||
List<Widget> get _widgets => [
|
@override
|
||||||
_buildSearchField("Space Name", _unitNameController, 200),
|
void initState() {
|
||||||
_buildSearchField("Device Name / Product Name", _productNameController, 300),
|
_unitNameController = TextEditingController();
|
||||||
_buildSearchResetButtons(),
|
_productNameController = TextEditingController();
|
||||||
];
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (isExtraLargeScreenSize(context)) {
|
|
||||||
return Row(
|
|
||||||
children: _widgets
|
|
||||||
.map((e) => Padding(padding: const EdgeInsets.all(10), child: e))
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Wrap(
|
return Wrap(
|
||||||
|
alignment: WrapAlignment.start,
|
||||||
|
runAlignment: WrapAlignment.start,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
spacing: 20,
|
spacing: 20,
|
||||||
runSpacing: 10,
|
runSpacing: 10,
|
||||||
children: _widgets,
|
children: [
|
||||||
|
_buildSearchField("Space Name", _unitNameController, 200),
|
||||||
|
_buildSearchField("Device Name / Product Name", _productNameController, 300),
|
||||||
|
_buildSearchResetButtons(),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,11 +24,10 @@ class FlushMountedPresenceSensorChangeValueEvent
|
|||||||
extends FlushMountedPresenceSensorEvent {
|
extends FlushMountedPresenceSensorEvent {
|
||||||
final int value;
|
final int value;
|
||||||
final String code;
|
final String code;
|
||||||
final bool isBatchControl;
|
|
||||||
const FlushMountedPresenceSensorChangeValueEvent({
|
const FlushMountedPresenceSensorChangeValueEvent({
|
||||||
required this.value,
|
required this.value,
|
||||||
required this.code,
|
required this.code,
|
||||||
this.isBatchControl = false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -75,7 +75,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.nearDetection / 100).toDouble(),
|
value: (model.nearDetection / 100).clamp(0.0, double.infinity),
|
||||||
title: 'Nearest Detect Dist:',
|
title: 'Nearest Detect Dist:',
|
||||||
description: 'm',
|
description: 'm',
|
||||||
minValue: 0.0,
|
minValue: 0.0,
|
||||||
@ -92,7 +92,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.farDetection / 100).toDouble(),
|
value: (model.farDetection / 100).clamp(0.0, double.infinity),
|
||||||
title: 'Max Detect Dist:',
|
title: 'Max Detect Dist:',
|
||||||
description: 'm',
|
description: 'm',
|
||||||
minValue: 0.0,
|
minValue: 0.0,
|
||||||
@ -109,7 +109,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: model.presenceDelay.toDouble(),
|
value: model.sensiReduce.toDouble(),
|
||||||
title: 'Trigger Level:',
|
title: 'Trigger Level:',
|
||||||
minValue: 0,
|
minValue: 0,
|
||||||
maxValue: 3,
|
maxValue: 3,
|
||||||
@ -117,7 +117,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
|
|||||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
FlushMountedPresenceSensorBatchControlEvent(
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
deviceIds: devicesIds,
|
deviceIds: devicesIds,
|
||||||
code: FlushMountedPresenceSensorModel.codePresenceDelay,
|
code: FlushMountedPresenceSensorModel.codeSensiReduce,
|
||||||
value: value,
|
value: value,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -137,17 +137,19 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.sensiReduce.toDouble()),
|
value: (model.presenceDelay / 10).toDouble(),
|
||||||
title: 'Target Confirm Time:',
|
title: 'Target Confirm Time:',
|
||||||
description: 's',
|
description: 's',
|
||||||
minValue: 0,
|
minValue: 0.0,
|
||||||
maxValue: 3,
|
maxValue: 0.5,
|
||||||
steps: 1,
|
steps: 0.1,
|
||||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
valuesPercision: 1,
|
||||||
|
action: (double value) =>
|
||||||
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
FlushMountedPresenceSensorBatchControlEvent(
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
deviceIds: devicesIds,
|
deviceIds: devicesIds,
|
||||||
code: FlushMountedPresenceSensorModel.codeSensiReduce,
|
code: FlushMountedPresenceSensorModel.codePresenceDelay,
|
||||||
value: value,
|
value: (value * 10).toInt(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -15,7 +15,7 @@ import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_la
|
|||||||
|
|
||||||
class FlushMountedPresenceSensorControlView extends StatelessWidget
|
class FlushMountedPresenceSensorControlView extends StatelessWidget
|
||||||
with HelperResponsiveLayout {
|
with HelperResponsiveLayout {
|
||||||
const FlushMountedPresenceSensorControlView({super.key, required this.device});
|
const FlushMountedPresenceSensorControlView({required this.device, super.key});
|
||||||
|
|
||||||
final AllDevicesModel device;
|
final AllDevicesModel device;
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.nearDetection / 100).toDouble(),
|
value: (model.nearDetection / 100).clamp(0.0, double.infinity),
|
||||||
title: 'Nearest Detect Dist:',
|
title: 'Nearest Detect Dist:',
|
||||||
description: 'm',
|
description: 'm',
|
||||||
minValue: 0.0,
|
minValue: 0.0,
|
||||||
@ -129,7 +129,7 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.farDetection / 100).toDouble(),
|
value: (model.farDetection / 100).clamp(0.0, double.infinity),
|
||||||
title: 'Max Detect Dist:',
|
title: 'Max Detect Dist:',
|
||||||
description: 'm',
|
description: 'm',
|
||||||
minValue: 0.0,
|
minValue: 0.0,
|
||||||
@ -145,20 +145,20 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.presenceDelay.toDouble()),
|
value: model.sensiReduce.toDouble(),
|
||||||
title: 'Trigger Level:',
|
title: 'Trigger Level:',
|
||||||
minValue: 0,
|
minValue: 0,
|
||||||
maxValue: 3,
|
maxValue: 3,
|
||||||
steps: 1,
|
steps: 1,
|
||||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
FlushMountedPresenceSensorChangeValueEvent(
|
FlushMountedPresenceSensorChangeValueEvent(
|
||||||
code: FlushMountedPresenceSensorModel.codePresenceDelay,
|
code: FlushMountedPresenceSensorModel.codeSensiReduce,
|
||||||
value: value,
|
value: value,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.occurDistReduce.toDouble()),
|
value: model.occurDistReduce.toDouble(),
|
||||||
title: 'Indent Level:',
|
title: 'Indent Level:',
|
||||||
minValue: 0,
|
minValue: 0,
|
||||||
maxValue: 3,
|
maxValue: 3,
|
||||||
@ -171,21 +171,23 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: (model.sensiReduce.toDouble()),
|
value: (model.presenceDelay / 10).toDouble(),
|
||||||
|
valuesPercision: 1,
|
||||||
title: 'Target Confirm Time:',
|
title: 'Target Confirm Time:',
|
||||||
description: 's',
|
description: 's',
|
||||||
minValue: 0,
|
minValue: 0.0,
|
||||||
maxValue: 3,
|
maxValue: 0.5,
|
||||||
steps: 1,
|
steps: 0.1,
|
||||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
action: (double value) =>
|
||||||
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
FlushMountedPresenceSensorChangeValueEvent(
|
FlushMountedPresenceSensorChangeValueEvent(
|
||||||
code: FlushMountedPresenceSensorModel.codeSensiReduce,
|
code: FlushMountedPresenceSensorModel.codePresenceDelay,
|
||||||
value: value,
|
value: (value * 10).toInt(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PresenceUpdateData(
|
PresenceUpdateData(
|
||||||
value: ((model.noneDelay / 10).toDouble()),
|
value: (model.noneDelay / 10).toDouble(),
|
||||||
description: 's',
|
description: 's',
|
||||||
title: 'Disappe Delay:',
|
title: 'Disappe Delay:',
|
||||||
minValue: 20,
|
minValue: 20,
|
||||||
|
@ -217,29 +217,31 @@ class SmartPowerBloc extends Bloc<SmartPowerEvent, SmartPowerState> {
|
|||||||
try {
|
try {
|
||||||
var status =
|
var status =
|
||||||
await DevicesManagementApi().getPowerClampInfo(event.deviceId);
|
await DevicesManagementApi().getPowerClampInfo(event.deviceId);
|
||||||
deviceStatus = PowerClampModel.fromJson(status);
|
deviceStatus = PowerClampModel.fromJson(status as Map<String, Object?>? ??{});
|
||||||
|
final phaseADataPoints = deviceStatus.status.phaseA.dataPoints;
|
||||||
|
final phaseBDataPoints = deviceStatus.status.phaseB.dataPoints;
|
||||||
|
final phaseCDataPoints = deviceStatus.status.phaseC.dataPoints;
|
||||||
phaseData = [
|
phaseData = [
|
||||||
{
|
{
|
||||||
'name': 'Phase A',
|
'name': 'Phase A',
|
||||||
'voltage': '${deviceStatus.status.phaseA.dataPoints[0].value / 10} V',
|
'voltage': '${(phaseADataPoints.elementAtOrNull(0)?.value as num? ?? 0) / 10} V',
|
||||||
'current': '${deviceStatus.status.phaseA.dataPoints[1].value / 10} A',
|
'current': '${(phaseADataPoints.elementAtOrNull(1)?.value as num? ?? 0) / 10} A',
|
||||||
'activePower': '${deviceStatus.status.phaseA.dataPoints[2].value} W',
|
'activePower': '${phaseADataPoints.elementAtOrNull(2)?.value??'N/A'} W',
|
||||||
'powerFactor': '${deviceStatus.status.phaseA.dataPoints[3].value}',
|
'powerFactor': '${phaseADataPoints.elementAtOrNull(3)?.value??'N/A'}',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Phase B',
|
'name': 'Phase B',
|
||||||
'voltage': '${deviceStatus.status.phaseB.dataPoints[0].value / 10} V',
|
'voltage': '${(phaseBDataPoints .elementAtOrNull(0)?.value as num? ?? 0) / 10} V',
|
||||||
'current': '${deviceStatus.status.phaseB.dataPoints[1].value / 10} A',
|
'current': '${(phaseBDataPoints .elementAtOrNull(1)?.value as num? ?? 0) / 10} A',
|
||||||
'activePower': '${deviceStatus.status.phaseB.dataPoints[2].value} W',
|
'activePower': '${phaseBDataPoints.elementAtOrNull(2)?.value??'N/A'} W',
|
||||||
'powerFactor': '${deviceStatus.status.phaseB.dataPoints[3].value}',
|
'powerFactor': '${phaseBDataPoints.elementAtOrNull(3)?.value??'N/A'}',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Phase C',
|
'name': 'Phase C',
|
||||||
'voltage': '${deviceStatus.status.phaseC.dataPoints[0].value / 10} V',
|
'voltage': '${(phaseCDataPoints.elementAtOrNull(0)?.value as num? ?? 0) / 10} V',
|
||||||
'current': '${deviceStatus.status.phaseC.dataPoints[1].value / 10} A',
|
'current': '${(phaseCDataPoints.elementAtOrNull(1)?.value as num? ?? 0) / 10} A',
|
||||||
'activePower': '${deviceStatus.status.phaseC.dataPoints[2].value} W',
|
'activePower': '${phaseCDataPoints.elementAtOrNull(2)?.value ?? 'N/A'} W',
|
||||||
'powerFactor': '${deviceStatus.status.phaseC.dataPoints[3].value}',
|
'powerFactor': '${phaseCDataPoints.elementAtOrNull(3)?.value ?? 'N/A'}',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
emit(GetDeviceStatus());
|
emit(GetDeviceStatus());
|
||||||
@ -785,7 +787,7 @@ class SmartPowerBloc extends Bloc<SmartPowerEvent, SmartPowerState> {
|
|||||||
void selectDateRange() async {
|
void selectDateRange() async {
|
||||||
DateTime startDate = dateTime!;
|
DateTime startDate = dateTime!;
|
||||||
DateTime endDate = DateTime(startDate.year, startDate.month + 1, 1)
|
DateTime endDate = DateTime(startDate.year, startDate.month + 1, 1)
|
||||||
.subtract(Duration(days: 1));
|
.subtract(const Duration(days: 1));
|
||||||
String formattedEndDate = DateFormat('dd/MM/yyyy').format(endDate);
|
String formattedEndDate = DateFormat('dd/MM/yyyy').format(endDate);
|
||||||
endChartDate = ' - $formattedEndDate';
|
endChartDate = ' - $formattedEndDate';
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,9 @@ class PowerClampModel {
|
|||||||
|
|
||||||
factory PowerClampModel.fromJson(Map<String, dynamic> json) {
|
factory PowerClampModel.fromJson(Map<String, dynamic> json) {
|
||||||
return PowerClampModel(
|
return PowerClampModel(
|
||||||
productUuid: json['productUuid'],
|
productUuid: json['productUuid'] as String? ?? '',
|
||||||
productType: json['productType'],
|
productType: json['productType'] as String? ?? '',
|
||||||
status: PowerStatus.fromJson(json['status']),
|
status: PowerStatus.fromJson(json['status'] as Map<String, dynamic>? ?? {}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ class PowerClampModel {
|
|||||||
return PowerClampModel(
|
return PowerClampModel(
|
||||||
productUuid: productUuid ?? this.productUuid,
|
productUuid: productUuid ?? this.productUuid,
|
||||||
productType: productType ?? this.productType,
|
productType: productType ?? this.productType,
|
||||||
status: statusPower ?? this.status,
|
status: statusPower ?? status,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,12 +46,10 @@ class PowerStatus {
|
|||||||
|
|
||||||
factory PowerStatus.fromJson(Map<String, dynamic> json) {
|
factory PowerStatus.fromJson(Map<String, dynamic> json) {
|
||||||
return PowerStatus(
|
return PowerStatus(
|
||||||
phaseA: Phase.fromJson(json['phaseA']),
|
phaseA: Phase.fromJson(json['phaseA']as List<dynamic>? ?? []),
|
||||||
phaseB: Phase.fromJson(json['phaseB']),
|
phaseB: Phase.fromJson(json['phaseB']as List<dynamic>? ?? []),
|
||||||
phaseC: Phase.fromJson(json['phaseC']),
|
phaseC: Phase.fromJson(json['phaseC']as List<dynamic>? ?? []),
|
||||||
general: Phase.fromJson(json['general']
|
general: Phase.fromJson(json['general']as List<dynamic>? ?? []
|
||||||
// List<DataPoint>.from(
|
|
||||||
// json['general'].map((x) => DataPoint.fromJson(x))),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,30 +67,30 @@ class Phase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DataPoint {
|
class DataPoint {
|
||||||
dynamic code;
|
final String? code;
|
||||||
dynamic customName;
|
final String? customName;
|
||||||
dynamic dpId;
|
final int? dpId;
|
||||||
dynamic time;
|
final int? time;
|
||||||
dynamic type;
|
final String? type;
|
||||||
dynamic value;
|
final dynamic value;
|
||||||
|
|
||||||
DataPoint({
|
DataPoint({
|
||||||
required this.code,
|
this.code,
|
||||||
required this.customName,
|
this.customName,
|
||||||
required this.dpId,
|
this.dpId,
|
||||||
required this.time,
|
this.time,
|
||||||
required this.type,
|
this.type,
|
||||||
required this.value,
|
this.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory DataPoint.fromJson(Map<String, dynamic> json) {
|
factory DataPoint.fromJson(Map<String, dynamic> json) {
|
||||||
return DataPoint(
|
return DataPoint(
|
||||||
code: json['code'],
|
code: json['code'] as String?,
|
||||||
customName: json['customName'],
|
customName: json['customName'] as String?,
|
||||||
dpId: json['dpId'],
|
dpId: json['dpId'] as int?,
|
||||||
time: json['time'],
|
time: json['time'] as int?,
|
||||||
type: json['type'],
|
type: json['type'] as String?,
|
||||||
value: json['value'],
|
value: json['value'] as dynamic,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class EnergyConsumptionPage extends StatefulWidget {
|
class EnergyConsumptionPage extends StatefulWidget {
|
||||||
@ -10,7 +10,8 @@ class EnergyConsumptionPage extends StatefulWidget {
|
|||||||
final Widget widget;
|
final Widget widget;
|
||||||
final Function()? onTap;
|
final Function()? onTap;
|
||||||
|
|
||||||
EnergyConsumptionPage({
|
const EnergyConsumptionPage({
|
||||||
|
super.key,
|
||||||
required this.chartData,
|
required this.chartData,
|
||||||
required this.totalConsumption,
|
required this.totalConsumption,
|
||||||
required this.date,
|
required this.date,
|
||||||
@ -91,11 +92,12 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 10),
|
padding: const EdgeInsets.only(top: 10),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: MediaQuery.of(context).size.height * 0.11,
|
height: MediaQuery.sizeOf(context).height * 0.09,
|
||||||
child: LineChart(
|
child: LineChart(
|
||||||
LineChartData(
|
LineChartData(
|
||||||
lineTouchData: LineTouchData(
|
lineTouchData: LineTouchData(
|
||||||
@ -151,7 +153,7 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
|
|||||||
child: RotatedBox(
|
child: RotatedBox(
|
||||||
quarterTurns: -1,
|
quarterTurns: -1,
|
||||||
child: Text(_chartData[index].time,
|
child: Text(_chartData[index].time,
|
||||||
style: TextStyle(fontSize: 10)),
|
style: const TextStyle(fontSize: 10)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -190,8 +192,8 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
|
|||||||
spots: _chartData
|
spots: _chartData
|
||||||
.asMap()
|
.asMap()
|
||||||
.entries
|
.entries
|
||||||
.map((entry) => FlSpot(entry.key.toDouble(),
|
.map((entry) => FlSpot(
|
||||||
entry.value.consumption))
|
entry.key.toDouble(), entry.value.consumption))
|
||||||
.toList(),
|
.toList(),
|
||||||
isCurved: true,
|
isCurved: true,
|
||||||
color: ColorsManager.primaryColor.withOpacity(0.6),
|
color: ColorsManager.primaryColor.withOpacity(0.6),
|
||||||
@ -218,7 +220,7 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
|
|||||||
borderData: FlBorderData(
|
borderData: FlBorderData(
|
||||||
show: false,
|
show: false,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: Color(0xff023DFE).withOpacity(0.7),
|
color: const Color(0xff023DFE).withOpacity(0.7),
|
||||||
width: 10,
|
width: 10,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -253,7 +255,6 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
|
|||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: widget.onTap,
|
onTap: widget.onTap,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: SizedBox(
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(5),
|
padding: const EdgeInsets.all(5),
|
||||||
child: Text(widget.date),
|
child: Text(widget.date),
|
||||||
@ -262,7 +263,6 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -12,8 +12,7 @@ import 'package:syncrow_web/utils/constants/assets.dart';
|
|||||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
//Smart Power Clamp
|
//Smart Power Clamp
|
||||||
class SmartPowerDeviceControl extends StatelessWidget
|
class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayout {
|
||||||
with HelperResponsiveLayout {
|
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
|
|
||||||
const SmartPowerDeviceControl({super.key, required this.deviceId});
|
const SmartPowerDeviceControl({super.key, required this.deviceId});
|
||||||
@ -25,27 +24,27 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
..add(SmartPowerFetchDeviceEvent(deviceId)),
|
..add(SmartPowerFetchDeviceEvent(deviceId)),
|
||||||
child: BlocBuilder<SmartPowerBloc, SmartPowerState>(
|
child: BlocBuilder<SmartPowerBloc, SmartPowerState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final _blocProvider = BlocProvider.of<SmartPowerBloc>(context);
|
final blocProvider = BlocProvider.of<SmartPowerBloc>(context);
|
||||||
|
|
||||||
if (state is SmartPowerLoading) {
|
if (state is SmartPowerLoading) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else if (state is FakeState) {
|
} else if (state is FakeState) {
|
||||||
return _buildStatusControls(
|
return _buildStatusControls(
|
||||||
currentPage: _blocProvider.currentPage,
|
currentPage: blocProvider.currentPage,
|
||||||
context: context,
|
context: context,
|
||||||
blocProvider: _blocProvider,
|
blocProvider: blocProvider,
|
||||||
);
|
);
|
||||||
} else if (state is GetDeviceStatus) {
|
} else if (state is GetDeviceStatus) {
|
||||||
return _buildStatusControls(
|
return _buildStatusControls(
|
||||||
currentPage: _blocProvider.currentPage,
|
currentPage: blocProvider.currentPage,
|
||||||
context: context,
|
context: context,
|
||||||
blocProvider: _blocProvider,
|
blocProvider: blocProvider,
|
||||||
);
|
);
|
||||||
} else if (state is FilterRecordsState) {
|
} else if (state is FilterRecordsState) {
|
||||||
return _buildStatusControls(
|
return _buildStatusControls(
|
||||||
currentPage: _blocProvider.currentPage,
|
currentPage: blocProvider.currentPage,
|
||||||
context: context,
|
context: context,
|
||||||
blocProvider: _blocProvider,
|
blocProvider: blocProvider,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
@ -60,7 +59,7 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
required SmartPowerBloc blocProvider,
|
required SmartPowerBloc blocProvider,
|
||||||
required int currentPage,
|
required int currentPage,
|
||||||
}) {
|
}) {
|
||||||
PageController _pageController = PageController(initialPage: currentPage);
|
PageController pageController = PageController(initialPage: currentPage);
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||||
child: DeviceControlsContainer(
|
child: DeviceControlsContainer(
|
||||||
@ -85,25 +84,31 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
PowerClampInfoCard(
|
PowerClampInfoCard(
|
||||||
iconPath: Assets.powerActiveIcon,
|
iconPath: Assets.powerActiveIcon,
|
||||||
title: 'Active',
|
title: 'Active',
|
||||||
value: blocProvider
|
value: blocProvider.deviceStatus.status.general.dataPoints
|
||||||
.deviceStatus.status.general.dataPoints[2].value
|
.elementAtOrNull(2)
|
||||||
.toString(),
|
?.value
|
||||||
|
.toString() ??
|
||||||
|
'',
|
||||||
unit: '',
|
unit: '',
|
||||||
),
|
),
|
||||||
PowerClampInfoCard(
|
PowerClampInfoCard(
|
||||||
iconPath: Assets.voltMeterIcon,
|
iconPath: Assets.voltMeterIcon,
|
||||||
title: 'Current',
|
title: 'Current',
|
||||||
value: blocProvider
|
value: blocProvider.deviceStatus.status.general.dataPoints
|
||||||
.deviceStatus.status.general.dataPoints[1].value
|
.elementAtOrNull(1)
|
||||||
.toString(),
|
?.value
|
||||||
|
.toString() ??
|
||||||
|
'',
|
||||||
unit: ' A',
|
unit: ' A',
|
||||||
),
|
),
|
||||||
PowerClampInfoCard(
|
PowerClampInfoCard(
|
||||||
iconPath: Assets.frequencyIcon,
|
iconPath: Assets.frequencyIcon,
|
||||||
title: 'Frequency',
|
title: 'Frequency',
|
||||||
value: blocProvider
|
value: blocProvider.deviceStatus.status.general.dataPoints
|
||||||
.deviceStatus.status.general.dataPoints[4].value
|
.elementAtOrNull(4)
|
||||||
.toString(),
|
?.value
|
||||||
|
.toString() ??
|
||||||
|
'',
|
||||||
unit: ' Hz',
|
unit: ' Hz',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -142,7 +147,7 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
icon: const Icon(Icons.arrow_left),
|
icon: const Icon(Icons.arrow_left),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
blocProvider.add(SmartPowerArrowPressedEvent(-1));
|
blocProvider.add(SmartPowerArrowPressedEvent(-1));
|
||||||
_pageController.previousPage(
|
pageController.previousPage(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
@ -162,7 +167,7 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
icon: const Icon(Icons.arrow_right),
|
icon: const Icon(Icons.arrow_right),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
blocProvider.add(SmartPowerArrowPressedEvent(1));
|
blocProvider.add(SmartPowerArrowPressedEvent(1));
|
||||||
_pageController.nextPage(
|
pageController.nextPage(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
@ -177,7 +182,7 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: PageView(
|
child: PageView(
|
||||||
controller: _pageController,
|
controller: pageController,
|
||||||
onPageChanged: (int page) {
|
onPageChanged: (int page) {
|
||||||
blocProvider.add(SmartPowerPageChangedEvent(page));
|
blocProvider.add(SmartPowerPageChangedEvent(page));
|
||||||
},
|
},
|
||||||
@ -190,8 +195,8 @@ class SmartPowerDeviceControl extends StatelessWidget
|
|||||||
blocProvider.add(SelectDateEvent(context: context));
|
blocProvider.add(SelectDateEvent(context: context));
|
||||||
blocProvider.add(FilterRecordsByDateEvent(
|
blocProvider.add(FilterRecordsByDateEvent(
|
||||||
selectedDate: blocProvider.dateTime!,
|
selectedDate: blocProvider.dateTime!,
|
||||||
viewType: blocProvider
|
viewType:
|
||||||
.views[blocProvider.currentIndex]));
|
blocProvider.views[blocProvider.currentIndex]));
|
||||||
},
|
},
|
||||||
widget: blocProvider.dateSwitcher(),
|
widget: blocProvider.dateSwitcher(),
|
||||||
chartData: blocProvider.energyDataList.isNotEmpty
|
chartData: blocProvider.energyDataList.isNotEmpty
|
||||||
|
@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/routines/models/ac/ac_operational_value.dart';
|
|||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
@ -74,11 +75,26 @@ class ACHelper {
|
|||||||
child: _buildFunctionsList(
|
child: _buildFunctionsList(
|
||||||
context: context,
|
context: context,
|
||||||
acFunctions: acFunctions,
|
acFunctions: acFunctions,
|
||||||
onFunctionSelected: (functionCode, operationName) =>
|
device: device,
|
||||||
context.read<FunctionBloc>().add(SelectFunction(
|
onFunctionSelected: (functionCode, operationName) {
|
||||||
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
|
context,
|
||||||
functionCode: functionCode,
|
functionCode: functionCode,
|
||||||
operationName: operationName,
|
functionOperationName: operationName,
|
||||||
)),
|
functionValueDescription:
|
||||||
|
selectedFunctionData.valueDescription,
|
||||||
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'temp_set',
|
||||||
|
'temp_current',
|
||||||
|
],
|
||||||
|
defaultValue: functionCode == 'temp_set'
|
||||||
|
? 200
|
||||||
|
: functionCode == 'temp_current'
|
||||||
|
? -100
|
||||||
|
: 0,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Value selector
|
// Value selector
|
||||||
@ -137,6 +153,7 @@ class ACHelper {
|
|||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
required List<ACFunction> acFunctions,
|
required List<ACFunction> acFunctions,
|
||||||
required Function(String, String) onFunctionSelected,
|
required Function(String, String) onFunctionSelected,
|
||||||
|
required AllDevicesModel? device,
|
||||||
}) {
|
}) {
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
shrinkWrap: false,
|
shrinkWrap: false,
|
||||||
|
@ -131,7 +131,12 @@ class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
|||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
CpsFunctionsList(cpsFunctions: _cpsFunctions),
|
CpsFunctionsList(
|
||||||
|
cpsFunctions: _cpsFunctions,
|
||||||
|
device: widget.device,
|
||||||
|
selectedFunctionData: selectedFunctionData,
|
||||||
|
dialogType: widget.dialogType,
|
||||||
|
),
|
||||||
if (state.selectedFunction != null)
|
if (state.selectedFunction != null)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: isToggleFunction
|
child: isToggleFunction
|
||||||
|
@ -1,14 +1,24 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialog_function_list_tile.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialog_function_list_tile.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class CpsFunctionsList extends StatelessWidget {
|
class CpsFunctionsList extends StatelessWidget {
|
||||||
const CpsFunctionsList({required this.cpsFunctions, super.key});
|
const CpsFunctionsList({
|
||||||
|
required this.cpsFunctions,
|
||||||
|
required this.device,
|
||||||
|
required this.selectedFunctionData,
|
||||||
|
required this.dialogType,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final List<CpsFunctions> cpsFunctions;
|
final List<CpsFunctions> cpsFunctions;
|
||||||
|
final AllDevicesModel? device;
|
||||||
|
final DeviceFunctionData? selectedFunctionData;
|
||||||
|
final String dialogType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -26,11 +36,26 @@ class CpsFunctionsList extends StatelessWidget {
|
|||||||
return RoutineDialogFunctionListTile(
|
return RoutineDialogFunctionListTile(
|
||||||
iconPath: function.icon,
|
iconPath: function.icon,
|
||||||
operationName: function.operationName,
|
operationName: function.operationName,
|
||||||
onTap: () => context.read<FunctionBloc>().add(
|
onTap: () => RoutineTapFunctionHelper.onTapFunction(
|
||||||
SelectFunction(
|
context,
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
operationName: function.operationName,
|
functionOperationName: function.operationName,
|
||||||
),
|
functionValueDescription: selectedFunctionData?.valueDescription,
|
||||||
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'static_max_dis',
|
||||||
|
'presence_reference',
|
||||||
|
'moving_reference',
|
||||||
|
'perceptual_boundary',
|
||||||
|
'moving_boundary',
|
||||||
|
'moving_rigger_time',
|
||||||
|
'moving_static_time',
|
||||||
|
'none_body_time',
|
||||||
|
'moving_max_dis',
|
||||||
|
'moving_range',
|
||||||
|
'presence_range',
|
||||||
|
if (dialogType == "IF") 'sensitivity',
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
|
|
||||||
|
abstract final class RoutineTapFunctionHelper {
|
||||||
|
const RoutineTapFunctionHelper._();
|
||||||
|
|
||||||
|
static void onTapFunction(
|
||||||
|
BuildContext context, {
|
||||||
|
required String functionCode,
|
||||||
|
required String functionOperationName,
|
||||||
|
required String? functionValueDescription,
|
||||||
|
required String? deviceUuid,
|
||||||
|
required List<String> codesToAddIntoFunctionsWithDefaultValue,
|
||||||
|
int defaultValue = 0,
|
||||||
|
}) {
|
||||||
|
final functionsBloc = context.read<FunctionBloc>();
|
||||||
|
functionsBloc.add(
|
||||||
|
SelectFunction(
|
||||||
|
functionCode: functionCode,
|
||||||
|
operationName: functionOperationName,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final addedFunctions = functionsBloc.state.addedFunctions;
|
||||||
|
final isFunctionAlreadyAdded = addedFunctions.any(
|
||||||
|
(e) => e.functionCode == functionCode,
|
||||||
|
);
|
||||||
|
final shouldAddFunction =
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue.contains(functionCode);
|
||||||
|
if (!isFunctionAlreadyAdded && shouldAddFunction) {
|
||||||
|
context.read<FunctionBloc>().add(
|
||||||
|
AddFunction(
|
||||||
|
functionData: DeviceFunctionData(
|
||||||
|
entityId: deviceUuid ?? '',
|
||||||
|
functionCode: functionCode,
|
||||||
|
operationName: functionOperationName,
|
||||||
|
value: defaultValue,
|
||||||
|
condition: '==',
|
||||||
|
valueDescription: functionValueDescription,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/
|
|||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
@ -24,21 +25,23 @@ class OneGangSwitchHelper {
|
|||||||
required String uniqueCustomId,
|
required String uniqueCustomId,
|
||||||
required bool removeComparetors,
|
required bool removeComparetors,
|
||||||
}) async {
|
}) async {
|
||||||
List<BaseSwitchFunction> oneGangFunctions = functions.whereType<BaseSwitchFunction>().toList();
|
List<BaseSwitchFunction> oneGangFunctions =
|
||||||
|
functions.whereType<BaseSwitchFunction>().toList();
|
||||||
|
|
||||||
return showDialog<Map<String, dynamic>?>(
|
return showDialog<Map<String, dynamic>?>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
|
create: (_) => FunctionBloc()
|
||||||
|
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
|
||||||
child: AlertDialog(
|
child: AlertDialog(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
|
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final selectedFunction = state.selectedFunction;
|
final selectedFunction = state.selectedFunction;
|
||||||
final selectedOperationName = state.selectedOperationName;
|
final selectedOperationName = state.selectedOperationName;
|
||||||
final selectedFunctionData =
|
final selectedFunctionData = state.addedFunctions
|
||||||
state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction,
|
.firstWhere((f) => f.functionCode == selectedFunction,
|
||||||
orElse: () => DeviceFunctionData(
|
orElse: () => DeviceFunctionData(
|
||||||
entityId: '',
|
entityId: '',
|
||||||
functionCode: selectedFunction ?? '',
|
functionCode: selectedFunction ?? '',
|
||||||
@ -84,12 +87,19 @@ class OneGangSwitchHelper {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () =>
|
||||||
context.read<FunctionBloc>().add(SelectFunction(
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
|
context,
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
operationName: function.operationName,
|
functionOperationName:
|
||||||
));
|
function.operationName,
|
||||||
},
|
functionValueDescription:
|
||||||
|
selectedFunctionData.valueDescription,
|
||||||
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'countdown_1',
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -220,11 +230,11 @@ class OneGangSwitchHelper {
|
|||||||
selectedFunctionData,
|
selectedFunctionData,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
_buildCountDownDisplay(
|
_buildCountDownDisplay(context, initialValue, device, operationName,
|
||||||
context, initialValue, device, operationName, selectedFunctionData, selectCode),
|
selectedFunctionData, selectCode),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
_buildCountDownSlider(
|
_buildCountDownSlider(context, initialValue, device, operationName,
|
||||||
context, initialValue, device, operationName, selectedFunctionData, selectCode),
|
selectedFunctionData, selectCode),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -314,7 +324,8 @@ class OneGangSwitchHelper {
|
|||||||
value: (initialValue ?? 0).toDouble(),
|
value: (initialValue ?? 0).toDouble(),
|
||||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||||
divisions: (((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
divisions:
|
||||||
|
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||||
(operationalValues.stepValue ?? 1))
|
(operationalValues.stepValue ?? 1))
|
||||||
.round(),
|
.round(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -368,7 +379,9 @@ class OneGangSwitchHelper {
|
|||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray,
|
color: isSelected
|
||||||
|
? ColorsManager.primaryColorWithOpacity
|
||||||
|
: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (!isSelected) {
|
if (!isSelected) {
|
||||||
|
@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_func
|
|||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
@ -85,15 +86,21 @@ class ThreeGangSwitchHelper {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () =>
|
||||||
context
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
.read<FunctionBloc>()
|
context,
|
||||||
.add(SelectFunction(
|
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
operationName:
|
functionOperationName:
|
||||||
function.operationName,
|
function.operationName,
|
||||||
));
|
functionValueDescription:
|
||||||
},
|
selectedFunctionData.valueDescription,
|
||||||
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'countdown_1',
|
||||||
|
'countdown_2',
|
||||||
|
'countdown_3',
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -183,8 +190,7 @@ class ThreeGangSwitchHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final selectedFn =
|
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||||
switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
|
||||||
final values = selectedFn.getOperationalValues();
|
final values = selectedFn.getOperationalValues();
|
||||||
|
|
||||||
return _buildOperationalValuesList(
|
return _buildOperationalValuesList(
|
||||||
@ -266,8 +272,7 @@ class ThreeGangSwitchHelper {
|
|||||||
minHeight: 40.0,
|
minHeight: 40.0,
|
||||||
minWidth: 40.0,
|
minWidth: 40.0,
|
||||||
),
|
),
|
||||||
isSelected:
|
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
|
||||||
children: conditions.map((c) => Text(c)).toList(),
|
children: conditions.map((c) => Text(c)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -316,8 +321,8 @@ class ThreeGangSwitchHelper {
|
|||||||
value: (initialValue ?? 0).toDouble(),
|
value: (initialValue ?? 0).toDouble(),
|
||||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||||
divisions: (((operationalValues.maxValue ?? 0) -
|
divisions:
|
||||||
(operationalValues.minValue ?? 0)) /
|
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||||
(operationalValues.stepValue ?? 1))
|
(operationalValues.stepValue ?? 1))
|
||||||
.round(),
|
.round(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -369,9 +374,7 @@ class ThreeGangSwitchHelper {
|
|||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected
|
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||||
? Icons.radio_button_checked
|
|
||||||
: Icons.radio_button_unchecked,
|
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.primaryColorWithOpacity
|
? ColorsManager.primaryColorWithOpacity
|
||||||
@ -387,8 +390,7 @@ class ThreeGangSwitchHelper {
|
|||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value.value,
|
value: value.value,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription:
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
selectedFunctionData?.valueDescription,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_func
|
|||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
@ -85,15 +86,20 @@ class TwoGangSwitchHelper {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () =>
|
||||||
context
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
.read<FunctionBloc>()
|
context,
|
||||||
.add(SelectFunction(
|
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
operationName:
|
functionOperationName:
|
||||||
function.operationName,
|
function.operationName,
|
||||||
));
|
functionValueDescription:
|
||||||
},
|
selectedFunctionData.valueDescription,
|
||||||
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'countdown_1',
|
||||||
|
'countdown_2',
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -167,8 +173,7 @@ class TwoGangSwitchHelper {
|
|||||||
required String operationName,
|
required String operationName,
|
||||||
required bool removeComparetors,
|
required bool removeComparetors,
|
||||||
}) {
|
}) {
|
||||||
if (selectedFunction == 'countdown_1' ||
|
if (selectedFunction == 'countdown_1' || selectedFunction == 'countdown_2') {
|
||||||
selectedFunction == 'countdown_2') {
|
|
||||||
final initialValue = selectedFunctionData?.value ?? 0;
|
final initialValue = selectedFunctionData?.value ?? 0;
|
||||||
return _buildTemperatureSelector(
|
return _buildTemperatureSelector(
|
||||||
context: context,
|
context: context,
|
||||||
@ -182,8 +187,7 @@ class TwoGangSwitchHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final selectedFn =
|
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||||
switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
|
||||||
final values = selectedFn.getOperationalValues();
|
final values = selectedFn.getOperationalValues();
|
||||||
|
|
||||||
return _buildOperationalValuesList(
|
return _buildOperationalValuesList(
|
||||||
@ -265,8 +269,7 @@ class TwoGangSwitchHelper {
|
|||||||
minHeight: 40.0,
|
minHeight: 40.0,
|
||||||
minWidth: 40.0,
|
minWidth: 40.0,
|
||||||
),
|
),
|
||||||
isSelected:
|
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
|
||||||
children: conditions.map((c) => Text(c)).toList(),
|
children: conditions.map((c) => Text(c)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -315,8 +318,8 @@ class TwoGangSwitchHelper {
|
|||||||
value: (initialValue ?? 0).toDouble(),
|
value: (initialValue ?? 0).toDouble(),
|
||||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||||
divisions: (((operationalValues.maxValue ?? 0) -
|
divisions:
|
||||||
(operationalValues.minValue ?? 0)) /
|
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||||
(operationalValues.stepValue ?? 1))
|
(operationalValues.stepValue ?? 1))
|
||||||
.round(),
|
.round(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -368,9 +371,7 @@ class TwoGangSwitchHelper {
|
|||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected
|
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||||
? Icons.radio_button_checked
|
|
||||||
: Icons.radio_button_unchecked,
|
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.primaryColorWithOpacity
|
? ColorsManager.primaryColorWithOpacity
|
||||||
@ -386,8 +387,7 @@ class TwoGangSwitchHelper {
|
|||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value.value,
|
value: value.value,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription:
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
selectedFunctionData?.valueDescription,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -8,6 +8,7 @@ import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
|||||||
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
@ -111,13 +112,23 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
|||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
_buildFunctionList(context),
|
_buildFunctionList(context, state),
|
||||||
if (state.selectedFunction != null) _buildValueSelector(context, state),
|
if (state.selectedFunction != null) _buildValueSelector(context, state),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFunctionList(BuildContext context) {
|
Widget _buildFunctionList(BuildContext context, FunctionBlocState state) {
|
||||||
|
final selectedFunction = state.selectedFunction;
|
||||||
|
final selectedFunctionData = state.addedFunctions.firstWhere(
|
||||||
|
(f) => f.functionCode == selectedFunction,
|
||||||
|
orElse: () => DeviceFunctionData(
|
||||||
|
entityId: '',
|
||||||
|
functionCode: selectedFunction ?? '',
|
||||||
|
operationName: '',
|
||||||
|
value: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 360,
|
width: 360,
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
@ -149,11 +160,17 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () => context.read<FunctionBloc>().add(
|
onTap: () => RoutineTapFunctionHelper.onTapFunction(
|
||||||
SelectFunction(
|
context,
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
operationName: function.operationName,
|
functionOperationName: function.operationName,
|
||||||
),
|
functionValueDescription: selectedFunctionData.valueDescription,
|
||||||
|
deviceUuid: widget.device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'dis_current',
|
||||||
|
'presence_time',
|
||||||
|
'illuminance_value',
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
@ -246,7 +247,9 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
final previousState = state;
|
final previousState = state;
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
||||||
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc);
|
var spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
||||||
|
|
||||||
|
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc, spaceTreeState);
|
||||||
await fetchSpaceModels();
|
await fetchSpaceModels();
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
|
|
||||||
@ -277,11 +280,13 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
LoadCommunityAndSpacesEvent event,
|
LoadCommunityAndSpacesEvent event,
|
||||||
Emitter<SpaceManagementState> emit,
|
Emitter<SpaceManagementState> emit,
|
||||||
) async {
|
) async {
|
||||||
|
var spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
||||||
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
||||||
|
|
||||||
_onloadProducts();
|
_onloadProducts();
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
// Wait until `communityList` is loaded
|
// Wait until `communityList` is loaded
|
||||||
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc);
|
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc, spaceTreeState);
|
||||||
|
|
||||||
// Fetch space models after communities are available
|
// Fetch space models after communities are available
|
||||||
final prevSpaceModels = await fetchSpaceModels();
|
final prevSpaceModels = await fetchSpaceModels();
|
||||||
@ -292,23 +297,38 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
allTags: _cachedTags ?? []));
|
allTags: _cachedTags ?? []));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<CommunityModel>> _waitForCommunityList(SpaceTreeBloc spaceBloc) async {
|
Future<List<CommunityModel>> _waitForCommunityList(
|
||||||
|
SpaceTreeBloc spaceBloc, SpaceTreeState spaceTreeState) async {
|
||||||
// Check if communityList is already populated
|
// Check if communityList is already populated
|
||||||
if (spaceBloc.state.communityList.isNotEmpty) {
|
final filteredCommunities = spaceTreeState.searchQuery.isNotEmpty
|
||||||
return spaceBloc.state.communityList;
|
? spaceTreeState.filteredCommunity
|
||||||
|
: spaceTreeState.communityList;
|
||||||
|
if (filteredCommunities.isNotEmpty) {
|
||||||
|
return filteredCommunities;
|
||||||
}
|
}
|
||||||
|
|
||||||
final completer = Completer<List<CommunityModel>>();
|
final completer = Completer<List<CommunityModel>>();
|
||||||
final subscription = spaceBloc.stream.listen((state) {
|
final subscription = spaceBloc.stream.listen((state) {
|
||||||
if (state.communityList.isNotEmpty) {
|
if (!completer.isCompleted && state.communityList.isNotEmpty) {
|
||||||
completer.complete(state.communityList);
|
completer
|
||||||
|
.complete(state.searchQuery.isNotEmpty ? state.filteredCommunity : state.communityList);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
try {
|
||||||
|
final communities = await completer.future.timeout(
|
||||||
|
const Duration(seconds: 10),
|
||||||
|
onTimeout: () {
|
||||||
|
if (!completer.isCompleted) {
|
||||||
|
completer.complete([]);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// Return the list once available, then cancel the listener
|
|
||||||
final communities = await completer.future;
|
|
||||||
await subscription.cancel();
|
|
||||||
return communities;
|
return communities;
|
||||||
|
} finally {
|
||||||
|
await subscription.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onCommunityDelete(
|
void _onCommunityDelete(
|
||||||
@ -446,6 +466,7 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
|
|
||||||
if (previousState is SpaceManagementLoaded) {
|
if (previousState is SpaceManagementLoaded) {
|
||||||
await _updateLoadedState(
|
await _updateLoadedState(
|
||||||
|
event.context,
|
||||||
previousState,
|
previousState,
|
||||||
allSpaces,
|
allSpaces,
|
||||||
event.communityUuid,
|
event.communityUuid,
|
||||||
@ -462,6 +483,7 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _updateLoadedState(
|
Future<void> _updateLoadedState(
|
||||||
|
BuildContext context,
|
||||||
SpaceManagementLoaded previousState,
|
SpaceManagementLoaded previousState,
|
||||||
List<SpaceModel> allSpaces,
|
List<SpaceModel> allSpaces,
|
||||||
String communityUuid,
|
String communityUuid,
|
||||||
@ -469,7 +491,10 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
) async {
|
) async {
|
||||||
var prevSpaceModels = await fetchSpaceModels();
|
var prevSpaceModels = await fetchSpaceModels();
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
final communities = List<CommunityModel>.from(previousState.communities);
|
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||||
|
final communities = spaceTreeState.searchQuery.isNotEmpty
|
||||||
|
? spaceTreeState.filteredCommunity
|
||||||
|
: spaceTreeState.communityList;
|
||||||
|
|
||||||
for (var community in communities) {
|
for (var community in communities) {
|
||||||
if (community.uuid == communityUuid) {
|
if (community.uuid == communityUuid) {
|
||||||
@ -484,6 +509,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
spaceModels: prevSpaceModels,
|
spaceModels: prevSpaceModels,
|
||||||
allTags: _cachedTags ?? []));
|
allTags: _cachedTags ?? []));
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
print("Community not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -491,12 +518,21 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
Future<List<SpaceModel>> saveSpacesHierarchically(
|
Future<List<SpaceModel>> saveSpacesHierarchically(
|
||||||
BuildContext context, List<SpaceModel> spaces, String communityUuid) async {
|
BuildContext context, List<SpaceModel> spaces, String communityUuid) async {
|
||||||
final orderedSpaces = flattenHierarchy(spaces);
|
final orderedSpaces = flattenHierarchy(spaces);
|
||||||
|
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
CommunityModel? selectedCommunity;
|
||||||
List<CommunityModel> communities = spaceBloc.state.communityList;
|
try {
|
||||||
CommunityModel? selectedCommunity = communities.firstWhere(
|
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||||
|
final filteredCommunities = spaceTreeState.searchQuery.isNotEmpty
|
||||||
|
? spaceTreeState.filteredCommunity
|
||||||
|
: spaceTreeState.communityList;
|
||||||
|
|
||||||
|
selectedCommunity = filteredCommunities.firstWhere(
|
||||||
(community) => community.uuid == communityUuid,
|
(community) => community.uuid == communityUuid,
|
||||||
);
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
final parentsToDelete = orderedSpaces.where((space) =>
|
final parentsToDelete = orderedSpaces.where((space) =>
|
||||||
space.status == SpaceStatus.deleted &&
|
space.status == SpaceStatus.deleted &&
|
||||||
@ -605,11 +641,11 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
projectId: projectUuid);
|
projectId: projectUuid);
|
||||||
} else {
|
} else {
|
||||||
// Call create if the space does not have a UUID
|
// Call create if the space does not have a UUID
|
||||||
final List<CreateTagBodyModel> tagBodyModels = space.tags != null
|
List<CreateTagBodyModel> tagBodyModels = space.tags != null
|
||||||
? space.tags!.map((tag) => tag.toCreateTagBodyModel()).toList()
|
? space.tags!.map((tag) => tag.toCreateTagBodyModel()).toList()
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
final createSubspaceBodyModels = space.subspaces?.map((subspace) {
|
var createSubspaceBodyModels = space.subspaces?.map((subspace) {
|
||||||
final tagBodyModels =
|
final tagBodyModels =
|
||||||
subspace.tags?.map((tag) => tag.toCreateTagBodyModel()).toList() ?? [];
|
subspace.tags?.map((tag) => tag.toCreateTagBodyModel()).toList() ?? [];
|
||||||
return CreateSubspaceModel()
|
return CreateSubspaceModel()
|
||||||
@ -618,6 +654,11 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
}).toList() ??
|
}).toList() ??
|
||||||
[];
|
[];
|
||||||
|
|
||||||
|
if (space.spaceModel?.uuid != null) {
|
||||||
|
tagBodyModels = [];
|
||||||
|
createSubspaceBodyModels = [];
|
||||||
|
}
|
||||||
|
|
||||||
final response = await _api.createSpace(
|
final response = await _api.createSpace(
|
||||||
communityId: communityUuid,
|
communityId: communityUuid,
|
||||||
name: space.name,
|
name: space.name,
|
||||||
@ -669,9 +710,12 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
||||||
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
final filteredCommunities = spaceTreeState.searchQuery.isNotEmpty
|
||||||
List<CommunityModel> communities = spaceBloc.state.communityList;
|
? spaceTreeState.filteredCommunity
|
||||||
|
: spaceTreeState.communityList;
|
||||||
|
|
||||||
|
List<CommunityModel> communities = filteredCommunities;
|
||||||
|
|
||||||
var prevSpaceModels = await fetchSpaceModels();
|
var prevSpaceModels = await fetchSpaceModels();
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
// Flutter imports
|
// Flutter imports
|
||||||
|
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';
|
||||||
|
|
||||||
@ -70,6 +72,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
_nameController = TextEditingController(
|
_nameController = TextEditingController(
|
||||||
text: widget.selectedCommunity?.name ?? '',
|
text: widget.selectedCommunity?.name ?? '',
|
||||||
);
|
);
|
||||||
|
realignTree();
|
||||||
|
|
||||||
_transformationController = TransformationController();
|
_transformationController = TransformationController();
|
||||||
if (widget.selectedSpace != null) {
|
if (widget.selectedSpace != null) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
@ -94,6 +98,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||||
_adjustCanvasSizeForSpaces();
|
_adjustCanvasSizeForSpaces();
|
||||||
|
realignTree();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,6 +341,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
spaces.add(newSpace);
|
spaces.add(newSpace);
|
||||||
_updateNodePosition(newSpace, newSpace.position);
|
_updateNodePosition(newSpace, newSpace.position);
|
||||||
|
realignTree();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -401,21 +407,31 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
|
|
||||||
List<SpaceModel> flattenSpaces(List<SpaceModel> spaces) {
|
List<SpaceModel> flattenSpaces(List<SpaceModel> spaces) {
|
||||||
List<SpaceModel> result = [];
|
List<SpaceModel> result = [];
|
||||||
|
Map<String, SpaceModel> idToSpace = {};
|
||||||
|
|
||||||
void flatten(SpaceModel space) {
|
void flatten(SpaceModel space) {
|
||||||
if (space.status == SpaceStatus.deleted || space.status == SpaceStatus.parentDeleted) {
|
if (space.status == SpaceStatus.deleted || space.status == SpaceStatus.parentDeleted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
result.add(space);
|
result.add(space);
|
||||||
|
idToSpace[space.internalId] = space;
|
||||||
|
|
||||||
for (var child in space.children) {
|
for (var child in space.children) {
|
||||||
flatten(child);
|
flatten(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (var space in spaces) {
|
for (var space in spaces) {
|
||||||
flatten(space);
|
flatten(space);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var space in result) {
|
||||||
|
if (space.parent != null) {
|
||||||
|
space.parent = idToSpace[space.parent!.internalId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,7 +466,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
|
|
||||||
void _saveSpaces() {
|
void _saveSpaces() {
|
||||||
if (widget.selectedCommunity == null) {
|
if (widget.selectedCommunity == null) {
|
||||||
debugPrint("No community selected for saving spaces.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,7 +480,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String communityUuid = widget.selectedCommunity!.uuid;
|
String communityUuid = widget.selectedCommunity!.uuid;
|
||||||
|
|
||||||
context.read<SpaceManagementBloc>().add(SaveSpacesEvent(
|
context.read<SpaceManagementBloc>().add(SaveSpacesEvent(
|
||||||
context,
|
context,
|
||||||
spaces: spacesToSave,
|
spaces: spacesToSave,
|
||||||
@ -530,35 +544,83 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Offset getBalancedChildPosition(SpaceModel parent) {
|
Offset getBalancedChildPosition(SpaceModel parent) {
|
||||||
int totalSiblings = parent.children.length + 1;
|
const double nodeWidth = 200;
|
||||||
double totalWidth = (totalSiblings - 1) * 250; // Horizontal spacing
|
const double verticalGap = 180;
|
||||||
|
|
||||||
|
if (parent.children.isEmpty) {
|
||||||
|
// First child → exactly center
|
||||||
|
return Offset(parent.position.dx, parent.position.dy + verticalGap);
|
||||||
|
} else {
|
||||||
|
// More children → arrange them spaced horizontally
|
||||||
|
double totalWidth = (parent.children.length) * (nodeWidth + 60);
|
||||||
double startX = parent.position.dx - (totalWidth / 2);
|
double startX = parent.position.dx - (totalWidth / 2);
|
||||||
|
|
||||||
Offset position = Offset(startX + (parent.children.length * 250), parent.position.dy + 180);
|
double childX = startX + (parent.children.length * (nodeWidth + 60));
|
||||||
|
return Offset(childX, parent.position.dy + verticalGap);
|
||||||
// Check for overlaps & adjust
|
|
||||||
while (spaces.any((s) => (s.position - position).distance < 250)) {
|
|
||||||
position = Offset(position.dx + 250, position.dy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return position;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void realignTree() {
|
void realignTree() {
|
||||||
void updatePositions(SpaceModel node, double x, double y) {
|
const double nodeWidth = 200;
|
||||||
node.position = Offset(x, y);
|
const double nodeHeight = 100;
|
||||||
|
const double horizontalGap = 60;
|
||||||
|
const double verticalGap = 180;
|
||||||
|
const double rootGap = 400; // extra space between different roots
|
||||||
|
|
||||||
int numChildren = node.children.length;
|
double canvasRightEdge = 1000;
|
||||||
double childStartX = x - ((numChildren - 1) * 250) / 2;
|
double canvasBottomEdge = 1000;
|
||||||
|
|
||||||
for (int i = 0; i < numChildren; i++) {
|
double calculateSubtreeWidth(SpaceModel node) {
|
||||||
updatePositions(node.children[i], childStartX + (i * 250), y + 180);
|
if (node.children.isEmpty) return nodeWidth;
|
||||||
|
double totalWidth = 0;
|
||||||
|
for (var child in node.children) {
|
||||||
|
totalWidth += calculateSubtreeWidth(child) + horizontalGap;
|
||||||
|
}
|
||||||
|
return totalWidth - horizontalGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void layoutSubtree(SpaceModel node, double startX, double y) {
|
||||||
|
double subtreeWidth = calculateSubtreeWidth(node);
|
||||||
|
double centerX = startX + subtreeWidth / 2 - nodeWidth / 2;
|
||||||
|
node.position = Offset(centerX, y);
|
||||||
|
|
||||||
|
canvasRightEdge = max(canvasRightEdge, centerX + nodeWidth);
|
||||||
|
canvasBottomEdge = max(canvasBottomEdge, y + nodeHeight);
|
||||||
|
|
||||||
|
if (node.children.length == 1) {
|
||||||
|
final child = node.children.first;
|
||||||
|
layoutSubtree(child, centerX, y + verticalGap);
|
||||||
|
} else {
|
||||||
|
double childX = startX;
|
||||||
|
for (var child in node.children) {
|
||||||
|
double childWidth = calculateSubtreeWidth(child);
|
||||||
|
layoutSubtree(child, childX, y + verticalGap);
|
||||||
|
childX += childWidth + horizontalGap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spaces.isNotEmpty) {
|
// ⚡ New: layout each root separately
|
||||||
updatePositions(spaces.first, spaces.first.position.dx, spaces.first.position.dy);
|
final List<SpaceModel> roots = spaces
|
||||||
|
.where((s) =>
|
||||||
|
s.parent == null &&
|
||||||
|
s.status != SpaceStatus.deleted &&
|
||||||
|
s.status != SpaceStatus.parentDeleted)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
double currentX = 100; // start some margin from left
|
||||||
|
double currentY = 100; // top margin
|
||||||
|
|
||||||
|
for (var root in roots) {
|
||||||
|
layoutSubtree(root, currentX, currentY);
|
||||||
|
double rootWidth = calculateSubtreeWidth(root);
|
||||||
|
currentX += rootWidth + rootGap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
canvasWidth = canvasRightEdge + 400;
|
||||||
|
canvasHeight = canvasBottomEdge + 400;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDuplicate(BuildContext parentContext) {
|
void _onDuplicate(BuildContext parentContext) {
|
||||||
@ -642,63 +704,19 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _duplicateSpace(SpaceModel space) {
|
void _duplicateSpace(SpaceModel space) {
|
||||||
final Map<SpaceModel, SpaceModel> originalToDuplicate = {};
|
final double horizontalGap = 250.0;
|
||||||
double horizontalGap = 250.0; // Increased spacing
|
final double verticalGap = 180.0;
|
||||||
double verticalGap = 180.0; // Adjusted for better visualization
|
final double nodeWidth = 200;
|
||||||
|
final double nodeHeight = 100;
|
||||||
|
final double breathingSpace = 300.0;
|
||||||
|
|
||||||
print("🟢 Duplicating: ${space.name}");
|
/// Helper to recursively duplicate a node and its children
|
||||||
|
|
||||||
/// **Find a new position ensuring no overlap**
|
|
||||||
Offset getBalancedChildPosition(SpaceModel parent) {
|
|
||||||
int totalSiblings = parent.children.length + 1;
|
|
||||||
double totalWidth = (totalSiblings - 1) * horizontalGap;
|
|
||||||
double startX = parent.position.dx - (totalWidth / 2);
|
|
||||||
Offset position = Offset(
|
|
||||||
startX + (parent.children.length * horizontalGap), parent.position.dy + verticalGap);
|
|
||||||
|
|
||||||
// **Check for overlaps & adjust**
|
|
||||||
while (spaces.any((s) => (s.position - position).distance < horizontalGap)) {
|
|
||||||
position = Offset(position.dx + horizontalGap, position.dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
print("🔹 New position for ${parent.name}: (${position.dx}, ${position.dy})");
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// **Realign the entire tree after duplication**
|
|
||||||
void realignTree() {
|
|
||||||
void updatePositions(SpaceModel node, double x, double y) {
|
|
||||||
node.position = Offset(x, y);
|
|
||||||
print("✅ Adjusted ${node.name} to (${x}, ${y})");
|
|
||||||
|
|
||||||
int numChildren = node.children.length;
|
|
||||||
double childStartX = x - ((numChildren - 1) * horizontalGap) / 2;
|
|
||||||
|
|
||||||
for (int i = 0; i < numChildren; i++) {
|
|
||||||
updatePositions(node.children[i], childStartX + (i * horizontalGap), y + verticalGap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spaces.isNotEmpty) {
|
|
||||||
print("🔄 Realigning tree...");
|
|
||||||
updatePositions(spaces.first, spaces.first.position.dx, spaces.first.position.dy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// **Recursive duplication logic**
|
|
||||||
SpaceModel duplicateRecursive(SpaceModel original, SpaceModel? duplicatedParent) {
|
SpaceModel duplicateRecursive(SpaceModel original, SpaceModel? duplicatedParent) {
|
||||||
Offset newPosition = duplicatedParent == null
|
|
||||||
? Offset(original.position.dx + horizontalGap, original.position.dy)
|
|
||||||
: getBalancedChildPosition(duplicatedParent);
|
|
||||||
|
|
||||||
final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
||||||
print(
|
|
||||||
"🟡 Duplicating ${original.name} → ${duplicatedName} at (${newPosition.dx}, ${newPosition.dy})");
|
|
||||||
|
|
||||||
final duplicated = SpaceModel(
|
final duplicated = SpaceModel(
|
||||||
name: duplicatedName,
|
name: duplicatedName,
|
||||||
icon: original.icon,
|
icon: original.icon,
|
||||||
position: newPosition,
|
position: original.position,
|
||||||
isPrivate: original.isPrivate,
|
isPrivate: original.isPrivate,
|
||||||
children: [],
|
children: [],
|
||||||
status: SpaceStatus.newSpace,
|
status: SpaceStatus.newSpace,
|
||||||
@ -708,9 +726,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
tags: original.tags,
|
tags: original.tags,
|
||||||
);
|
);
|
||||||
|
|
||||||
setState(() {
|
|
||||||
spaces.add(duplicated);
|
spaces.add(duplicated);
|
||||||
_updateNodePosition(duplicated, duplicated.position);
|
|
||||||
|
|
||||||
if (duplicatedParent != null) {
|
if (duplicatedParent != null) {
|
||||||
final newConnection = Connection(
|
final newConnection = Connection(
|
||||||
@ -719,17 +735,13 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
direction: "down",
|
direction: "down",
|
||||||
);
|
);
|
||||||
connections.add(newConnection);
|
connections.add(newConnection);
|
||||||
|
|
||||||
duplicated.incomingConnection = newConnection;
|
duplicated.incomingConnection = newConnection;
|
||||||
duplicatedParent.addOutgoingConnection(newConnection);
|
duplicatedParent.addOutgoingConnection(newConnection);
|
||||||
duplicatedParent.children.add(duplicated);
|
duplicatedParent.children.add(duplicated);
|
||||||
print("🔗 Created connection: ${duplicatedParent.name} → ${duplicated.name}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// **Recalculate the whole tree to avoid overlaps**
|
// Duplicate its children recursively
|
||||||
realignTree();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Recursively duplicate children
|
|
||||||
for (var child in original.children) {
|
for (var child in original.children) {
|
||||||
duplicateRecursive(child, duplicated);
|
duplicateRecursive(child, duplicated);
|
||||||
}
|
}
|
||||||
@ -737,21 +749,30 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
return duplicated;
|
return duplicated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Handle root duplication**
|
/// Actual duplication process
|
||||||
if (space.parent == null) {
|
|
||||||
print("🟠 Duplicating root node: ${space.name}");
|
|
||||||
SpaceModel duplicatedRoot = duplicateRecursive(space, null);
|
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
spaces.add(duplicatedRoot);
|
if (space.parent == null) {
|
||||||
|
// Duplicating a ROOT node
|
||||||
|
duplicateRecursive(space, null);
|
||||||
|
connections = createConnections(spaces);
|
||||||
realignTree();
|
realignTree();
|
||||||
});
|
|
||||||
|
|
||||||
print("✅ Root duplication successful: ${duplicatedRoot.name}");
|
|
||||||
} else {
|
} else {
|
||||||
duplicateRecursive(space, space.parent);
|
final parent = space.parent!;
|
||||||
}
|
|
||||||
|
|
||||||
print("🟢 Finished duplication process for: ${space.name}");
|
final duplicated = duplicateRecursive(space, parent);
|
||||||
|
final newConnection = Connection(
|
||||||
|
startSpace: parent,
|
||||||
|
endSpace: duplicated,
|
||||||
|
direction: "down",
|
||||||
|
);
|
||||||
|
connections.add(newConnection);
|
||||||
|
duplicated.incomingConnection = newConnection;
|
||||||
|
parent.addOutgoingConnection(newConnection);
|
||||||
|
parent.children.add(duplicated);
|
||||||
|
connections = createConnections(spaces);
|
||||||
|
realignTree();
|
||||||
|
//_realignSubtree(space.parent!);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
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:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||||
@ -45,7 +46,6 @@ class _LoadedSpaceViewState extends State<LoadedSpaceView> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
_spaceModels = List.from(widget.spaceModels ?? []);
|
_spaceModels = List.from(widget.spaceModels ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,9 +106,8 @@ class _LoadedSpaceViewState extends State<LoadedSpaceView> {
|
|||||||
children: [
|
children: [
|
||||||
SidebarWidget(
|
SidebarWidget(
|
||||||
communities: widget.communities,
|
communities: widget.communities,
|
||||||
selectedSpaceUuid: widget.selectedSpace?.uuid ??
|
selectedSpaceUuid:
|
||||||
widget.selectedCommunity?.uuid ??
|
widget.selectedSpace?.uuid ?? widget.selectedCommunity?.uuid ?? '',
|
||||||
'',
|
|
||||||
onCreateCommunity: (name, description) {
|
onCreateCommunity: (name, description) {
|
||||||
context.read<SpaceManagementBloc>().add(
|
context.read<SpaceManagementBloc>().add(
|
||||||
CreateCommunityEvent(name, description, context),
|
CreateCommunityEvent(name, description, context),
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
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:syncrow_web/common/widgets/empty_search_result_widget.dart';
|
import 'package:syncrow_web/common/widgets/empty_search_result_widget.dart';
|
||||||
import 'package:syncrow_web/common/widgets/search_bar.dart';
|
import 'package:syncrow_web/common/widgets/search_bar.dart';
|
||||||
import 'package:syncrow_web/common/widgets/sidebar_communities_list.dart';
|
import 'package:syncrow_web/common/widgets/sidebar_communities_list.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||||
@ -15,6 +18,8 @@ import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/cent
|
|||||||
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_event.dart';
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
|
import '../../../space_tree/bloc/space_tree_event.dart';
|
||||||
|
|
||||||
class SidebarWidget extends StatefulWidget {
|
class SidebarWidget extends StatefulWidget {
|
||||||
final List<CommunityModel> communities;
|
final List<CommunityModel> communities;
|
||||||
final String? selectedSpaceUuid;
|
final String? selectedSpaceUuid;
|
||||||
@ -33,6 +38,7 @@ class SidebarWidget extends StatefulWidget {
|
|||||||
|
|
||||||
class _SidebarWidgetState extends State<SidebarWidget> {
|
class _SidebarWidgetState extends State<SidebarWidget> {
|
||||||
late final ScrollController _scrollController;
|
late final ScrollController _scrollController;
|
||||||
|
Timer? _debounce;
|
||||||
|
|
||||||
String _searchQuery = '';
|
String _searchQuery = '';
|
||||||
String? _selectedSpaceUuid;
|
String? _selectedSpaceUuid;
|
||||||
@ -40,17 +46,48 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_selectedId = widget.selectedSpaceUuid;
|
|
||||||
_scrollController = ScrollController();
|
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_scrollController = ScrollController();
|
||||||
|
_scrollController.addListener(_onScroll);
|
||||||
|
_selectedId = widget.selectedSpaceUuid;
|
||||||
|
_searchQuery = '';
|
||||||
|
context.read<SpaceTreeBloc>().add(ClearCachedData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onScroll() {
|
||||||
|
if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 100) {
|
||||||
|
// Trigger pagination event
|
||||||
|
final bloc = context.read<SpaceTreeBloc>();
|
||||||
|
if (!bloc.state.paginationIsLoading && bloc.state.paginationModel?.hasNext == true) {
|
||||||
|
bloc.add(
|
||||||
|
PaginationEvent(
|
||||||
|
bloc.state.paginationModel!,
|
||||||
|
bloc.state.communityList,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_scrollController.removeListener(_onScroll);
|
||||||
_scrollController.dispose();
|
_scrollController.dispose();
|
||||||
|
_debounce?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onSearchChanged(String query) {
|
||||||
|
if (_debounce?.isActive ?? false) _debounce?.cancel();
|
||||||
|
|
||||||
|
_debounce = Timer(const Duration(milliseconds: 500), () {
|
||||||
|
setState(() {
|
||||||
|
_searchQuery = query;
|
||||||
|
});
|
||||||
|
context.read<SpaceTreeBloc>().add(SearchQueryEvent(query));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant SidebarWidget oldWidget) {
|
void didUpdateWidget(covariant SidebarWidget oldWidget) {
|
||||||
if (widget.selectedSpaceUuid != oldWidget.selectedSpaceUuid) {
|
if (widget.selectedSpaceUuid != oldWidget.selectedSpaceUuid) {
|
||||||
@ -59,38 +96,6 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<CommunityModel> _filteredCommunities() {
|
|
||||||
if (_searchQuery.isEmpty) {
|
|
||||||
_selectedSpaceUuid = null;
|
|
||||||
return widget.communities;
|
|
||||||
}
|
|
||||||
|
|
||||||
return widget.communities.where((community) {
|
|
||||||
final containsQueryInCommunity =
|
|
||||||
community.name.toLowerCase().contains(_searchQuery.toLowerCase());
|
|
||||||
final containsQueryInSpaces = community.spaces.any((space) =>
|
|
||||||
_containsQuery(space: space, query: _searchQuery.toLowerCase()));
|
|
||||||
|
|
||||||
return containsQueryInCommunity || containsQueryInSpaces;
|
|
||||||
}).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _containsQuery({
|
|
||||||
required SpaceModel space,
|
|
||||||
required String query,
|
|
||||||
}) {
|
|
||||||
final matchesSpace = space.name.toLowerCase().contains(query);
|
|
||||||
final matchesChildren = space.children.any(
|
|
||||||
(child) => _containsQuery(space: child, query: query),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (matchesSpace || matchesChildren) {
|
|
||||||
_selectedSpaceUuid = space.uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchesSpace || matchesChildren;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _isSpaceOrChildSelected(SpaceModel space) {
|
bool _isSpaceOrChildSelected(SpaceModel space) {
|
||||||
final isSpaceSelected = _selectedSpaceUuid == space.uuid;
|
final isSpaceSelected = _selectedSpaceUuid == space.uuid;
|
||||||
final anySubSpaceIsSelected = space.children.any(_isSpaceOrChildSelected);
|
final anySubSpaceIsSelected = space.children.any(_isSpaceOrChildSelected);
|
||||||
@ -101,7 +106,10 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final filteredCommunities = _filteredCommunities();
|
final spaceTreeState = context.watch<SpaceTreeBloc>().state;
|
||||||
|
final filteredCommunities = spaceTreeState.searchQuery.isNotEmpty
|
||||||
|
? spaceTreeState.filteredCommunity
|
||||||
|
: spaceTreeState.communityList;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
width: _width,
|
width: _width,
|
||||||
@ -112,7 +120,7 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
children: [
|
children: [
|
||||||
SidebarHeader(onAddCommunity: _onAddCommunity),
|
SidebarHeader(onAddCommunity: _onAddCommunity),
|
||||||
CustomSearchBar(
|
CustomSearchBar(
|
||||||
onSearchChanged: (query) => setState(() => _searchQuery = query),
|
onSearchChanged: _onSearchChanged,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -123,11 +131,20 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
scrollController: _scrollController,
|
scrollController: _scrollController,
|
||||||
onScrollToEnd: () {},
|
onScrollToEnd: () {},
|
||||||
communities: filteredCommunities,
|
communities: filteredCommunities,
|
||||||
itemBuilder: (context, index) => _buildCommunityTile(
|
itemBuilder: (context, index) {
|
||||||
context,
|
if (index == filteredCommunities.length) {
|
||||||
filteredCommunities[index],
|
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
||||||
),
|
if (spaceTreeState.paginationIsLoading) {
|
||||||
),
|
return const Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _buildCommunityTile(context, filteredCommunities[index]);
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -205,9 +222,8 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAddCommunity() => _selectedId?.isNotEmpty ?? true
|
void _onAddCommunity() =>
|
||||||
? _clearSelection()
|
_selectedId?.isNotEmpty ?? true ? _clearSelection() : _showCreateCommunityDialog();
|
||||||
: _showCreateCommunityDialog();
|
|
||||||
|
|
||||||
void _clearSelection() {
|
void _clearSelection() {
|
||||||
setState(() => _selectedId = '');
|
setState(() => _selectedId = '');
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
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:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart';
|
||||||
@ -27,6 +29,8 @@ class SpaceModelPage extends StatelessWidget {
|
|||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else if (state is SpaceModelLoaded) {
|
} else if (state is SpaceModelLoaded) {
|
||||||
final spaceModels = state.spaceModels;
|
final spaceModels = state.spaceModels;
|
||||||
|
context.read<SpaceTreeBloc>().add(ClearCachedData());
|
||||||
|
|
||||||
final allTagValues = _getAllTagValues(spaceModels);
|
final allTagValues = _getAllTagValues(spaceModels);
|
||||||
final allSpaceModelNames = _getAllSpaceModelName(spaceModels);
|
final allSpaceModelNames = _getAllSpaceModelName(spaceModels);
|
||||||
|
|
||||||
|
@ -46,14 +46,14 @@ final class DebouncedBatchControlDevicesService
|
|||||||
final BatchControlDevicesService decoratee;
|
final BatchControlDevicesService decoratee;
|
||||||
final Duration debounceDuration;
|
final Duration debounceDuration;
|
||||||
|
|
||||||
final _pendingRequests = <(List<String> uuids, String code, Object value)>[];
|
|
||||||
var _isProcessing = false;
|
|
||||||
|
|
||||||
DebouncedBatchControlDevicesService({
|
DebouncedBatchControlDevicesService({
|
||||||
required this.decoratee,
|
required this.decoratee,
|
||||||
this.debounceDuration = const Duration(milliseconds: 800),
|
this.debounceDuration = const Duration(milliseconds: 1500),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final _pendingRequests = <(List<String> uuids, String code, Object value)>[];
|
||||||
|
var _isProcessing = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> batchControlDevices({
|
Future<bool> batchControlDevices({
|
||||||
required List<String> uuids,
|
required List<String> uuids,
|
||||||
@ -68,16 +68,26 @@ final class DebouncedBatchControlDevicesService
|
|||||||
|
|
||||||
await Future.delayed(debounceDuration);
|
await Future.delayed(debounceDuration);
|
||||||
|
|
||||||
final lastRequest = _pendingRequests.last;
|
final groupedRequests =
|
||||||
|
<String, (List<String> uuids, String code, Object value)>{};
|
||||||
|
for (final request in _pendingRequests) {
|
||||||
|
final (_, requestCode, requestValue) = request;
|
||||||
|
groupedRequests[requestCode] = request;
|
||||||
|
}
|
||||||
_pendingRequests.clear();
|
_pendingRequests.clear();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final (lastRequestUuids, lastRequestCode, lastRequestValue) = lastRequest;
|
var allSuccessful = true;
|
||||||
return decoratee.batchControlDevices(
|
for (final request in groupedRequests.values) {
|
||||||
|
final (lastRequestUuids, lastRequestCode, lastRequestValue) = request;
|
||||||
|
final success = await decoratee.batchControlDevices(
|
||||||
uuids: lastRequestUuids,
|
uuids: lastRequestUuids,
|
||||||
code: lastRequestCode,
|
code: lastRequestCode,
|
||||||
value: lastRequestValue,
|
value: lastRequestValue,
|
||||||
);
|
);
|
||||||
|
if (!success) allSuccessful = false;
|
||||||
|
}
|
||||||
|
return allSuccessful;
|
||||||
} finally {
|
} finally {
|
||||||
_isProcessing = false;
|
_isProcessing = false;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ final class DebouncedControlDeviceService implements ControlDeviceService {
|
|||||||
|
|
||||||
DebouncedControlDeviceService({
|
DebouncedControlDeviceService({
|
||||||
required this.decoratee,
|
required this.decoratee,
|
||||||
this.debounceDuration = const Duration(milliseconds: 800),
|
this.debounceDuration = const Duration(milliseconds: 1500),
|
||||||
});
|
});
|
||||||
|
|
||||||
final _pendingRequests = <(String deviceUuid, Status status)>[];
|
final _pendingRequests = <(String deviceUuid, Status status)>[];
|
||||||
@ -59,15 +59,24 @@ final class DebouncedControlDeviceService implements ControlDeviceService {
|
|||||||
|
|
||||||
await Future.delayed(debounceDuration);
|
await Future.delayed(debounceDuration);
|
||||||
|
|
||||||
final lastRequest = _pendingRequests.last;
|
final groupedRequests = <String, (String deviceUuid, Status status)>{};
|
||||||
|
for (final request in _pendingRequests) {
|
||||||
|
final (_, requestStatus) = request;
|
||||||
|
groupedRequests[requestStatus.code] = request;
|
||||||
|
}
|
||||||
_pendingRequests.clear();
|
_pendingRequests.clear();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final (lastRequestDeviceUuid, lastRequestStatus) = lastRequest;
|
var allSuccessful = true;
|
||||||
return decoratee.controlDevice(
|
for (final request in groupedRequests.values) {
|
||||||
|
final (lastRequestDeviceUuid, lastRequestStatus) = request;
|
||||||
|
final success = await decoratee.controlDevice(
|
||||||
deviceUuid: lastRequestDeviceUuid,
|
deviceUuid: lastRequestDeviceUuid,
|
||||||
status: lastRequestStatus,
|
status: lastRequestStatus,
|
||||||
);
|
);
|
||||||
|
if (!success) allSuccessful = false;
|
||||||
|
}
|
||||||
|
return allSuccessful;
|
||||||
} finally {
|
} finally {
|
||||||
_isProcessing = false;
|
_isProcessing = false;
|
||||||
}
|
}
|
||||||
|
@ -281,6 +281,7 @@ class CommunitySpaceManagementApi {
|
|||||||
return json['success'] ?? false;
|
return json['success'] ?? false;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Error updating space: $e');
|
debugPrint('Error updating space: $e');
|
||||||
|
Reference in New Issue
Block a user