Compare commits

..

45 Commits

Author SHA1 Message Date
9044645f95 remove screenWidth parameter from TagChipDisplay and use context.screenWidth instead. 2025-04-15 15:45:47 +03:00
7699453e6d moved styling of _buildChip up, and removed unnecessary SizedBox. 2025-04-15 15:43:50 +03:00
d1a21be983 removed else from TagChipDisplay.build 2025-04-15 15:35:24 +03:00
db8e5a4aa6 Refactor TagChipDisplay._groupedTags to enhance readabaility. 2025-04-15 15:32:31 +03:00
fa5bb350c3 refactor: replace spaceNameController with spaceName in TagChipDisplay. 2025-04-15 15:29:25 +03:00
920827d763 Removed unnecessary SizedBox from TagChipDisplay. 2025-04-15 15:28:12 +03:00
d3902d622e Moved constructor to be the first element in TagChipDisplay. 2025-04-15 15:27:45 +03:00
a4432656ab refactor: extract EditChip into a private method for improved readability 2025-04-15 15:27:04 +03:00
90e0d2f52b Extracted Chip into a private method. 2025-04-15 15:24:36 +03:00
08e5e17910 Extracted Add Devices button into a private method. 2025-04-15 15:23:36 +03:00
f57348e5cd converted to using expressions wherever possible in TagChipDisplay. 2025-04-15 15:13:47 +03:00
be168aed93 refactor: simplify tag checking logic to enhance readability. 2025-04-15 15:08:30 +03:00
a66784473f Replaced conditional with an if statement in TagChipDisplay. 2025-04-15 15:06:01 +03:00
c0a963ded5 refactor: use context extension for text theme. 2025-04-15 15:05:31 +03:00
7945cefe53 added trailing commas wherever necessary. 2025-04-15 15:05:06 +03:00
7d0e50fb1d removed unnecessary comments. 2025-04-15 15:03:27 +03:00
117f6190dd removed unnecessary BuildContext from TagChipDisplay constructor, sorted its properies, and converted to using super.key. 2025-04-15 15:02:58 +03:00
748c67fd8b SP-1333 2025-04-15 14:52:20 +03:00
db1f29e2b2 Merged with dev 2025-04-15 11:18:43 +03:00
dba89027e3 Updated the if statement for the tag and the location 2025-04-15 11:09:48 +03:00
4849bb41ba Added function to single card, included tag and location to cards 2025-04-15 02:13:00 +03:00
a7bdbfe3ec Merge pull request #141 from SyncrowIOT/fix-timer-toggle-issue
Refactor AC device controls and toggle widget
2025-04-14 16:06:37 +03:00
db84a9aa5e fix a logic 2025-04-14 16:03:34 +03:00
1023170788 Sort communities in create new routine dropdown. 2025-04-14 11:25:36 +03:00
140f4ff5e2 Refactor AC device controls and toggle widget 2025-04-14 09:57:25 +03:00
cbaeecc968 Merge pull request #139 from SyncrowIOT/SP-1189-FE-Add-Button-Not-clickable-Opening-Pop-up-in-Community-Screen
Sp 1189 fe add button not clickable opening pop up in community screen
2025-04-13 16:22:37 +03:00
2a95720cb0 Merge pull request #140 from SyncrowIOT/SP-1345-FE-SOS-Button-UI-Issue
Refactor SosDeviceControlsView
2025-04-13 16:20:55 +03:00
4fae2d6be0 Refactor SosDeviceControlsView 2025-04-13 16:19:16 +03:00
5b84076572 Merge pull request #138 from SyncrowIOT/SP-1192-FE-Implement-the-schedule-on-the-web-for-the-AC-device
Sp 1192 fe implement the schedule on the web for the ac device
2025-04-13 15:02:02 +03:00
9bf37243a6 remove unused code and make a limitation for the nobody time picker 2025-04-13 14:55:55 +03:00
acad0e8c9c SP-1189-FE-Add-Button-Not-clickable-Opening-Pop-up-in-Community-Screen 2025-04-13 14:50:07 +03:00
79f5ef7871 simplify space selection logic. 2025-04-13 13:12:48 +03:00
6493f02bcc simplify if statements readabaility. 2025-04-13 13:08:23 +03:00
c7c8898763 removed redundant code. 2025-04-13 12:52:38 +03:00
62ee9a72d6 moved SidebarHeader and SidebarAddCommunityButton to their own files. 2025-04-13 12:47:05 +03:00
55695ca5db refactor: improve readability and structure in SidebarWidget's space tile handling 2025-04-13 12:33:50 +03:00
c2f5a8df10 refactor: streamline context usage and improve readability in SidebarWidget 2025-04-13 12:31:17 +03:00
cd9821679e added trailing commas. 2025-04-13 12:29:38 +03:00
bfd3d4542e refactor: simplify onSearchChanged callback to use expressions. 2025-04-13 12:28:50 +03:00
35a99ccda7 removed unnecessary comments from SidebarWidget. 2025-04-13 12:27:04 +03:00
978934399e fixed invalid use of a private type in a public API in SidebarWidget. 2025-04-13 12:25:28 +03:00
bc32fe7941 removed unused method and its use. 2025-04-13 12:25:03 +03:00
cb6d50d367 remove unnecessary print statement 2025-04-13 12:21:35 +03:00
97eb1c152b Implement a countdown timer for the AC and fix bugs in the 'Forgot Password' 2025-04-13 12:18:56 +03:00
c23176706f Merge pull request #136 from SyncrowIOT/SP-1367-FE-Save-Flow-edit-or-delete-the-device-functions
Implementing the Ceiling Sensor in Routine WEB
2025-04-09 13:19:53 +03:00
25 changed files with 1158 additions and 861 deletions

View File

@ -0,0 +1,4 @@
<svg width="8" height="9" viewBox="0 0 8 9" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.13918 0.5H4.10294C4.0408 0.5 3.98117 0.524721 3.93722 0.568668L0.251783 4.25398C-0.0839278 4.58982 -0.0839278 5.13617 0.251783 5.47188L3.02848 8.24852C3.1906 8.41064 3.40686 8.49994 3.63734 8.5H3.6374C3.86794 8.5 4.0842 8.41064 4.24638 8.24846L7.9317 4.56308C7.97565 4.51914 8.00037 4.4595 8.00037 4.39736L8.00043 1.36113C8.00037 0.886312 7.61399 0.5 7.13918 0.5ZM7.53159 4.30031L3.91488 7.91702C3.84127 7.9907 3.74269 8.03122 3.6374 8.03122C3.53205 8.03122 3.43353 7.9907 3.35992 7.91708L0.583222 5.14045C0.43026 4.98748 0.43026 4.73845 0.583222 4.58542L4.19999 0.968775H7.13918C7.35556 0.968775 7.53165 1.14481 7.53165 1.36119L7.53159 4.30031Z" fill="#999999"/>
<path d="M5.93455 1.8291C5.73782 1.8291 5.55288 1.90577 5.41377 2.04487C5.27466 2.18392 5.19806 2.36886 5.19806 2.56559C5.19806 2.76232 5.27466 2.94726 5.41377 3.08637C5.55288 3.22548 5.73782 3.30208 5.93455 3.30208C6.13121 3.30208 6.31616 3.22548 6.45527 3.08637C6.59437 2.94726 6.67098 2.76232 6.67098 2.56559C6.67098 2.36886 6.59437 2.18392 6.45533 2.04487C6.31622 1.90577 6.13128 1.8291 5.93455 1.8291ZM6.12383 2.75487C6.07329 2.80547 6.00602 2.83331 5.93455 2.83331C5.86301 2.83331 5.79581 2.80547 5.74527 2.75487C5.69467 2.70433 5.66683 2.63707 5.66683 2.56559C5.66683 2.49412 5.69467 2.42685 5.74527 2.37631C5.79581 2.32571 5.86307 2.29788 5.93455 2.29788C6.00602 2.29788 6.07323 2.32571 6.12383 2.37631C6.17443 2.42685 6.20226 2.49412 6.20226 2.56559C6.20226 2.63707 6.17437 2.70433 6.12383 2.75487Z" fill="#999999"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -201,20 +201,17 @@ class ForgetPasswordWebPage extends StatelessWidget {
!state.isButtonEnabled &&
state.remainingTime != 1
? null
: () {
:
() {
if (forgetBloc
.forgetEmailKey.currentState!
.validate() ||
forgetBloc
.forgetRegionKey.currentState!
.validate()) {
if (forgetBloc
.forgetRegionKey.currentState!
.validate()) {
forgetBloc.add(StartTimerEvent());
}
.forgetEmailKey
.currentState!
.validate()) {
forgetBloc.add(
StartTimerEvent());
}
},
child: Text(
'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}',
style: TextStyle(

View File

@ -13,6 +13,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
late AcStatusModel deviceStatus;
final String deviceId;
Timer? _timer;
Timer? _countdownTimer;
AcBloc({required this.deviceId}) : super(AcsInitialState()) {
on<AcFetchDeviceStatusEvent>(_onFetchAcStatus);
@ -21,7 +22,16 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
on<AcBatchControlEvent>(_onAcBatchControl);
on<AcFactoryResetEvent>(_onFactoryReset);
on<AcStatusUpdated>(_onAcStatusUpdated);
on<OnClose>(_onClose);
on<IncreaseTimeEvent>(_handleIncreaseTime);
on<DecreaseTimeEvent>(_handleDecreaseTime);
on<UpdateTimerEvent>(_handleUpdateTimer);
on<ToggleScheduleEvent>(_handleToggleTimer);
on<ApiCountdownValueEvent>(_handleApiCountdownValue);
}
bool timerActive = false;
int scheduledHours = 0;
int scheduledMinutes = 0;
FutureOr<void> _onFetchAcStatus(
AcFetchDeviceStatusEvent event, Emitter<AcsState> emit) async {
@ -30,8 +40,23 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status);
if (deviceStatus.countdown1 != 0) {
// Convert API value to minutes
final totalMinutes = deviceStatus.countdown1 * 6;
scheduledHours = totalMinutes ~/ 60;
scheduledMinutes = totalMinutes % 60;
timerActive = true;
_startCountdownTimer(emit);
}
emit(ACStatusLoaded(
status: deviceStatus,
scheduledHours: scheduledHours,
scheduledMinutes: scheduledMinutes,
isTimerActive: timerActive,
));
_listenToChanges(event.deviceId);
emit(ACStatusLoaded(deviceStatus));
} catch (e) {
emit(AcsFailedState(error: e.toString()));
}
@ -70,31 +95,16 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
void _onAcStatusUpdated(AcStatusUpdated event, Emitter<AcsState> emit) {
deviceStatus = event.deviceStatus;
emit(ACStatusLoaded(deviceStatus));
emit(ACStatusLoaded(status: deviceStatus));
}
// Future<void> testFirebaseConnection() async {
// // Reference to a test node in your database
// final testRef = FirebaseDatabase.instance.ref("test");
// // Write a test value
// await testRef.set("Hello, Firebase!");
// // Listen for changes on the test node
// testRef.onValue.listen((DatabaseEvent event) {
// final data = event.snapshot.value;
// print("Data from Firebase: $data");
// // If you see "Hello, Firebase!" printed in your console, it means the connection works.
// });
// }
FutureOr<void> _onAcControl(
AcControlEvent event, Emitter<AcsState> emit) async {
final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value, emit);
emit(ACStatusLoaded(deviceStatus));
emit(ACStatusLoaded(status: deviceStatus));
await _runDebounce(
isBatch: false,
@ -151,7 +161,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
void _revertValueAndEmit(
String deviceId, String code, dynamic oldValue, Emitter<AcsState> emit) {
_updateLocalValue(code, oldValue, emit);
emit(ACStatusLoaded(deviceStatus));
emit(ACStatusLoaded(status: deviceStatus));
}
void _updateLocalValue(String code, dynamic value, Emitter<AcsState> emit) {
@ -184,11 +194,16 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
if (value is bool) {
deviceStatus = deviceStatus.copyWith(childLock: value);
}
case 'countdown_time':
if (value is int) {
deviceStatus = deviceStatus.copyWith(countdown1: value);
}
break;
default:
break;
}
emit(ACStatusLoaded(deviceStatus));
emit(ACStatusLoaded(status: deviceStatus));
}
dynamic _getValueByCode(String code) {
@ -203,6 +218,8 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
return deviceStatus.fanSpeedsString;
case 'child_lock':
return deviceStatus.childLock;
case 'countdown_time':
return deviceStatus.countdown1;
default:
return null;
}
@ -216,7 +233,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus =
AcStatusModel.fromJson(event.devicesIds.first, status.status);
emit(ACStatusLoaded(deviceStatus));
emit(ACStatusLoaded(status: deviceStatus));
} catch (e) {
emit(AcsFailedState(error: e.toString()));
}
@ -228,7 +245,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
_updateLocalValue(event.code, event.value, emit);
emit(ACStatusLoaded(deviceStatus));
emit(ACStatusLoaded(status: deviceStatus));
await _runDebounce(
isBatch: true,
@ -257,4 +274,144 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
emit(AcsFailedState(error: e.toString()));
}
}
void _onClose(OnClose event, Emitter<AcsState> emit) {
_countdownTimer?.cancel();
_timer?.cancel();
}
void _handleIncreaseTime(IncreaseTimeEvent event, Emitter<AcsState> emit) {
if (state is! ACStatusLoaded) return;
final currentState = state as ACStatusLoaded;
int newHours = scheduledHours;
int newMinutes = scheduledMinutes + 30;
newHours += newMinutes ~/ 60;
newMinutes = newMinutes % 60;
if (newHours > 23) {
newHours = 23;
newMinutes = 59;
}
scheduledHours = newHours;
scheduledMinutes = newMinutes;
emit(currentState.copyWith(
scheduledHours: scheduledHours,
scheduledMinutes: scheduledMinutes,
));
}
void _handleDecreaseTime(DecreaseTimeEvent event, Emitter<AcsState> emit) {
if (state is! ACStatusLoaded) return;
final currentState = state as ACStatusLoaded;
int totalMinutes = (scheduledHours * 60) + scheduledMinutes;
totalMinutes = (totalMinutes - 30).clamp(0, 1440);
scheduledHours = totalMinutes ~/ 60;
scheduledMinutes = totalMinutes % 60;
emit(currentState.copyWith(
scheduledHours: scheduledHours,
scheduledMinutes: scheduledMinutes,
));
}
Future<void> _handleToggleTimer(
ToggleScheduleEvent event, Emitter<AcsState> emit) async {
if (state is! ACStatusLoaded) return;
final currentState = state as ACStatusLoaded;
timerActive = !timerActive;
if (timerActive) {
final totalMinutes = scheduledHours * 60 + scheduledMinutes;
if (totalMinutes <= 0) {
timerActive = false;
emit(currentState.copyWith(isTimerActive: timerActive));
return;
}
try {
final scaledValue = totalMinutes ~/ 6;
await _runDebounce(
isBatch: false,
deviceId: deviceId,
code: 'countdown_time',
value: scaledValue,
oldValue: scaledValue,
emit: emit,
);
_startCountdownTimer(emit);
emit(currentState.copyWith(isTimerActive: timerActive));
} catch (e) {
timerActive = false;
emit(AcsFailedState(error: e.toString()));
}
} else {
await _runDebounce(
isBatch: false,
deviceId: deviceId,
code: 'countdown_time',
value: 0,
oldValue: 0,
emit: emit,
);
_countdownTimer?.cancel();
scheduledHours = 0;
scheduledMinutes = 0;
emit(currentState.copyWith(
isTimerActive: timerActive,
scheduledHours: 0,
scheduledMinutes: 0,
));
}
}
void _startCountdownTimer(Emitter<AcsState> emit) {
_countdownTimer?.cancel();
int totalSeconds = (scheduledHours * 3600) + (scheduledMinutes * 60);
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (totalSeconds > 0) {
totalSeconds--;
scheduledHours = totalSeconds ~/ 3600;
scheduledMinutes = (totalSeconds % 3600) ~/ 60;
add(UpdateTimerEvent());
} else {
_countdownTimer?.cancel();
timerActive = false;
scheduledHours = 0;
scheduledMinutes = 0;
add(TimerCompletedEvent());
}
});
}
void _handleUpdateTimer(UpdateTimerEvent event, Emitter<AcsState> emit) {
if (state is ACStatusLoaded) {
final currentState = state as ACStatusLoaded;
emit(currentState.copyWith(
scheduledHours: scheduledHours,
scheduledMinutes: scheduledMinutes,
isTimerActive: timerActive,
));
}
}
void _handleApiCountdownValue(
ApiCountdownValueEvent event, Emitter<AcsState> emit) {
if (state is ACStatusLoaded) {
final totalMinutes = event.apiValue * 6;
final scheduledHours = totalMinutes ~/ 60;
scheduledMinutes = totalMinutes % 60;
_startCountdownTimer(
emit,
);
add(UpdateTimerEvent());
}
}
@override
Future<void> close() {
add(OnClose());
return super.close();
}
}

View File

@ -8,6 +8,7 @@ sealed class AcsEvent extends Equatable {
@override
List<Object> get props => [];
}
class AcUpdated extends AcsEvent {}
class AcFetchDeviceStatusEvent extends AcsEvent {
@ -18,10 +19,12 @@ class AcFetchDeviceStatusEvent extends AcsEvent {
@override
List<Object> get props => [deviceId];
}
class AcStatusUpdated extends AcsEvent {
final AcStatusModel deviceStatus;
AcStatusUpdated(this.deviceStatus);
}
class AcFetchBatchStatusEvent extends AcsEvent {
final List<String> devicesIds;
@ -73,3 +76,30 @@ class AcFactoryResetEvent extends AcsEvent {
@override
List<Object> get props => [deviceId, factoryResetModel];
}
class OnClose extends AcsEvent {}
class IncreaseTimeEvent extends AcsEvent {
@override
List<Object> get props => [];
}
class DecreaseTimeEvent extends AcsEvent {
@override
List<Object> get props => [];
}
class ToggleScheduleEvent extends AcsEvent {}
class TimerCompletedEvent extends AcsEvent {}
class UpdateTimerEvent extends AcsEvent {
}
class ApiCountdownValueEvent extends AcsEvent {
final int apiValue;
const ApiCountdownValueEvent(this.apiValue);
}

View File

@ -2,8 +2,9 @@ import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
abstract class AcsState extends Equatable {
const AcsState();
final bool isTimerActive;
const AcsState({this.isTimerActive = false});
@override
List<Object> get props => [];
}
@ -15,8 +16,30 @@ class AcsLoadingState extends AcsState {}
class ACStatusLoaded extends AcsState {
final AcStatusModel status;
final DateTime timestamp;
final int scheduledHours;
final int scheduledMinutes;
final bool isTimerActive;
ACStatusLoaded(this.status) : timestamp = DateTime.now();
ACStatusLoaded({
required this.status,
this.scheduledHours = 0,
this.scheduledMinutes = 0,
this.isTimerActive = false,
}) : timestamp = DateTime.now();
ACStatusLoaded copyWith({
AcStatusModel? status,
int? scheduledHours,
int? scheduledMinutes,
bool? isTimerActive,
int? remainingTime,
}) {
return ACStatusLoaded(
status: status ?? this.status,
scheduledHours: scheduledHours ?? this.scheduledHours,
scheduledMinutes: scheduledMinutes ?? this.scheduledMinutes,
isTimerActive: isTimerActive ?? this.isTimerActive,
);
}
@override
List<Object> get props => [status, timestamp];
@ -40,3 +63,14 @@ class AcsFailedState extends AcsState {
@override
List<Object> get props => [error];
}
class TimerRunInProgress extends AcsState {
final int remainingTime;
const TimerRunInProgress(this.remainingTime);
@override
List<Object> get props => [remainingTime];
}

View File

@ -11,6 +11,7 @@ class AcStatusModel {
final bool childLock;
final TempModes acMode;
final FanSpeeds acFanSpeed;
final int countdown1;
AcStatusModel({
required this.uuid,
@ -18,6 +19,7 @@ class AcStatusModel {
required this.modeString,
required this.tempSet,
required this.currentTemp,
required this.countdown1,
required this.fanSpeedsString,
required this.childLock,
}) : acMode = getACMode(modeString),
@ -30,6 +32,7 @@ class AcStatusModel {
late int currentTemp;
late String fanSpeeds;
late bool childLock;
late int _countdown1 = 0;
for (var status in jsonList) {
switch (status.code) {
@ -51,6 +54,9 @@ class AcStatusModel {
case 'child_lock':
childLock = status.value ?? false;
break;
case 'countdown_time':
_countdown1 = status.value ?? 0;
break;
}
}
@ -62,6 +68,7 @@ class AcStatusModel {
currentTemp: currentTemp,
fanSpeedsString: fanSpeeds,
childLock: childLock,
countdown1: _countdown1,
);
}
@ -73,6 +80,7 @@ class AcStatusModel {
int? currentTemp,
String? fanSpeedsString,
bool? childLock,
int? countdown1,
}) {
return AcStatusModel(
uuid: uuid ?? this.uuid,
@ -82,6 +90,7 @@ class AcStatusModel {
currentTemp: currentTemp ?? this.currentTemp,
fanSpeedsString: fanSpeedsString ?? this.fanSpeedsString,
childLock: childLock ?? this.childLock,
countdown1: countdown1 ?? this.countdown1,
);
}

View File

@ -10,11 +10,10 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
const AcDeviceControlsView({super.key, required this.device});
const AcDeviceControlsView({super.key, required this.device});
final AllDevicesModel device;
@ -23,11 +22,13 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
final isExtraLarge = isExtraLargeScreenSize(context);
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
return BlocProvider(
create: (context) => AcBloc(deviceId: device.uuid!)
..add(AcFetchDeviceStatusEvent(device.uuid!)),
child: BlocBuilder<AcBloc, AcsState>(
builder: (context, state) {
final acBloc = BlocProvider.of<AcBloc>(context);
if (state is ACStatusLoaded) {
return GridView(
padding: const EdgeInsets.symmetric(horizontal: 50),
@ -78,56 +79,101 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
),
ToggleWidget(
label: '',
labelWidget: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
labelWidget: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
padding: const EdgeInsets.all(0),
onPressed: () {},
icon: const Icon(
Icons.remove,
size: 28,
color: ColorsManager.greyColor,
Container(
width: MediaQuery.of(context).size.width,
decoration: const ShapeDecoration(
color: ColorsManager.primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(30)),
),
),
),
Text(
'06',
style: context.textTheme.titleLarge!.copyWith(
color: ColorsManager.dialogBlueTitle,
fontWeight: FontWeight.bold,
),
),
Text(
'h',
style: context.textTheme.bodySmall!
.copyWith(color: ColorsManager.blackColor),
),
Text(
'30',
style: context.textTheme.titleLarge!.copyWith(
color: ColorsManager.dialogBlueTitle,
fontWeight: FontWeight.bold,
),
),
Text('m',
style: context.textTheme.bodySmall!
.copyWith(color: ColorsManager.blackColor)),
IconButton(
padding: const EdgeInsets.all(0),
onPressed: () {},
icon: const Icon(
Icons.add,
size: 28,
color: ColorsManager.greyColor,
Center(
child: SizedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
onPressed: () {
if (acBloc.timerActive == false) {
context
.read<AcBloc>()
.add(DecreaseTimeEvent());
}
},
icon: const Icon(Icons.remove,
color: ColorsManager.greyColor),
),
Text(
acBloc.scheduledHours
.toString()
.padLeft(2, '0'),
style: Theme.of(context)
.textTheme
.titleLarge!
.copyWith(
color: ColorsManager.dialogBlueTitle,
fontWeight: FontWeight.bold,
),
),
Text(
'h',
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: ColorsManager.blackColor,
),
),
Text(
acBloc.scheduledMinutes
.toString()
.padLeft(2, '0'),
style: Theme.of(context)
.textTheme
.titleLarge!
.copyWith(
color: ColorsManager.dialogBlueTitle,
fontWeight: FontWeight.bold,
),
),
Text(
'm',
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: ColorsManager.blackColor,
),
),
IconButton(
onPressed: () {
if (acBloc.timerActive == false) {
context
.read<AcBloc>()
.add(IncreaseTimeEvent());
}
},
icon: const Icon(Icons.add,
color: ColorsManager.greyColor),
),
],
),
),
),
],
),
value: false,
value: acBloc.timerActive,
code: 'ac_schedule',
deviceId: device.uuid!,
icon: Assets.acSchedule,
onChange: (value) {},
onChange: (value) {
context.read<AcBloc>().add(ToggleScheduleEvent());
},
),
ToggleWidget(
deviceId: device.uuid!,

View File

@ -0,0 +1,18 @@
class DeviceSubSpace {
String? id;
String? createdAt;
String? updatedAt;
String? subspaceName;
bool? disabled;
DeviceSubSpace({this.id, this.createdAt, this.updatedAt, this.subspaceName, this.disabled});
DeviceSubSpace.fromJson(Map<String, dynamic> json) {
id = json['uuid']?.toString() ?? '';
createdAt = json['createdAt']?.toString() ?? '';
updatedAt = json['updatedAt']?.toString() ?? '';
subspaceName = json['subspaceName']?.toString() ?? '';
subspaceName = json['subspaceName']?.toString() ?? '';
disabled = json['disabled'] ?? false;
}
}

View File

@ -0,0 +1,20 @@
class DeviceTagModel {
String? id;
String? createdAt;
String? updatedAt;
String? name;
DeviceTagModel({
this.id,
this.createdAt,
this.updatedAt,
this.name,
});
DeviceTagModel.fromJson(Map<String, dynamic> json) {
id = json['uuid']?.toString() ?? '';
createdAt = json['createdAt']?.toString() ?? '';
updatedAt = json['updatedAt']?.toString() ?? '';
name = json['name']?.toString() ?? '';
}
}

View File

@ -1,6 +1,8 @@
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_community.model.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_space_model.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_sub_space.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_subspace.model.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_tag_model.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart';
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
@ -78,38 +80,41 @@ class AllDevicesModel {
int? batteryLevel;
String? productName;
List<DeviceSpaceModel>? spaces;
List<DeviceTagModel>? deviceTags;
DeviceSubSpace? deviceSubSpace;
AllDevicesModel({
this.room,
this.subspace,
this.unit,
this.community,
this.productUuid,
this.productType,
this.permissionType,
this.activeTime,
this.category,
this.categoryName,
this.createTime,
this.gatewayId,
this.icon,
this.ip,
this.lat,
this.localKey,
this.lon,
this.model,
this.name,
this.nodeId,
this.online,
this.ownerId,
this.sub,
this.timeZone,
this.updateTime,
this.uuid,
this.batteryLevel,
this.productName,
this.spaces,
});
AllDevicesModel(
{this.room,
this.subspace,
this.unit,
this.community,
this.productUuid,
this.productType,
this.permissionType,
this.activeTime,
this.category,
this.categoryName,
this.createTime,
this.gatewayId,
this.icon,
this.ip,
this.lat,
this.localKey,
this.lon,
this.model,
this.name,
this.nodeId,
this.online,
this.ownerId,
this.sub,
this.timeZone,
this.updateTime,
this.uuid,
this.batteryLevel,
this.productName,
this.spaces,
this.deviceTags,
this.deviceSubSpace});
AllDevicesModel.fromJson(Map<String, dynamic> json) {
room = (json['room'] != null && (json['room'] is Map))
@ -147,12 +152,15 @@ class AllDevicesModel {
updateTime = int.tryParse(json['updateTime']?.toString() ?? '');
uuid = json['uuid']?.toString();
batteryLevel = int.tryParse(json['battery']?.toString() ?? '');
productName = json['productName']?.toString();
deviceTags = json['deviceTag'] != null && json['deviceTag'] is List
? (json['deviceTag'] as List).map((tag) => DeviceTagModel.fromJson(tag)).toList()
: [];
deviceSubSpace = json['subspace'] != null
? DeviceSubSpace.fromJson(json['subspace'])
: DeviceSubSpace(subspaceName: '');
if (json['spaces'] != null && json['spaces'] is List) {
spaces = (json['spaces'] as List)
.map((space) => DeviceSpaceModel.fromJson(space))
.toList();
spaces = (json['spaces'] as List).map((space) => DeviceSpaceModel.fromJson(space)).toList();
}
}
@ -200,8 +208,7 @@ SOS
String tempIcon = '';
if (type == DeviceType.LightBulb) {
tempIcon = Assets.lightBulb;
} else if (type == DeviceType.CeilingSensor ||
type == DeviceType.WallSensor) {
} else if (type == DeviceType.CeilingSensor || type == DeviceType.WallSensor) {
tempIcon = Assets.sensors;
} else if (type == DeviceType.AC) {
tempIcon = Assets.ac;
@ -248,76 +255,51 @@ SOS
switch (productType) {
case 'AC':
return [
SwitchFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ModeFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
TempSetFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
CurrentTempFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
LevelFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ChildLockFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
SwitchFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ModeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
TempSetFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
CurrentTempFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
LevelFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ChildLockFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
];
case '1G':
return [
OneGangSwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
OneGangCountdownFunction(
deviceId: uuid ?? '', deviceName: name ?? ''),
OneGangCountdownFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
];
case '2G':
return [
TwoGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown1Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown2Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
];
case '3G':
return [
ThreeGangSwitch1Function(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangSwitch2Function(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangSwitch3Function(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangCountdown1Function(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangCountdown2Function(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangCountdown3Function(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangSwitch3Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangCountdown3Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
];
case 'WPS':
return [
//IF Functions
PresenceStateFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
CurrentDistanceFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
IlluminanceValueFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
PresenceTimeFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
PresenceStateFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
CurrentDistanceFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
IlluminanceValueFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
PresenceTimeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
//THEN Functions
FarDetectionFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
MotionSensitivityFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
MotionLessSensitivityFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
IndicatorFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
NoOneTimeFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
FarDetectionFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
MotionSensitivityFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
MotionLessSensitivityFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
IndicatorFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
NoOneTimeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
];
case 'GW':
return [

View File

@ -62,9 +62,6 @@ class ToggleWidget extends StatelessWidget {
)),
if (showToggle)
Container(
height: 20,
width: 35,
padding: const EdgeInsets.only(right: 16, top: 10),
child: CupertinoSwitch(
value: value,
activeColor: ColorsManager.dialogBlueTitle,

View File

@ -13,7 +13,8 @@ import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_la
import '../models/sos_status_model.dart';
class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
class SosDeviceControlsView extends StatelessWidget
with HelperResponsiveLayout {
const SosDeviceControlsView({
super.key,
required this.device,
@ -24,7 +25,8 @@ class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => SosDeviceBloc()..add(GetDeviceStatus(device.uuid!)),
create: (context) =>
SosDeviceBloc()..add(GetDeviceStatus(device.uuid!)),
child: BlocBuilder<SosDeviceBloc, SosDeviceState>(
builder: (context, state) {
if (state is SosDeviceLoadingState) {
@ -63,7 +65,8 @@ class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout
));
}
Widget _buildStatusControls(BuildContext context, SosStatusModel deviceStatus) {
Widget _buildStatusControls(
BuildContext context, SosStatusModel deviceStatus) {
final isExtraLarge = isExtraLargeScreenSize(context);
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
@ -85,7 +88,7 @@ class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout
IconNameStatusContainer(
isFullIcon: false,
name: deviceStatus.sosStatus == 'sos' ? 'SOS' : 'Normal',
icon: deviceStatus.sosStatus == 'sos' ? Assets.sos : Assets.sosNormal,
icon: deviceStatus.sosStatus == 'sos' ? Assets.sosNormal : Assets.sos,
onTap: () {},
status: false,
textColor: ColorsManager.blackColor,

View File

@ -30,6 +30,7 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
condition: event.functionData.condition ?? existingData.condition,
);
} else {
functions.clear();
functions.add(event.functionData);
}

View File

@ -64,8 +64,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
TriggerSwitchTabsEvent event,
Emitter<RoutineState> emit,
) {
emit(state.copyWith(
routineTab: event.isRoutineTab, createRoutineView: false));
emit(state.copyWith(routineTab: event.isRoutineTab, createRoutineView: false));
add(ResetRoutineState());
if (event.isRoutineTab) {
add(const LoadScenes());
@ -91,8 +90,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
// Find the index of the item in teh current itemsList
int index = updatedIfItems.indexWhere(
(map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
int index =
updatedIfItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
// Replace the map if the index is valid
if (index != -1) {
updatedIfItems[index] = event.item;
@ -101,21 +100,18 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
if (event.isTabToRun) {
emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
} else {
emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
}
}
void _onAddToThenContainer(
AddToThenContainer event, Emitter<RoutineState> emit) {
void _onAddToThenContainer(AddToThenContainer event, Emitter<RoutineState> emit) {
final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
// Find the index of the item in teh current itemsList
int index = currentItems.indexWhere(
(map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
int index =
currentItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
// Replace the map if the index is valid
if (index != -1) {
currentItems[index] = event.item;
@ -126,45 +122,42 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(thenItems: currentItems));
}
void _onAddFunctionsToRoutine(
AddFunctionToRoutine event, Emitter<RoutineState> emit) {
void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter<RoutineState> emit) {
try {
if (event.functions.isEmpty) return;
List<DeviceFunctionData> selectedFunction =
List<DeviceFunctionData>.from(event.functions);
// List<DeviceFunctionData> selectedFunction = List<DeviceFunctionData>.from(event.functions);
Map<String, List<DeviceFunctionData>> currentSelectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
List<DeviceFunctionData> currentFunctions =
List<DeviceFunctionData>.from(
currentSelectedFunctions[event.uniqueCustomId] ?? []);
List<String> functionCode = [];
for (int i = 0; i < selectedFunction.length; i++) {
for (int j = 0; j < currentFunctions.length; j++) {
if (selectedFunction[i].functionCode ==
currentFunctions[j].functionCode) {
currentFunctions[j] = selectedFunction[i];
if (!functionCode.contains(currentFunctions[j].functionCode)) {
functionCode.add(currentFunctions[j].functionCode);
}
}
}
}
// if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
// List<DeviceFunctionData> currentFunctions =
// List<DeviceFunctionData>.from(currentSelectedFunctions[event.uniqueCustomId] ?? []);
for (int i = 0; i < functionCode.length; i++) {
selectedFunction
.removeWhere((code) => code.functionCode == functionCode[i]);
}
// List<String> functionCode = [];
// for (int i = 0; i < selectedFunction.length; i++) {
// for (int j = 0; j < currentFunctions.length; j++) {
// if (selectedFunction[i].functionCode == currentFunctions[j].functionCode) {
// currentFunctions[j] = selectedFunction[i];
// if (!functionCode.contains(currentFunctions[j].functionCode)) {
// functionCode.add(currentFunctions[j].functionCode);
// }
// }
// }
// }
currentSelectedFunctions[event.uniqueCustomId] =
List.from(currentFunctions)..addAll(selectedFunction);
} else {
currentSelectedFunctions[event.uniqueCustomId] =
List.from(event.functions);
}
// for (int i = 0; i < functionCode.length; i++) {
// selectedFunction.removeWhere((code) => code.functionCode == functionCode[i]);
// }
// currentSelectedFunctions[event.uniqueCustomId] = List.from(currentFunctions)
// ..addAll(selectedFunction);
// } else {
// currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions);
// }
currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions);
emit(state.copyWith(selectedFunctions: currentSelectedFunctions));
} catch (e) {
@ -172,30 +165,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {
Future<void> _onLoadScenes(LoadScenes event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> scenes = [];
try {
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
var spaceBloc = context.read<SpaceTreeBloc>();
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
scenes.addAll(
await SceneApi.getScenes(spaceId, communityId, projectUuid));
scenes.addAll(await SceneApi.getScenes(spaceId, communityId, projectUuid));
}
}
} else {
scenes.addAll(await SceneApi.getScenes(
createRoutineBloc.selectedSpaceId,
createRoutineBloc.selectedCommunityId,
projectUuid));
createRoutineBloc.selectedSpaceId, createRoutineBloc.selectedCommunityId, projectUuid));
}
emit(state.copyWith(
@ -212,8 +199,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onLoadAutomation(
LoadAutomation event, Emitter<RoutineState> emit) async {
Future<void> _onLoadAutomation(LoadAutomation event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> automations = [];
final projectId = await ProjectManager.getProjectUUID() ?? '';
@ -221,23 +207,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
try {
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
var spaceBloc = context.read<SpaceTreeBloc>();
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
automations.addAll(
await SceneApi.getAutomation(spaceId, communityId, projectId));
automations.addAll(await SceneApi.getAutomation(spaceId, communityId, projectId));
}
}
} else {
automations.addAll(await SceneApi.getAutomation(
createRoutineBloc.selectedSpaceId,
createRoutineBloc.selectedCommunityId,
projectId));
createRoutineBloc.selectedSpaceId, createRoutineBloc.selectedCommunityId, projectId));
}
emit(state.copyWith(
automations: automations,
@ -253,16 +233,14 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
FutureOr<void> _onSearchRoutines(
SearchRoutines event, Emitter<RoutineState> emit) async {
FutureOr<void> _onSearchRoutines(SearchRoutines event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
await Future.delayed(const Duration(seconds: 1));
emit(state.copyWith(isLoading: false, errorMessage: null));
emit(state.copyWith(searchText: event.query));
}
FutureOr<void> _onAddSelectedIcon(
AddSelectedIcon event, Emitter<RoutineState> emit) {
FutureOr<void> _onAddSelectedIcon(AddSelectedIcon event, Emitter<RoutineState> emit) {
emit(state.copyWith(selectedIcon: event.icon));
}
@ -276,8 +254,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
return actions.last['deviceId'] == 'delay';
}
Future<void> _onCreateScene(
CreateSceneEvent event, Emitter<RoutineState> emit) async {
Future<void> _onCreateScene(CreateSceneEvent event, Emitter<RoutineState> emit) async {
try {
// Check if first action is delay
// if (_isFirstActionDelay(state.thenItems)) {
@ -290,8 +267,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (_isLastActionDelay(state.thenItems)) {
emit(state.copyWith(
errorMessage:
'A delay condition cannot be the only or the last action',
errorMessage: 'A delay condition cannot be the only or the last action',
isLoading: false,
));
return;
@ -367,8 +343,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onCreateAutomation(
CreateAutomationEvent event, Emitter<RoutineState> emit) async {
Future<void> _onCreateAutomation(CreateAutomationEvent event, Emitter<RoutineState> emit) async {
try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (state.routineName == null || state.routineName!.isEmpty) {
@ -390,8 +365,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (_isLastActionDelay(state.thenItems)) {
emit(state.copyWith(
errorMessage:
'A delay condition cannot be the only or the last action',
errorMessage: 'A delay condition cannot be the only or the last action',
isLoading: false,
));
CustomSnackBar.redSnackBar('Cannot have delay as the last action');
@ -482,8 +456,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
actions: actions,
);
final result =
await SceneApi.createAutomation(createAutomationModel, projectUuid);
final result = await SceneApi.createAutomation(createAutomationModel, projectUuid);
if (result['success']) {
add(ResetRoutineState());
add(const LoadAutomation());
@ -504,21 +477,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
FutureOr<void> _onRemoveDragCard(
RemoveDragCard event, Emitter<RoutineState> emit) {
FutureOr<void> _onRemoveDragCard(RemoveDragCard event, Emitter<RoutineState> emit) {
if (event.isFromThen) {
final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
final selectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
thenItems.removeAt(event.index);
selectedFunctions.remove(event.key);
emit(state.copyWith(
thenItems: thenItems, selectedFunctions: selectedFunctions));
emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions));
} else {
final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
final selectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
ifItems.removeAt(event.index);
selectedFunctions.remove(event.key);
@ -529,8 +498,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
isAutomation: false,
isTabToRun: false));
} else {
emit(state.copyWith(
ifItems: ifItems, selectedFunctions: selectedFunctions));
emit(state.copyWith(ifItems: ifItems, selectedFunctions: selectedFunctions));
}
}
}
@ -542,141 +510,138 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
));
}
FutureOr<void> _onEffectiveTimeEvent(
EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
FutureOr<void> _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
emit(state.copyWith(effectiveTime: event.effectiveTime));
}
FutureOr<void> _onSetRoutineName(
SetRoutineName event, Emitter<RoutineState> emit) {
FutureOr<void> _onSetRoutineName(SetRoutineName event, Emitter<RoutineState> emit) {
emit(state.copyWith(
routineName: event.name,
));
}
(
List<Map<String, dynamic>>,
List<Map<String, dynamic>>,
Map<String, List<DeviceFunctionData>>
) _createCardData(
List<RoutineAction> actions,
List<RoutineCondition>? conditions,
Map<String, List<DeviceFunctionData>> currentFunctions,
bool isTabToRun,
) {
final ifItems = isTabToRun
? [
{
'entityId': 'tab_to_run',
'uniqueCustomId': const Uuid().v4(),
'deviceId': 'tab_to_run',
'title': 'Tab to run',
'productType': 'tab_to_run',
'imagePath': Assets.tabToRun,
}
]
: conditions?.map((condition) {
final matchingDevice = state.devices.firstWhere(
(device) => device.uuid == condition.entityId,
orElse: () => AllDevicesModel(
uuid: condition.entityId,
name: condition.entityId,
productType: condition.entityType,
),
);
// (
// List<Map<String, dynamic>>,
// List<Map<String, dynamic>>,
// Map<String, List<DeviceFunctionData>>
// ) _createCardData(
// List<RoutineAction> actions,
// List<RoutineCondition>? conditions,
// Map<String, List<DeviceFunctionData>> currentFunctions,
// bool isTabToRun,
// ) {
// final ifItems = isTabToRun
// ? [
// {
// 'entityId': 'tab_to_run',
// 'uniqueCustomId': const Uuid().v4(),
// 'deviceId': 'tab_to_run',
// 'title': 'Tab to run',
// 'productType': 'tab_to_run',
// 'imagePath': Assets.tabToRun,
// }
// ]
// : conditions?.map((condition) {
// final matchingDevice = state.devices.firstWhere(
// (device) => device.uuid == condition.entityId,
// orElse: () => AllDevicesModel(
// uuid: condition.entityId,
// name: condition.entityId,
// productType: condition.entityType,
// ),
// );
final cardData = {
'entityId': condition.entityId,
'uniqueCustomId': const Uuid().v4(),
'deviceId': condition.entityId,
'title': matchingDevice.name ?? condition.entityId,
'productType': condition.entityType,
'imagePath':
matchingDevice.getDefaultIcon(condition.entityType),
};
// final cardData = {
// 'entityId': condition.entityId,
// 'uniqueCustomId': const Uuid().v4(),
// 'deviceId': condition.entityId,
// 'title': matchingDevice.name ?? condition.entityId,
// 'productType': condition.entityType,
// 'imagePath':
// matchingDevice.getDefaultIcon(condition.entityType),
// };
final functions = matchingDevice.functions;
// final functions = matchingDevice.functions;
for (var function in functions) {
if (function.code == condition.expr.statusCode) {
currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
DeviceFunctionData(
entityId: condition.entityId,
functionCode: condition.expr.statusCode,
value: condition.expr.statusValue,
operationName: function.operationName,
),
];
break;
}
}
// for (var function in functions) {
// if (function.code == condition.expr.statusCode) {
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
// DeviceFunctionData(
// entityId: condition.entityId,
// functionCode: condition.expr.statusCode,
// value: condition.expr.statusValue,
// operationName: function.operationName,
// ),
// ];
// break;
// }
// }
return cardData;
}).toList() ??
[];
// return cardData;
// }).toList() ??
// [];
final thenItems = actions.map((action) {
final matchingDevice = state.devices.firstWhere(
(device) => device.uuid == action.entityId,
orElse: () => AllDevicesModel(
uuid: action.entityId,
name: action.entityId,
productType: action.productType,
),
);
// final thenItems = actions.map((action) {
// final matchingDevice = state.devices.firstWhere(
// (device) => device.uuid == action.entityId,
// orElse: () => AllDevicesModel(
// uuid: action.entityId,
// name: action.entityId,
// productType: action.productType,
// ),
// );
final cardData = {
'entityId': action.entityId,
'uniqueCustomId': const Uuid().v4(),
'deviceId':
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'title': action.actionExecutor == 'delay'
? 'Delay'
: (matchingDevice.name ?? 'Device'),
'productType': action.productType,
'imagePath': matchingDevice.getDefaultIcon(action.productType),
};
// final cardData = {
// 'entityId': action.entityId,
// 'uniqueCustomId': const Uuid().v4(),
// 'deviceId':
// action.actionExecutor == 'delay' ? 'delay' : action.entityId,
// 'title': action.actionExecutor == 'delay'
// ? 'Delay'
// : (matchingDevice.name ?? 'Device'),
// 'productType': action.productType,
// 'imagePath': matchingDevice.getDefaultIcon(action.productType),
// };
final functions = matchingDevice.functions;
// final functions = matchingDevice.functions;
if (action.executorProperty != null && action.actionExecutor != 'delay') {
final functionCode = action.executorProperty!.functionCode;
for (var function in functions) {
if (function.code == functionCode) {
currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
DeviceFunctionData(
entityId: action.entityId,
functionCode: functionCode ?? '',
value: action.executorProperty!.functionValue,
operationName: function.operationName,
),
];
break;
}
}
} else if (action.actionExecutor == 'delay') {
final delayFunction = DelayFunction(
deviceId: action.entityId,
deviceName: 'Delay',
);
currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
DeviceFunctionData(
entityId: action.entityId,
functionCode: 'delay',
value: action.executorProperty?.delaySeconds ?? 0,
operationName: delayFunction.operationName,
),
];
}
// if (action.executorProperty != null && action.actionExecutor != 'delay') {
// final functionCode = action.executorProperty!.functionCode;
// for (var function in functions) {
// if (function.code == functionCode) {
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
// DeviceFunctionData(
// entityId: action.entityId,
// functionCode: functionCode ?? '',
// value: action.executorProperty!.functionValue,
// operationName: function.operationName,
// ),
// ];
// break;
// }
// }
// } else if (action.actionExecutor == 'delay') {
// final delayFunction = DelayFunction(
// deviceId: action.entityId,
// deviceName: 'Delay',
// );
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
// DeviceFunctionData(
// entityId: action.entityId,
// functionCode: 'delay',
// value: action.executorProperty?.delaySeconds ?? 0,
// operationName: delayFunction.operationName,
// ),
// ];
// }
return cardData;
}).toList();
// return cardData;
// }).toList();
return (thenItems, ifItems, currentFunctions);
}
// return (thenItems, ifItems, currentFunctions);
// }
Future<void> _onGetSceneDetails(
GetSceneDetails event, Emitter<RoutineState> emit) async {
Future<void> _onGetSceneDetails(GetSceneDetails event, Emitter<RoutineState> emit) async {
try {
emit(state.copyWith(
isLoading: true,
@ -724,12 +689,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (!deviceCards.containsKey(deviceId)) {
deviceCards[deviceId] = {
'entityId': action.entityId,
'deviceId':
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'uniqueCustomId':
action.type == 'automation' || action.actionExecutor == 'delay'
? const Uuid().v4()
: action.entityId,
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'uniqueCustomId': action.type == 'automation' || action.actionExecutor == 'delay'
? const Uuid().v4()
: action.entityId,
'title': action.actionExecutor == 'delay'
? 'Delay'
: action.type == 'automation'
@ -764,16 +727,15 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
),
);
// emit(state.copyWith(automationActionExecutor: action.actionExecutor));
} else if (action.executorProperty != null &&
action.actionExecutor != 'delay') {
if (!updatedFunctions.containsKey(uniqueCustomId)) {
updatedFunctions[uniqueCustomId] = [];
}
} else if (action.executorProperty != null && action.actionExecutor != 'delay') {
// if (!updatedFunctions.containsKey(uniqueCustomId)) {
// updatedFunctions[uniqueCustomId] = [];
// }
final functions = matchingDevice?.functions;
final functionCode = action.executorProperty?.functionCode;
for (DeviceFunction function in functions ?? []) {
if (function.code == functionCode) {
updatedFunctions[uniqueCustomId]!.add(
updatedFunctions[const Uuid().v4()]!.add(
DeviceFunctionData(
entityId: action.entityId,
functionCode: functionCode ?? '',
@ -837,8 +799,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
FutureOr<void> _onResetRoutineState(
ResetRoutineState event, Emitter<RoutineState> emit) {
FutureOr<void> _onResetRoutineState(ResetRoutineState event, Emitter<RoutineState> emit) {
emit(state.copyWith(
ifItems: [],
thenItems: [],
@ -861,6 +822,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
isUpdate: false,
createRoutineView: false));
}
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) async {
try {
final projectId = await ProjectManager.getProjectUUID() ?? '';
@ -900,7 +862,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
));
}
}
// FutureOr<void> _deleteAutomation(DeleteAutomation event, Emitter<RoutineState> emit) {
// try {
// emit(state.copyWith(isLoading: true));
@ -915,8 +877,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
// }
// }
FutureOr<void> _fetchDevices(
FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
FutureOr<void> _fetchDevices(FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true));
try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
@ -925,21 +886,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
var createRoutineBloc = context.read<CreateRoutineBloc>();
var spaceBloc = context.read<SpaceTreeBloc>();
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
devices.addAll(await DevicesManagementApi()
.fetchDevices(communityId, spaceId, projectUuid));
devices.addAll(
await DevicesManagementApi().fetchDevices(communityId, spaceId, projectUuid));
}
}
} else {
devices.addAll(await DevicesManagementApi().fetchDevices(
createRoutineBloc.selectedCommunityId,
createRoutineBloc.selectedSpaceId,
projectUuid));
createRoutineBloc.selectedCommunityId, createRoutineBloc.selectedSpaceId, projectUuid));
}
emit(state.copyWith(isLoading: false, devices: devices));
@ -948,8 +905,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
FutureOr<void> _onUpdateScene(
UpdateScene event, Emitter<RoutineState> emit) async {
FutureOr<void> _onUpdateScene(UpdateScene event, Emitter<RoutineState> emit) async {
try {
// Check if first action is delay
// if (_isFirstActionDelay(state.thenItems)) {
@ -963,8 +919,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (_isLastActionDelay(state.thenItems)) {
emit(state.copyWith(
errorMessage:
'A delay condition cannot be the only or the last action',
errorMessage: 'A delay condition cannot be the only or the last action',
isLoading: false,
));
return;
@ -1017,8 +972,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
actions: actions,
);
final result =
await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
final result = await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
if (result['success']) {
add(ResetRoutineState());
add(const LoadScenes());
@ -1037,8 +991,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
FutureOr<void> _onUpdateAutomation(
UpdateAutomation event, Emitter<RoutineState> emit) async {
FutureOr<void> _onUpdateAutomation(UpdateAutomation event, Emitter<RoutineState> emit) async {
try {
if (state.routineName == null || state.routineName!.isEmpty) {
emit(state.copyWith(
@ -1253,15 +1206,13 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
),
);
final deviceId = action.actionExecutor == 'delay'
? '${action.entityId}_delay'
: action.entityId;
final deviceId =
action.actionExecutor == 'delay' ? '${action.entityId}_delay' : action.entityId;
if (!deviceThenCards.containsKey(deviceId)) {
deviceThenCards[deviceId] = {
'entityId': action.entityId,
'deviceId':
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'uniqueCustomId': const Uuid().v4(),
'title': action.actionExecutor == 'delay'
? 'Delay'
@ -1281,7 +1232,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
: action.type == 'automation'
? 'automation'
: 'action',
'icon': action.icon ?? ''
'icon': action.icon ?? '',
};
}
@ -1292,8 +1243,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
updatedFunctions[uniqueCustomId] = [];
}
if (action.executorProperty != null &&
action.actionExecutor != 'delay') {
if (action.executorProperty != null && action.actionExecutor != 'delay') {
final functions = matchingDevice.functions;
final functionCode = action.executorProperty!.functionCode;
for (var function in functions) {
@ -1335,14 +1285,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
final ifItems = deviceIfCards.values
.where((card) => card['type'] == 'condition')
.toList();
final ifItems = deviceIfCards.values.where((card) => card['type'] == 'condition').toList();
final thenItems = deviceThenCards.values
.where((card) =>
card['type'] == 'action' ||
card['type'] == 'automation' ||
card['type'] == 'scene')
card['type'] == 'action' || card['type'] == 'automation' || card['type'] == 'scene')
.toList();
emit(state.copyWith(
@ -1364,8 +1310,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onSceneTrigger(
SceneTrigger event, Emitter<RoutineState> emit) async {
Future<void> _onSceneTrigger(SceneTrigger event, Emitter<RoutineState> emit) async {
emit(state.copyWith(loadingSceneId: event.sceneId));
try {
@ -1407,29 +1352,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (success) {
final updatedAutomations = await SceneApi.getAutomationByUnitId(
event.automationStatusUpdate.spaceUuid,
event.communityId,
projectId);
event.automationStatusUpdate.spaceUuid, event.communityId, projectId);
// Remove from loading set safely
final updatedLoadingIds = {...state.loadingAutomationIds!}
..remove(event.automationId);
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
emit(state.copyWith(
automations: updatedAutomations,
loadingAutomationIds: updatedLoadingIds,
));
} else {
final updatedLoadingIds = {...state.loadingAutomationIds!}
..remove(event.automationId);
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
emit(state.copyWith(
loadingAutomationIds: updatedLoadingIds,
errorMessage: 'Update failed',
));
}
} catch (e) {
final updatedLoadingIds = {...state.loadingAutomationIds!}
..remove(event.automationId);
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
emit(state.copyWith(
loadingAutomationIds: updatedLoadingIds,
errorMessage: 'Update error: ${e.toString()}',

View File

@ -63,7 +63,11 @@ class _CreateNewRoutinesDialogState extends State<CreateNewRoutinesDialog> {
Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child: CommunityDropdown(
communities: _bloc.communities,
communities: _bloc.communities..sort(
(a, b) => a.name.toLowerCase().compareTo(
b.name.toLowerCase(),
),
),
selectedValue: _selectedCommunity,
onChanged: (String? newValue) {
setState(() {

View File

@ -6,6 +6,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class DraggableCard extends StatelessWidget {
@ -70,7 +71,7 @@ class DraggableCard extends StatelessWidget {
child: Container(
padding: padding ?? const EdgeInsets.all(16),
width: 110,
height: deviceFunctions.isEmpty ? 123 : null,
height: deviceFunctions.isEmpty ? 160 : 170,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
@ -78,6 +79,7 @@ class DraggableCard extends StatelessWidget {
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 50,
@ -101,19 +103,82 @@ class DraggableCard extends StatelessWidget {
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 3),
child: Text(
deviceData['title'] ?? deviceData['name'] ?? title,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize: 12,
child: Flexible(
child: Text(
deviceData['title'] ?? deviceData['name'] ?? title,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize: 12,
),
),
),
),
const SizedBox(
height: 4,
),
Visibility(
visible: deviceData['tag'] != null && deviceData['tag'] != '',
child: Row(
spacing: 2,
children: [
SizedBox(
width: 8, height: 8, child: SvgPicture.asset(Assets.deviceTagIcon)),
Flexible(
child: Text(
deviceData['tag'] ?? '',
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.lightGreyColor,
fontSize: 9,
fontWeight: FontWeight.w400,
),
),
),
],
),
),
Visibility(
visible: deviceData['subSpace'] != null && deviceData['subSpace'] != '',
child: const SizedBox(
height: 4,
),
),
Visibility(
visible: deviceData['subSpace'] != null && deviceData['subSpace'] != '',
child: Row(
spacing: 2,
children: [
SizedBox(
width: 8,
height: 8,
child: SvgPicture.asset(Assets.spaceLocationIcon)),
Flexible(
child: Text(
deviceData['subSpace'] ?? '',
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.lightGreyColor,
fontSize: 9,
fontWeight: FontWeight.w400,
),
),
),
],
),
),
],
),
if (deviceFunctions.isNotEmpty)
const SizedBox(
height: 4,
),
if (deviceFunctions.isNotEmpty)
...deviceFunctions.map((function) => Row(
mainAxisSize: MainAxisSize.min,
@ -123,7 +188,7 @@ class DraggableCard extends StatelessWidget {
'${function.operationName}: ${_formatFunctionValue(function)}',
style: context.textTheme.bodySmall?.copyWith(
fontSize: 9,
color: ColorsManager.textGray,
color: ColorsManager.lightGreyColor,
height: 1.2,
),
maxLines: 2,

View File

@ -51,12 +51,12 @@ class _RoutineDevicesState extends State<RoutineDevices> {
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
'tag': device.deviceTags!.isNotEmpty ? device.deviceTags![0].name : '',
'subSpace': device.deviceSubSpace?.subspaceName ?? '',
};
if (state.searchText != null && state.searchText!.isNotEmpty) {
return device.name!
.toLowerCase()
.contains(state.searchText!.toLowerCase())
return device.name!.toLowerCase().contains(state.searchText!.toLowerCase())
? DraggableCard(
imagePath: deviceData['imagePath'] as String,
title: deviceData['title'] as String,

View File

@ -5,7 +5,6 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart';
@ -25,23 +24,21 @@ class OneGangSwitchHelper {
required String uniqueCustomId,
required bool removeComparetors,
}) async {
List<BaseSwitchFunction> oneGangFunctions =
functions.whereType<BaseSwitchFunction>().toList();
List<BaseSwitchFunction> oneGangFunctions = functions.whereType<BaseSwitchFunction>().toList();
return showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext context) {
return BlocProvider(
create: (_) => FunctionBloc()
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog(
contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) {
final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions
.firstWhere((f) => f.functionCode == selectedFunction,
final selectedFunctionData =
state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData(
entityId: '',
functionCode: selectedFunction ?? '',
@ -88,12 +85,9 @@ class OneGangSwitchHelper {
color: ColorsManager.textGray,
),
onTap: () {
context
.read<FunctionBloc>()
.add(SelectFunction(
context.read<FunctionBloc>().add(SelectFunction(
functionCode: function.code,
operationName:
function.operationName,
operationName: function.operationName,
));
},
);
@ -226,11 +220,11 @@ class OneGangSwitchHelper {
selectedFunctionData,
),
const SizedBox(height: 20),
_buildCountDownDisplay(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
_buildCountDownDisplay(
context, initialValue, device, operationName, selectedFunctionData, selectCode),
const SizedBox(height: 20),
_buildCountDownSlider(context, initialValue, device, operationName,
selectedFunctionData, selectCode),
_buildCountDownSlider(
context, initialValue, device, operationName, selectedFunctionData, selectCode),
],
);
}
@ -271,8 +265,7 @@ class OneGangSwitchHelper {
minHeight: 40.0,
minWidth: 40.0,
),
isSelected:
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
children: conditions.map((c) => Text(c)).toList(),
);
}
@ -320,8 +313,7 @@ class OneGangSwitchHelper {
value: (initialValue ?? 0).toDouble(),
min: operationalValues.minValue?.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))
.round(),
onChanged: (value) {
@ -373,13 +365,9 @@ class OneGangSwitchHelper {
style: context.textTheme.bodyMedium,
),
trailing: Icon(
isSelected
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
size: 24,
color: isSelected
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray,
),
onTap: () {
if (!isSelected) {
@ -391,8 +379,7 @@ class OneGangSwitchHelper {
operationName: operationName,
value: value.value,
condition: selectedFunctionData?.condition,
valueDescription:
selectedFunctionData?.valueDescription,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);

View File

@ -1,4 +1,3 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
@ -28,9 +27,12 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
@override
void initState() {
super.initState();
_hoursController = FixedExtentScrollController(initialItem: widget.initialHours);
_minutesController = FixedExtentScrollController(initialItem: widget.initialMinutes);
_secondsController = FixedExtentScrollController(initialItem: widget.initialSeconds);
_hoursController =
FixedExtentScrollController(initialItem: widget.initialHours);
_minutesController =
FixedExtentScrollController(initialItem: widget.initialMinutes);
_secondsController =
FixedExtentScrollController(initialItem: widget.initialSeconds);
}
@override
@ -47,6 +49,8 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
}
}
@override
void dispose() {
_hoursController.dispose();
@ -61,26 +65,28 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildPickerColumn(
label: 'h',
controller: _hoursController,
itemCount: 24,
onChanged: (value) => _handleTimeChange(
value,
_minutesController.selectedItem,
_secondsController.selectedItem,
),
),
label: 'h',
controller: _hoursController,
itemCount: 3,
onChanged: (value) {
_handleTimeChange(
value,
_minutesController.selectedItem,
_secondsController.selectedItem,
);
}),
const SizedBox(width: 5),
_buildPickerColumn(
label: 'm',
controller: _minutesController,
itemCount: 60,
onChanged: (value) => _handleTimeChange(
_hoursController.selectedItem,
value,
_secondsController.selectedItem,
),
),
label: 'm',
controller: _minutesController,
itemCount: 60,
onChanged: (value) {
_handleTimeChange(
_hoursController.selectedItem,
value,
_secondsController.selectedItem,
);
}),
const SizedBox(width: 5),
_buildPickerColumn(
label: 's',
@ -97,6 +103,19 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
}
void _handleTimeChange(int hours, int minutes, int seconds) {
int total = hours * 3600 + minutes * 60 + seconds;
if (total > 10000) {
hours = 2;
minutes = 46;
seconds = 40;
total = 10000;
WidgetsBinding.instance.addPostFrameCallback((_) {
_hoursController.jumpToItem(hours);
_minutesController.jumpToItem(minutes);
_secondsController.jumpToItem(seconds);
});
}
widget.onTimeChanged(hours, minutes, seconds);
}
@ -147,4 +166,4 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
],
);
}
}
}

View File

@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.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/create_community/view/create_community_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class SidebarAddCommunityButton extends StatelessWidget {
const SidebarAddCommunityButton({
required this.existingCommunityNames,
super.key,
});
final List<String> existingCommunityNames;
@override
Widget build(BuildContext context) {
return SizedBox.square(
dimension: 30,
child: IconButton(
style: IconButton.styleFrom(
iconSize: 20,
backgroundColor: ColorsManager.circleImageBackground,
shape: const CircleBorder(
side: BorderSide(
color: ColorsManager.lightGrayBorderColor,
width: 3,
),
),
),
onPressed: () => _showCreateCommunityDialog(context),
icon: SvgPicture.asset(Assets.addIcon),
),
);
}
void _showCreateCommunityDialog(BuildContext context) => showDialog<void>(
context: context,
builder: (context) => CreateCommunityDialog(
isEditMode: false,
existingCommunityNames: existingCommunityNames,
onCreateCommunity: (name, description) {
context.read<SpaceManagementBloc>().add(
CreateCommunityEvent(name, description, context),
);
},
),
);
}

View File

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/sidebar_add_community_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/style.dart';
class SidebarHeader extends StatelessWidget {
const SidebarHeader({required this.existingCommunityNames, super.key});
final List<String> existingCommunityNames;
@override
Widget build(BuildContext context) {
return Container(
decoration: subSectionContainerDecoration,
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Communities',
style: context.textTheme.titleMedium?.copyWith(
color: ColorsManager.blackColor,
),
),
SidebarAddCommunityButton(existingCommunityNames: existingCommunityNames),
],
),
);
}
}

View File

@ -1,17 +1,15 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/common/widgets/search_bar.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/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_tile.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/sidebar_header.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_tile_widget.dart';
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_event.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart';
class SidebarWidget extends StatefulWidget {
@ -19,62 +17,59 @@ class SidebarWidget extends StatefulWidget {
final String? selectedSpaceUuid;
const SidebarWidget({
super.key,
required this.communities,
this.selectedSpaceUuid,
super.key,
});
@override
_SidebarWidgetState createState() => _SidebarWidgetState();
State<SidebarWidget> createState() => _SidebarWidgetState();
}
class _SidebarWidgetState extends State<SidebarWidget> {
String _searchQuery = ''; // Track search query
String _searchQuery = '';
String? _selectedSpaceUuid;
String? _selectedId;
@override
void initState() {
_selectedId = widget.selectedSpaceUuid;
super.initState();
_selectedId = widget.selectedSpaceUuid; // Initialize with the passed selected space UUID
}
@override
void didUpdateWidget(covariant SidebarWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.selectedSpaceUuid != oldWidget.selectedSpaceUuid) {
setState(() {
_selectedId = widget.selectedSpaceUuid;
});
setState(() => _selectedId = widget.selectedSpaceUuid);
}
super.didUpdateWidget(oldWidget);
}
// Function to filter communities based on the search query
List<CommunityModel> _filterCommunities() {
List<CommunityModel> _filteredCommunities() {
if (_searchQuery.isEmpty) {
// Reset the selected community and space UUIDs if there's no query
_selectedSpaceUuid = null;
return widget.communities;
}
// Filter communities and expand only those that match the query
return widget.communities.where((community) {
final containsQueryInCommunity =
community.name.toLowerCase().contains(_searchQuery.toLowerCase());
final containsQueryInSpaces =
community.spaces.any((space) => _containsQuery(space, _searchQuery.toLowerCase()));
final containsQueryInSpaces = community.spaces.any((space) =>
_containsQuery(space: space, query: _searchQuery.toLowerCase()));
return containsQueryInCommunity || containsQueryInSpaces;
}).toList();
}
// Helper function to determine if any space or its children match the search query
bool _containsQuery(SpaceModel space, String query) {
bool _containsQuery({
required SpaceModel space,
required String query,
}) {
final matchesSpace = space.name.toLowerCase().contains(query);
final matchesChildren =
space.children.any((child) => _containsQuery(child, query)); // Recursive check for children
final matchesChildren = space.children.any(
(child) => _containsQuery(space: child, query: query),
);
// If the space or any of its children match the query, expand this space
if (matchesSpace || matchesChildren) {
_selectedSpaceUuid = space.uuid;
}
@ -83,79 +78,35 @@ class _SidebarWidgetState extends State<SidebarWidget> {
}
bool _isSpaceOrChildSelected(SpaceModel space) {
// Return true if the current space or any of its child spaces is selected
if (_selectedSpaceUuid == space.uuid) {
return true;
}
// Recursively check if any child spaces match the query
for (var child in space.children) {
if (_isSpaceOrChildSelected(child)) {
return true;
}
}
return false;
final isSpaceSelected = _selectedSpaceUuid == space.uuid;
final anySubSpaceIsSelected = space.children.any(_isSpaceOrChildSelected);
return isSpaceSelected || anySubSpaceIsSelected;
}
@override
Widget build(BuildContext context) {
final filteredCommunities = _filterCommunities();
final filteredCommunities = _filteredCommunities();
return Container(
width: 300,
decoration: subSectionContainerDecoration,
child: Column(
mainAxisSize: MainAxisSize.min, // Ensures the Column only takes necessary height
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Communities title with the add button
Container(
decoration: subSectionContainerDecoration,
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Communities',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: ColorsManager.blackColor,
)),
GestureDetector(
onTap: () => _navigateToBlank(context),
child: Container(
width: 30,
height: 30,
decoration: const BoxDecoration(
color: ColorsManager.whiteColors,
shape: BoxShape.circle,
),
child: Center(
child: SvgPicture.asset(
Assets.roundedAddIcon,
width: 24,
height: 24,
),
),
),
),
],
),
SidebarHeader(
existingCommunityNames:
widget.communities.map((community) => community.name).toList(),
),
// Search bar
CustomSearchBar(
onSearchChanged: (query) {
setState(() {
_searchQuery = query;
});
},
onSearchChanged: (query) => setState(() => _searchQuery = query),
),
const SizedBox(height: 16),
// Community list
Expanded(
child: ListView(
children: filteredCommunities.map((community) {
return _buildCommunityTile(context, community);
}).toList(),
children: filteredCommunities
.map((community) => _buildCommunityTile(context, community))
.toList(),
),
),
],
@ -163,18 +114,7 @@ class _SidebarWidgetState extends State<SidebarWidget> {
);
}
void _navigateToBlank(BuildContext context) {
setState(() {
_selectedId = '';
});
context.read<SpaceManagementBloc>().add(
NewCommunityEvent(communities: widget.communities),
);
}
Widget _buildCommunityTile(BuildContext context, CommunityModel community) {
bool hasChildren = community.spaces.isNotEmpty;
return CommunityTile(
title: community.name,
key: ValueKey(community.uuid),
@ -183,7 +123,7 @@ class _SidebarWidgetState extends State<SidebarWidget> {
onItemSelected: () {
setState(() {
_selectedId = community.uuid;
_selectedSpaceUuid = null; // Update the selected community
_selectedSpaceUuid = null;
});
context.read<CenterBodyBloc>().add(CommunitySelectedEvent());
@ -192,46 +132,51 @@ class _SidebarWidgetState extends State<SidebarWidget> {
SelectCommunityEvent(selectedCommunity: community),
);
},
onExpansionChanged: (String title, bool expanded) {
_handleExpansionChange(community.uuid, expanded);
},
children: hasChildren
? community.spaces
.where((space) => (space.status != SpaceStatus.deleted ||
space.status != SpaceStatus.parentDeleted))
.map((space) => _buildSpaceTile(space, community))
.toList()
: null,
onExpansionChanged: (title, expanded) {},
children: community.spaces
.where((space) {
final isDeleted = space.status != SpaceStatus.deleted;
final isParentDeleted = space.status != SpaceStatus.parentDeleted;
return (isDeleted || isParentDeleted);
})
.map((space) => _buildSpaceTile(space: space, community: community))
.toList(),
);
}
Widget _buildSpaceTile(SpaceModel space, CommunityModel community, {int depth = 1}) {
bool isExpandedSpace = _isSpaceOrChildSelected(space);
Widget _buildSpaceTile({
required SpaceModel space,
required CommunityModel community,
}) {
final spaceIsExpanded = _isSpaceOrChildSelected(space);
final isSelected = _selectedId == space.uuid;
return Padding(
padding: EdgeInsets.only(left: depth * 16.0),
child: SpaceTile(
title: space.name,
key: ValueKey(space.uuid),
isSelected: _selectedId == space.uuid,
initiallyExpanded: isExpandedSpace,
onExpansionChanged: (bool expanded) {
_handleExpansionChange(space.uuid ?? '', expanded);
},
onItemSelected: () {
setState(() {
_selectedId = space.uuid;
_selectedSpaceUuid = space.uuid;
});
padding: const EdgeInsetsDirectional.only(start: 16.0),
child: SpaceTile(
title: space.name,
key: ValueKey(space.uuid),
isSelected: isSelected,
initiallyExpanded: spaceIsExpanded,
onExpansionChanged: (expanded) {},
onItemSelected: () {
setState(() {
_selectedId = space.uuid;
_selectedSpaceUuid = space.uuid;
});
context.read<SpaceManagementBloc>().add(
SelectSpaceEvent(selectedCommunity: community, selectedSpace: space),
);
},
children: space.children.isNotEmpty
? space.children.map((childSpace) => _buildSpaceTile(childSpace, community)).toList()
: [], // Recursively render child spaces if available
));
context.read<SpaceManagementBloc>().add(
SelectSpaceEvent(selectedCommunity: community, selectedSpace: space),
);
},
children: space.children
.map(
(childSpace) => _buildSpaceTile(
space: childSpace,
community: community,
),
)
.toList(),
),
);
}
void _handleExpansionChange(String uuid, bool expanded) {}
}

View File

@ -145,13 +145,11 @@ class CreateSpaceModelDialog extends StatelessWidget {
),
const SizedBox(height: 10),
TagChipDisplay(
context,
screenWidth: screenWidth,
spaceModel: updatedSpaceModel,
products: products,
subspaces: subspaces,
allTags: allTags,
spaceNameController: spaceNameController,
spaceName: spaceNameController.text,
pageContext: pageContext,
otherSpaceModels: otherSpaceModels,
allSpaceModels: allSpaceModels,

View File

@ -10,139 +10,152 @@ import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/button_content_widget.dart';
import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class TagChipDisplay extends StatelessWidget {
final double screenWidth;
const TagChipDisplay({
required this.spaceModel,
required this.products,
required this.subspaces,
required this.allTags,
required this.spaceName,
required this.projectTags,
this.pageContext,
this.otherSpaceModels,
this.allSpaceModels,
super.key,
});
final SpaceTemplateModel? spaceModel;
final List<ProductModel>? products;
final List<SubspaceTemplateModel>? subspaces;
final List<String>? allTags;
final TextEditingController spaceNameController;
final String spaceName;
final BuildContext? pageContext;
final List<String>? otherSpaceModels;
final List<SpaceTemplateModel>? allSpaceModels;
final List<Tag> projectTags;
const TagChipDisplay(BuildContext context,
{Key? key,
required this.screenWidth,
required this.spaceModel,
required this.products,
required this.subspaces,
required this.allTags,
required this.spaceNameController,
this.pageContext,
this.otherSpaceModels,
this.allSpaceModels,
required this.projectTags})
: super(key: key);
Map<ProductModel, int> get _groupedTags {
final spaceTags = spaceModel?.tags ?? <Tag>[];
final subspaces = spaceModel?.subspaceModels ?? [];
final subspaceTags = subspaces.expand((e) => e.tags ?? <Tag>[]).toList();
return TagHelper.groupTags([...spaceTags, ...subspaceTags]);
}
@override
Widget build(BuildContext context) {
return (spaceModel?.tags?.isNotEmpty == true ||
spaceModel?.subspaceModels?.any((subspace) => subspace.tags?.isNotEmpty == true) ==
true)
? SizedBox(
width: screenWidth * 0.25,
child: Container(
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: ColorsManager.textFieldGreyColor,
borderRadius: BorderRadius.circular(15),
border: Border.all(
color: ColorsManager.textFieldGreyColor,
width: 3.0, // Border width
),
),
child: Wrap(
spacing: 8.0,
runSpacing: 8.0,
children: [
// Combine tags from spaceModel and subspaces
...TagHelper.groupTags([
...?spaceModel?.tags,
...?spaceModel?.subspaceModels?.expand((subspace) => subspace.tags ?? [])
]).entries.map(
(entry) => Chip(
avatar: SizedBox(
width: 24,
height: 24,
child: SvgPicture.asset(
entry.key.icon ?? 'assets/icons/gateway.svg',
fit: BoxFit.contain,
),
),
label: Text(
'x${entry.value}', // Show count
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(color: ColorsManager.spaceColor),
),
backgroundColor: ColorsManager.whiteColors,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: const BorderSide(
color: ColorsManager.spaceColor,
),
),
),
),
EditChip(onTap: () async {
// Use the Navigator's context for showDialog
Navigator.of(context).pop();
final hasTags = spaceModel?.tags?.isNotEmpty ?? false;
final hasSubspaceTags =
spaceModel?.subspaceModels?.any((e) => e.tags?.isNotEmpty ?? false) ?? false;
await showDialog<bool>(
barrierDismissible: false,
context: context,
builder: (context) => AssignTagModelsDialog(
products: products,
allSpaceModels: allSpaceModels,
subspaces: subspaces,
pageContext: pageContext,
allTags: allTags,
spaceModel: spaceModel,
otherSpaceModels: otherSpaceModels,
initialTags: TagHelper.generateInitialTags(
subspaces: subspaces, spaceTagModels: spaceModel?.tags ?? []),
title: 'Edit Device',
addedProducts: TagHelper.createInitialSelectedProducts(
spaceModel?.tags ?? [], subspaces),
spaceName: spaceModel?.modelName ?? '',
projectTags: projectTags,
));
})
],
),
),
)
: TextButton(
onPressed: () async {
Navigator.of(context).pop();
if (hasTags || hasSubspaceTags) {
return Container(
width: context.screenWidth * 0.25,
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: ColorsManager.textFieldGreyColor,
borderRadius: BorderRadius.circular(15),
border: Border.all(
color: ColorsManager.textFieldGreyColor,
width: 3,
),
),
child: Wrap(
spacing: 8,
runSpacing: 8,
children: [
..._groupedTags.entries.map((entry) => _buildChip(context, entry)),
_buildEditChip(context),
],
),
);
}
await showDialog<bool>(
barrierDismissible: false,
context: context,
builder: (context) => AddDeviceTypeModelWidget(
products: products,
subspaces: subspaces,
allTags: allTags,
spaceName: spaceNameController.text,
pageContext: pageContext,
isCreate: true,
spaceModel: spaceModel,
otherSpaceModels: otherSpaceModels,
projectTags: projectTags,
),
);
},
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
),
child: const ButtonContentWidget(
icon: Icons.add,
label: 'Add Devices',
),
);
return _buildAddDevicesButton(context);
}
Widget _buildEditChip(BuildContext context) {
return EditChip(
onTap: () => showDialog<void>(
context: context,
builder: (context) => AssignTagModelsDialog(
products: products,
allSpaceModels: allSpaceModels,
subspaces: subspaces,
pageContext: pageContext,
allTags: allTags,
spaceModel: spaceModel,
otherSpaceModels: otherSpaceModels,
initialTags: TagHelper.generateInitialTags(
subspaces: subspaces,
spaceTagModels: spaceModel?.tags ?? [],
),
title: 'Edit Device',
addedProducts: TagHelper.createInitialSelectedProducts(
spaceModel?.tags ?? [],
subspaces,
),
spaceName: spaceModel?.modelName ?? '',
projectTags: projectTags,
),
),
);
}
Widget _buildChip(
BuildContext context,
MapEntry<ProductModel, int> entry,
) {
return Chip(
backgroundColor: ColorsManager.whiteColors,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: const BorderSide(
color: ColorsManager.spaceColor,
),
),
avatar: SvgPicture.asset(
entry.key.icon ?? Assets.gateway,
fit: BoxFit.contain,
height: 24,
width: 24,
),
label: Text(
'${entry.value}',
style: context.textTheme.bodySmall!.copyWith(
color: ColorsManager.spaceColor,
),
),
);
}
Widget _buildAddDevicesButton(BuildContext context) {
return TextButton(
onPressed: () => showDialog<void>(
context: context,
builder: (context) => AddDeviceTypeModelWidget(
products: products,
subspaces: subspaces,
allTags: allTags,
spaceName: spaceName,
pageContext: pageContext,
isCreate: true,
spaceModel: spaceModel,
otherSpaceModels: otherSpaceModels,
projectTags: projectTags,
),
),
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
),
child: const ButtonContentWidget(
icon: Icons.add,
label: 'Add Devices',
),
);
}
}

View File

@ -13,12 +13,10 @@ class Assets {
static const String rightLine = "assets/images/right_line.png";
static const String google = "assets/images/google.svg";
static const String facebook = "assets/images/facebook.svg";
static const String invisiblePassword =
"assets/images/Password_invisible.svg";
static const String invisiblePassword = "assets/images/Password_invisible.svg";
static const String visiblePassword = "assets/images/password_visible.svg";
static const String accessIcon = "assets/images/access_icon.svg";
static const String spaseManagementIcon =
"assets/images/spase_management_icon.svg";
static const String spaseManagementIcon = "assets/images/spase_management_icon.svg";
static const String devicesIcon = "assets/images/devices_icon.svg";
static const String moveinIcon = "assets/images/movein_icon.svg";
static const String constructionIcon = "assets/images/construction_icon.svg";
@ -31,15 +29,13 @@ class Assets {
static const String emptyTable = "assets/images/empty_table.svg";
// General assets
static const String motionlessDetection =
"assets/icons/motionless_detection.svg";
static const String motionlessDetection = "assets/icons/motionless_detection.svg";
static const String acHeating = "assets/icons/ac_heating.svg";
static const String acPowerOff = "assets/icons/ac_power_off.svg";
static const String acFanMiddle = "assets/icons/ac_fan_middle.svg";
static const String switchAlarmSound = "assets/icons/switch_alarm_sound.svg";
static const String resetOff = "assets/icons/reset_off.svg";
static const String sensitivityOperationIcon =
"assets/icons/sesitivity_operation_icon.svg";
static const String sensitivityOperationIcon = "assets/icons/sesitivity_operation_icon.svg";
static const String motionDetection = "assets/icons/motion_detection.svg";
static const String freezing = "assets/icons/freezing.svg";
static const String indicator = "assets/icons/indicator.svg";
@ -60,8 +56,7 @@ class Assets {
static const String celsiusDegrees = "assets/icons/celsius_degrees.svg";
static const String masterState = "assets/icons/master_state.svg";
static const String acPower = "assets/icons/ac_power.svg";
static const String farDetectionFunction =
"assets/icons/far_detection_function.svg";
static const String farDetectionFunction = "assets/icons/far_detection_function.svg";
static const String nobodyTime = "assets/icons/nobody_time.svg";
// Automation functions
@ -69,47 +64,33 @@ class Assets {
"assets/icons/automation_functions/temp_password_unlock.svg";
static const String doorlockNormalOpen =
"assets/icons/automation_functions/doorlock_normal_open.svg";
static const String doorbell =
"assets/icons/automation_functions/doorbell.svg";
static const String doorbell = "assets/icons/automation_functions/doorbell.svg";
static const String remoteUnlockViaApp =
"assets/icons/automation_functions/remote_unlock_via_app.svg";
static const String doubleLock =
"assets/icons/automation_functions/double_lock.svg";
static const String selfTestResult =
"assets/icons/automation_functions/self_test_result.svg";
static const String lockAlarm =
"assets/icons/automation_functions/lock_alarm.svg";
static const String presenceState =
"assets/icons/automation_functions/presence_state.svg";
static const String currentTemp =
"assets/icons/automation_functions/current_temp.svg";
static const String presence =
"assets/icons/automation_functions/presence.svg";
static const String doubleLock = "assets/icons/automation_functions/double_lock.svg";
static const String selfTestResult = "assets/icons/automation_functions/self_test_result.svg";
static const String lockAlarm = "assets/icons/automation_functions/lock_alarm.svg";
static const String presenceState = "assets/icons/automation_functions/presence_state.svg";
static const String currentTemp = "assets/icons/automation_functions/current_temp.svg";
static const String presence = "assets/icons/automation_functions/presence.svg";
static const String residualElectricity =
"assets/icons/automation_functions/residual_electricity.svg";
static const String hijackAlarm =
"assets/icons/automation_functions/hijack_alarm.svg";
static const String passwordUnlock =
"assets/icons/automation_functions/password_unlock.svg";
static const String hijackAlarm = "assets/icons/automation_functions/hijack_alarm.svg";
static const String passwordUnlock = "assets/icons/automation_functions/password_unlock.svg";
static const String remoteUnlockRequest =
"assets/icons/automation_functions/remote_unlock_req.svg";
static const String cardUnlock =
"assets/icons/automation_functions/card_unlock.svg";
static const String cardUnlock = "assets/icons/automation_functions/card_unlock.svg";
static const String motion = "assets/icons/automation_functions/motion.svg";
static const String fingerprintUnlock =
"assets/icons/automation_functions/fingerprint_unlock.svg";
// Presence Sensor Assets
static const String sensorMotionIcon = "assets/icons/sensor_motion_ic.svg";
static const String sensorPresenceIcon =
"assets/icons/sensor_presence_ic.svg";
static const String sensorPresenceIcon = "assets/icons/sensor_presence_ic.svg";
static const String sensorVacantIcon = "assets/icons/sensor_vacant_ic.svg";
static const String illuminanceRecordIcon =
"assets/icons/illuminance_record_ic.svg";
static const String presenceRecordIcon =
"assets/icons/presence_record_ic.svg";
static const String helpDescriptionIcon =
"assets/icons/help_description_ic.svg";
static const String illuminanceRecordIcon = "assets/icons/illuminance_record_ic.svg";
static const String presenceRecordIcon = "assets/icons/presence_record_ic.svg";
static const String helpDescriptionIcon = "assets/icons/help_description_ic.svg";
static const String lightPulp = "assets/icons/light_pulb.svg";
static const String acDevice = "assets/icons/ac_device.svg";
@ -159,12 +140,10 @@ class Assets {
static const String unit = 'assets/icons/unit_icon.svg';
static const String villa = 'assets/icons/villa_icon.svg';
static const String iconEdit = 'assets/icons/icon_edit_icon.svg';
static const String textFieldSearch =
'assets/icons/textfield_search_icon.svg';
static const String textFieldSearch = 'assets/icons/textfield_search_icon.svg';
static const String roundedAddIcon = 'assets/icons/rounded_add_icon.svg';
static const String addIcon = 'assets/icons/add_icon.svg';
static const String smartThermostatIcon =
'assets/icons/smart_thermostat_icon.svg';
static const String smartThermostatIcon = 'assets/icons/smart_thermostat_icon.svg';
static const String smartLightIcon = 'assets/icons/smart_light_icon.svg';
static const String presenceSensor = 'assets/icons/presence_sensor.svg';
static const String Gang3SwitchIcon = 'assets/icons/3_Gang_switch_icon.svg';
@ -212,8 +191,7 @@ class Assets {
//assets/icons/water_leak_normal.svg
static const String waterLeakNormal = 'assets/icons/water_leak_normal.svg';
//assets/icons/water_leak_detected.svg
static const String waterLeakDetected =
'assets/icons/water_leak_detected.svg';
static const String waterLeakDetected = 'assets/icons/water_leak_detected.svg';
//assets/icons/automation_records.svg
static const String automationRecords = 'assets/icons/automation_records.svg';
@ -280,64 +258,40 @@ class Assets {
static const String delay = 'assets/icons/routine/delay.svg';
// Assets for functions_icons
static const String assetsSensitivityFunction =
"assets/icons/functions_icons/sensitivity.svg";
static const String assetsSensitivityFunction = "assets/icons/functions_icons/sensitivity.svg";
static const String assetsSensitivityOperationIcon =
"assets/icons/functions_icons/sesitivity_operation_icon.svg";
static const String assetsAcPower =
"assets/icons/functions_icons/ac_power.svg";
static const String assetsAcPowerOFF =
"assets/icons/functions_icons/ac_power_off.svg";
static const String assetsChildLock =
"assets/icons/functions_icons/child_lock.svg";
static const String assetsFreezing =
"assets/icons/functions_icons/freezing.svg";
static const String assetsFanSpeed =
"assets/icons/functions_icons/fan_speed.svg";
static const String assetsAcCooling =
"assets/icons/functions_icons/ac_cooling.svg";
static const String assetsAcHeating =
"assets/icons/functions_icons/ac_heating.svg";
static const String assetsCelsiusDegrees =
"assets/icons/functions_icons/celsius_degrees.svg";
static const String assetsTempreture =
"assets/icons/functions_icons/tempreture.svg";
static const String assetsAcFanLow =
"assets/icons/functions_icons/ac_fan_low.svg";
static const String assetsAcFanMiddle =
"assets/icons/functions_icons/ac_fan_middle.svg";
static const String assetsAcFanHigh =
"assets/icons/functions_icons/ac_fan_high.svg";
static const String assetsAcFanAuto =
"assets/icons/functions_icons/ac_fan_auto.svg";
static const String assetsSceneChildLock =
"assets/icons/functions_icons/scene_child_lock.svg";
static const String assetsAcPower = "assets/icons/functions_icons/ac_power.svg";
static const String assetsAcPowerOFF = "assets/icons/functions_icons/ac_power_off.svg";
static const String assetsChildLock = "assets/icons/functions_icons/child_lock.svg";
static const String assetsFreezing = "assets/icons/functions_icons/freezing.svg";
static const String assetsFanSpeed = "assets/icons/functions_icons/fan_speed.svg";
static const String assetsAcCooling = "assets/icons/functions_icons/ac_cooling.svg";
static const String assetsAcHeating = "assets/icons/functions_icons/ac_heating.svg";
static const String assetsCelsiusDegrees = "assets/icons/functions_icons/celsius_degrees.svg";
static const String assetsTempreture = "assets/icons/functions_icons/tempreture.svg";
static const String assetsAcFanLow = "assets/icons/functions_icons/ac_fan_low.svg";
static const String assetsAcFanMiddle = "assets/icons/functions_icons/ac_fan_middle.svg";
static const String assetsAcFanHigh = "assets/icons/functions_icons/ac_fan_high.svg";
static const String assetsAcFanAuto = "assets/icons/functions_icons/ac_fan_auto.svg";
static const String assetsSceneChildLock = "assets/icons/functions_icons/scene_child_lock.svg";
static const String assetsSceneChildUnlock =
"assets/icons/functions_icons/scene_child_unlock.svg";
static const String assetsSceneRefresh =
"assets/icons/functions_icons/scene_refresh.svg";
static const String assetsLightCountdown =
"assets/icons/functions_icons/light_countdown.svg";
static const String assetsFarDetection =
"assets/icons/functions_icons/far_detection.svg";
static const String assetsSceneRefresh = "assets/icons/functions_icons/scene_refresh.svg";
static const String assetsLightCountdown = "assets/icons/functions_icons/light_countdown.svg";
static const String assetsFarDetection = "assets/icons/functions_icons/far_detection.svg";
static const String assetsFarDetectionFunction =
"assets/icons/functions_icons/far_detection_function.svg";
static const String assetsIndicator =
"assets/icons/functions_icons/indicator.svg";
static const String assetsMotionDetection =
"assets/icons/functions_icons/motion_detection.svg";
static const String assetsIndicator = "assets/icons/functions_icons/indicator.svg";
static const String assetsMotionDetection = "assets/icons/functions_icons/motion_detection.svg";
static const String assetsMotionlessDetection =
"assets/icons/functions_icons/motionless_detection.svg";
static const String assetsNobodyTime =
"assets/icons/functions_icons/nobody_time.svg";
static const String assetsFactoryReset =
"assets/icons/functions_icons/factory_reset.svg";
static const String assetsMasterState =
"assets/icons/functions_icons/master_state.svg";
static const String assetsNobodyTime = "assets/icons/functions_icons/nobody_time.svg";
static const String assetsFactoryReset = "assets/icons/functions_icons/factory_reset.svg";
static const String assetsMasterState = "assets/icons/functions_icons/master_state.svg";
static const String assetsSwitchAlarmSound =
"assets/icons/functions_icons/switch_alarm_sound.svg";
static const String assetsResetOff =
"assets/icons/functions_icons/reset_off.svg";
static const String assetsResetOff = "assets/icons/functions_icons/reset_off.svg";
// Assets for automation_functions
static const String assetsCardUnlock =
@ -368,8 +322,7 @@ class Assets {
"assets/icons/functions_icons/automation_functions/self_test_result.svg";
static const String assetsPresence =
"assets/icons/functions_icons/automation_functions/presence.svg";
static const String assetsMotion =
"assets/icons/functions_icons/automation_functions/motion.svg";
static const String assetsMotion = "assets/icons/functions_icons/automation_functions/motion.svg";
static const String assetsCurrentTemp =
"assets/icons/functions_icons/automation_functions/current_temp.svg";
static const String assetsPresenceState =
@ -381,16 +334,12 @@ class Assets {
static const String activeUser = 'assets/icons/active_user.svg';
static const String deActiveUser = 'assets/icons/deactive_user.svg';
static const String invitedIcon = 'assets/icons/invited_icon.svg';
static const String rectangleCheckBox =
'assets/icons/rectangle_check_box.png';
static const String rectangleCheckBox = 'assets/icons/rectangle_check_box.png';
static const String CheckBoxChecked = 'assets/icons/box_checked.png';
static const String emptyBox = 'assets/icons/empty_box.png';
static const String completeProcessIcon =
'assets/icons/compleate_process_icon.svg';
static const String currentProcessIcon =
'assets/icons/current_process_icon.svg';
static const String uncomplete_ProcessIcon =
'assets/icons/uncompleate_process_icon.svg';
static const String completeProcessIcon = 'assets/icons/compleate_process_icon.svg';
static const String currentProcessIcon = 'assets/icons/current_process_icon.svg';
static const String uncomplete_ProcessIcon = 'assets/icons/uncompleate_process_icon.svg';
static const String wrongProcessIcon = 'assets/icons/wrong_process_icon.svg';
static const String arrowForward = 'assets/icons/arrow_forward.svg';
static const String arrowDown = 'assets/icons/arrow_down.svg';
@ -403,17 +352,14 @@ class Assets {
static const String duplicate = 'assets/icons/duplicate.svg';
static const String spaceDelete = 'assets/icons/space_delete.svg';
static const String deleteSpaceLinkIcon =
'assets/icons/delete_space_link_icon.svg';
static const String deleteSpaceLinkIcon = 'assets/icons/delete_space_link_icon.svg';
static const String spaceLinkIcon = 'assets/icons/space_link_icon.svg';
static const String successIcon = 'assets/icons/success_icon.svg';
static const String spaceLocationIcon = 'assets/icons/spaseLocationIcon.svg';
static const String scenesPlayIcon = 'assets/icons/scenesPlayIcon.png';
static const String scenesPlayIconCheck =
'assets/icons/scenesPlayIconCheck.png';
static const String scenesPlayIconCheck = 'assets/icons/scenesPlayIconCheck.png';
static const String presenceStateIcon = 'assets/icons/presence_state.svg';
static const String currentDistanceIcon =
'assets/icons/current_distance_icon.svg';
static const String currentDistanceIcon = 'assets/icons/current_distance_icon.svg';
static const String farDetectionIcon = 'assets/icons/far_detection_icon.svg';
static const String motionDetectionSensitivityIcon =
@ -423,11 +369,11 @@ class Assets {
'assets/icons/motionless_detection_sensitivity_icon.svg';
static const String IndicatorIcon = 'assets/icons/Indicator_icon.svg';
static const String motionDetectionSensitivityValueIcon = 'assets/icons/motion_detection_sensitivity_value_icon.svg';
static const String presenceTimeIcon = 'assets/icons/presence_time_icon.svg';
static const String IlluminanceIcon = 'assets/icons/Illuminance_icon.svg';
static const String gear = 'assets/icons/gear.svg';
static const String activeBell='assets/icons/active_bell.svg';
static const String motionDetectionSensitivityValueIcon =
'assets/icons/motion_detection_sensitivity_value_icon.svg';
static const String presenceTimeIcon = 'assets/icons/presence_time_icon.svg';
static const String IlluminanceIcon = 'assets/icons/Illuminance_icon.svg';
static const String gear = 'assets/icons/gear.svg';
static const String activeBell = 'assets/icons/active_bell.svg';
static const String deviceTagIcon = 'assets/icons/device_tag_ic.svg';
}