Update Automation Status

This commit is contained in:
mohammad
2025-03-19 12:24:55 +03:00
parent 5e05b20f68
commit c0d53fdf5c
12 changed files with 512 additions and 71 deletions

View File

@ -0,0 +1,17 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_6675_32326)">
<path d="M18 3C9.71584 3 3 9.71572 3 18C3 26.2843 9.71584 33 18 33C26.2842 33 33 26.2843 33 18C33 9.71572 26.2842 3 18 3ZM23.1844 18.7951L15.6844 23.4826C15.5326 23.5774 15.3601 23.625 15.1875 23.625C15.0312 23.625 14.8746 23.5861 14.7329 23.5073C14.4349 23.3421 14.25 23.0285 14.25 22.6875V13.3125C14.25 12.9715 14.4349 12.6579 14.7329 12.4927C15.0309 12.3265 15.3953 12.3366 15.6844 12.5174L23.1844 17.2049C23.4584 17.3766 23.625 17.6769 23.625 18C23.625 18.3231 23.4584 18.6235 23.1844 18.7951Z" fill="#F4F4F4"/>
</g>
<defs>
<filter id="filter0_d_6675_32326" x="0" y="0" width="36" height="36" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_6675_32326"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_6675_32326" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,4 @@
<svg width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.13597 13.8173C7.21711 13.9314 7.35373 14 7.5 14C7.64627 14 7.78289 13.9315 7.86403 13.8173C8.89942 12.3614 10.4245 10.5632 11.4872 8.73444C12.3369 7.27221 12.75 6.02509 12.75 4.92188C12.75 2.20795 10.3948 0 7.5 0C4.60515 0 2.25 2.20795 2.25 4.92188C2.25 6.02509 2.66309 7.27221 3.51283 8.73444C4.57476 10.5618 6.10271 12.3644 7.13597 13.8173ZM7.5 0.820312C9.91237 0.820312 11.875 2.66027 11.875 4.92188C11.875 5.88448 11.4968 7.00323 10.7188 8.342C9.80277 9.91834 8.49557 11.5174 7.5 12.8617C6.50457 11.5176 5.19729 9.91843 4.2812 8.342C3.5032 7.00323 3.125 5.88448 3.125 4.92188C3.125 2.66027 5.08763 0.820312 7.5 0.820312Z" fill="#999999"/>
<path d="M7.5 7.38281C8.94742 7.38281 10.125 6.27884 10.125 4.92188C10.125 3.56491 8.94742 2.46094 7.5 2.46094C6.05257 2.46094 4.875 3.56491 4.875 4.92188C4.875 6.27884 6.05257 7.38281 7.5 7.38281ZM7.5 3.28125C8.46495 3.28125 9.25 4.01723 9.25 4.92188C9.25 5.82652 8.46495 6.5625 7.5 6.5625C6.53505 6.5625 5.75 5.82652 5.75 4.92188C5.75 4.01723 6.53505 3.28125 7.5 3.28125Z" fill="#999999"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,124 @@
import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/auth/model/project_model.dart';
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_scene_trigger_event.dart';
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_scene_trigger_status.dart';
import 'package:syncrow_web/pages/routines/models/routine_model.dart';
import 'package:syncrow_web/services/routines_api.dart';
class AutomationSceneTriggerBloc extends Bloc<AutomationSceneTriggerEvent, AutomationSceneTriggerStatus> {
AutomationSceneTriggerBloc() : super(AutomationSceneInitial()) {
// on<LoadScenes>(_onLoadScenes);
// on<LoadAutomation>(_onLoadAutomation);
on<SceneTrigger>(_onSceneTrigger);
on<UpdateAutomationStatus>(_onUpdateAutomationStatus);
}
List<ScenesModel> scenes = [];
List<ScenesModel> automationList = [];
// Future<void> _onLoadScenes(LoadScenes event, Emitter<AutomationSceneTriggerStatus> emit) async {
// emit(SceneLoading());
// try {
// Project? project = HomeCubit.getInstance().project;
// if (event.unitId.isNotEmpty) {
// scenes = await SceneApi.getScenesByUnitId(event.unitId,
// event.unit.community.uuid, project?.uuid ?? TempConst.projectIdDev,
// showInDevice: event.showInDevice);
// emit(SceneLoaded(scenes, automationList));
// } else {
// emit(const SceneError(message: 'Unit ID is empty'));
// }
// } catch (e) {
// emit(const SceneError(message: 'Something went wrong'));
// }
// }
// Future<void> _onLoadAutomation(
// LoadAutomation event, Emitter<AutomationSceneTriggerStatus> emit) async {
// emit(SceneLoading());
// try {
// Project? project = HomeCubit.getInstance().project;
// if (event.unitId.isNotEmpty) {
// automationList = await SceneApi.getAutomationByUnitId(
// event.unitId, event.communityId, project?.uuid ?? '');
// emit(SceneLoaded(scenes, automationList));
// } else {
// emit(const SceneError(message: 'Unit ID is empty'));
// }
// } catch (e) {
// emit(const SceneError(message: 'Something went wrong'));
// }
// }
Future<void> _onSceneTrigger(
SceneTrigger event, Emitter<AutomationSceneTriggerStatus> emit) async {
final currentState = state;
if (currentState is AutomationSceneLoaded) {
emit(AutomationSceneLoaded(
currentState.scenes,
currentState.automationList,
loadingSceneId: event.sceneId,
));
try {
final success = await SceneApi.triggerScene(event.sceneId);
if (success) {
emit(SceneTriggerSuccess(event.name));
emit(AutomationSceneLoaded(currentState.scenes, currentState.automationList));
} else {
emit(const AutomationSceneError(message: 'Something went wrong'));
}
} catch (e) {
emit(const AutomationSceneError(message: 'Something went wrong'));
}
}
}
Future<void> _onUpdateAutomationStatus(
UpdateAutomationStatus event, Emitter<AutomationSceneTriggerStatus> emit) async {
final currentState = state;
if (currentState is AutomationSceneLoaded) {
final newLoadingStates =
Map<String, bool>.from(currentState.loadingStates)
..[event.automationId] = true;
emit(AutomationSceneLoaded(
currentState.scenes,
currentState.automationList,
loadingStates: newLoadingStates,
));
try {
Project? project = HomeCubit.getInstance().project;
final success = await SceneApi.updateAutomationStatus(
event.automationId,
event.automationStatusUpdate,
project?.uuid ?? '');
if (success) {
automationList = await SceneApi.getAutomationByUnitId(
event.automationStatusUpdate.spaceUuid,
event.communityId,
project?.uuid ?? '');
newLoadingStates[event.automationId] = false;
emit(AutomationSceneLoaded(
currentState.scenes,
automationList,
loadingStates: newLoadingStates,
));
} else {
emit(const AutomationSceneError(message: 'Something went wrong'));
}
} catch (e) {
emit(const AutomationSceneError(message: 'Something went wrong'));
}
}
}
}

View File

@ -0,0 +1,55 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_status_update.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
abstract class AutomationSceneTriggerEvent extends Equatable {
const AutomationSceneTriggerEvent();
@override
List<Object> get props => [];
}
class AutomationSceneScenes extends AutomationSceneTriggerEvent {
final String unitId;
final bool showInDevice;
final SpaceModel unit;
const AutomationSceneScenes(this.unitId, this.unit, {this.showInDevice = false});
@override
List<Object> get props => [unitId, showInDevice];
}
class AutomationSceneAutomation extends AutomationSceneTriggerEvent {
final String unitId;
final String communityId;
const AutomationSceneAutomation(this.unitId, this.communityId);
@override
List<Object> get props => [unitId, communityId];
}
class SceneTrigger extends AutomationSceneTriggerEvent {
final String sceneId;
final String name;
const SceneTrigger(this.sceneId, this.name);
@override
List<Object> get props => [sceneId];
}
//updateAutomationStatus
class UpdateAutomationStatus extends AutomationSceneTriggerEvent {
final String automationId;
final AutomationStatusUpdate automationStatusUpdate;
final String communityId;
const UpdateAutomationStatus({required this.automationStatusUpdate, required this.automationId, required this.communityId});
@override
List<Object> get props => [automationStatusUpdate];
}

View File

@ -0,0 +1,51 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/routines/models/routine_model.dart';
abstract class AutomationSceneTriggerStatus extends Equatable {
const AutomationSceneTriggerStatus();
@override
List<Object?> get props => [];
}
class AutomationSceneInitial extends AutomationSceneTriggerStatus {}
class AutomationSceneLoading extends AutomationSceneTriggerStatus {}
class AutomationSceneLoaded extends AutomationSceneTriggerStatus {
final List<ScenesModel> scenes;
final List<ScenesModel> automationList;
final String? loadingSceneId;
final Map<String, bool> loadingStates;
const AutomationSceneLoaded(this.scenes, this.automationList,
{this.loadingSceneId, this.loadingStates = const {}});
@override
List<Object?> get props =>
[scenes, loadingSceneId, automationList, loadingStates];
}
class AutomationSceneError extends AutomationSceneTriggerStatus {
final String message;
const AutomationSceneError({required this.message});
@override
List<Object> get props => [message];
}
class SceneTriggerSuccess extends AutomationSceneTriggerStatus {
final String sceneName;
const SceneTriggerSuccess(this.sceneName);
@override
List<Object> get props => [sceneName];
}
class UpdateAutomationStatusLoading extends AutomationSceneTriggerStatus {
const UpdateAutomationStatusLoading();
}

View File

@ -0,0 +1,39 @@
import 'dart:convert';
class AutomationStatusUpdate {
final String spaceUuid;
final bool isEnable;
AutomationStatusUpdate({
required this.spaceUuid,
required this.isEnable,
});
factory AutomationStatusUpdate.fromRawJson(String str) =>
AutomationStatusUpdate.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory AutomationStatusUpdate.fromJson(Map<String, dynamic> json) =>
AutomationStatusUpdate(
spaceUuid: json["spaceUuid"],
isEnable: json["isEnable"],
);
Map<String, dynamic> toJson() => {
"spaceUuid": spaceUuid,
"isEnable": isEnable,
};
factory AutomationStatusUpdate.fromMap(Map<String, dynamic> map) =>
AutomationStatusUpdate(
spaceUuid: map["spaceUuid"],
isEnable: map["isEnable"],
);
Map<String, dynamic> toMap() => {
"spaceUuid": spaceUuid,
"isEnable": isEnable,
};
}

View File

@ -60,22 +60,39 @@ class _RoutinesViewState extends State<RoutinesView> {
height: 10,
),
RoutineViewCard(
isFromScenes: false,
cardType: '',
spaceName: '',
onTap: () {
if (context.read<SpaceTreeBloc>().state.selectedCommunities.length == 1 &&
context.read<SpaceTreeBloc>().state.selectedSpaces.length == 1) {
if (context
.read<SpaceTreeBloc>()
.state
.selectedCommunities
.length ==
1 &&
context
.read<SpaceTreeBloc>()
.state
.selectedSpaces
.length ==
1) {
context.read<RoutineBloc>().add(
(ResetRoutineState()),
);
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(createRoutineView: true),
const CreateNewRoutineViewEvent(
createRoutineView: true),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
context.read<SpaceTreeBloc>().state.selectedSpaces.isEmpty
? 'Please select a space'
: 'Please select only one space to proceed'),
content: Text(context
.read<SpaceTreeBloc>()
.state
.selectedSpaces
.isEmpty
? 'Please select a space'
: 'Please select only one space to proceed'),
),
);
// CustomSnackBar.redSnackBar(

View File

@ -12,7 +12,8 @@ class FetchRoutineScenesAutomation extends StatefulWidget {
const FetchRoutineScenesAutomation({super.key});
@override
State<FetchRoutineScenesAutomation> createState() => _FetchRoutineScenesState();
State<FetchRoutineScenesAutomation> createState() =>
_FetchRoutineScenesState();
}
class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
@ -64,9 +65,12 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: RoutineViewCard(
cardType: 'scenes',
spaceName: 'scenes',
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(createRoutineView: true),
const CreateNewRoutineViewEvent(
createRoutineView: true),
);
context.read<RoutineBloc>().add(
GetSceneDetails(
@ -77,7 +81,8 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
);
},
textString: state.scenes[index].name,
icon: state.scenes[index].icon ?? Assets.logoHorizontal,
icon: state.scenes[index].icon ??
Assets.logoHorizontal,
isFromScenes: true,
iconInBytes: state.scenes[index].iconInBytes,
),
@ -113,19 +118,24 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: RoutineViewCard(
cardType: 'automations',
spaceName: 'automations',
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(createRoutineView: true),
const CreateNewRoutineViewEvent(
createRoutineView: true),
);
context.read<RoutineBloc>().add(
GetAutomationDetails(
automationId: state.automations[index].id,
automationId:
state.automations[index].id,
isAutomation: true,
isUpdate: true),
);
},
textString: state.automations[index].name,
icon: state.automations[index].icon ?? Assets.automation,
icon: state.automations[index].icon ??
Assets.automation,
),
),
),

View File

@ -1,3 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
@ -12,6 +13,8 @@ class RoutineViewCard extends StatelessWidget with HelperResponsiveLayout {
required this.onTap,
required this.icon,
required this.textString,
required this.spaceName,
required this.cardType,
this.isFromScenes,
this.iconInBytes,
});
@ -19,6 +22,9 @@ class RoutineViewCard extends StatelessWidget with HelperResponsiveLayout {
final Function() onTap;
final dynamic icon;
final String textString;
final String spaceName;
final String cardType;
final bool? isFromScenes;
final Uint8List? iconInBytes;
@ -50,69 +56,125 @@ class RoutineViewCard extends StatelessWidget with HelperResponsiveLayout {
),
color: ColorsManager.whiteColors,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(10),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: Container(
decoration: BoxDecoration(
color: ColorsManager.graysColor,
borderRadius: BorderRadius.circular(120),
border: Border.all(
color: ColorsManager.greyColor,
width: 2.0,
),
),
height: iconSize,
width: iconSize,
child: (isFromScenes ?? false)
? (iconInBytes != null && iconInBytes?.isNotEmpty == true)
? Image.memory(
iconInBytes!,
height: iconSize,
width: iconSize,
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) => Image.asset(
Assets.logo,
height: iconSize,
width: iconSize,
fit: BoxFit.contain,
),
)
: Image.asset(
Assets.logo,
height: iconSize,
width: iconSize,
fit: BoxFit.contain,
)
: (icon is String && icon.endsWith('.svg'))
? SvgPicture.asset(
icon,
fit: BoxFit.contain,
)
: Icon(
icon,
color: ColorsManager.dialogBlueTitle,
size: isSmallScreenSize(context) ? 30 : 40,
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: [
(isFromScenes ?? false)
? InkWell(
onTap: () {},
child: SvgPicture.asset(
Assets.scenesPlayIcon,
fit: BoxFit.contain,
),
)
: CupertinoSwitch(
activeColor: ColorsManager.primaryColor,
value: false,
onChanged: (value) {},
)
],
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 3),
child: Text(
textString,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize: isSmallScreenSize(context) ? 10 : 12,
),
InkWell(
onTap: onTap,
child: Column(
children: [
Center(
child: Container(
decoration: BoxDecoration(
color: ColorsManager.graysColor,
borderRadius: BorderRadius.circular(120),
border: Border.all(
color: ColorsManager.greyColor,
width: 2.0,
),
),
height: iconSize,
width: iconSize,
child: (isFromScenes ?? false)
? (iconInBytes != null &&
iconInBytes?.isNotEmpty == true)
? Image.memory(
iconInBytes!,
height: iconSize,
width: iconSize,
fit: BoxFit.contain,
errorBuilder:
(context, error, stackTrace) =>
Image.asset(
Assets.logo,
height: iconSize,
width: iconSize,
fit: BoxFit.contain,
),
)
: Image.asset(
Assets.logo,
height: iconSize,
width: iconSize,
fit: BoxFit.contain,
)
: (icon is String && icon.endsWith('.svg'))
? SvgPicture.asset(
icon,
fit: BoxFit.contain,
)
: Icon(
icon,
color: ColorsManager.dialogBlueTitle,
size:
isSmallScreenSize(context) ? 30 : 40,
),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 3),
child: Column(
children: [
Text(
textString,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize: isSmallScreenSize(context) ? 10 : 12,
),
),
if (spaceName != '')
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.spaceLocationIcon,
fit: BoxFit.contain,
),
Text(
spaceName,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style:
context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize:
isSmallScreenSize(context) ? 10 : 12,
),
),
],
),
],
),
),
],
),
),
],

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/routines/bloc/automation_scene_trigger_bloc/automation_status_update.dart';
import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_scene_model.dart';
import 'package:syncrow_web/pages/routines/models/icon_model.dart';
@ -230,4 +231,59 @@ class SceneApi {
rethrow;
}
}
static Future<bool> updateAutomationStatus(String automationId,
AutomationStatusUpdate createAutomationEnable, String projectId) async {
try {
final response = await _httpService.patch(
path: ApiEndpoints.updateAutomationStatus
.replaceAll('{automationId}', automationId)
.replaceAll('{projectId}', projectId),
body: createAutomationEnable.toMap(),
expectedResponseModel: (json) => json['success'],
);
return response;
} catch (e) {
rethrow;
}
}
static Future<bool> triggerScene(String sceneId) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.triggerScene.replaceAll('{sceneId}', sceneId),
showServerMessage: false,
expectedResponseModel: (json) => json['success'],
);
return response;
} catch (e) {
rethrow;
}
}
static Future<List<ScenesModel>> getAutomationByUnitId(
String unitId,
String communityId,
String projectId,
) async {
try {
final response = await _httpService.get(
path: ApiEndpoints.getUnitAutomation
.replaceAll('{unitUuid}', unitId)
.replaceAll('{communityId}', communityId)
.replaceAll('{projectId}', projectId),
showServerMessage: false,
expectedResponseModel: (json) {
List<ScenesModel> scenes = [];
for (var scene in json) {
scenes.add(ScenesModel.fromJson(scene));
}
return scenes;
},
);
return response;
} catch (e) {
rethrow;
}
}
}

View File

@ -111,4 +111,9 @@ abstract class ApiEndpoints {
static const String terms = '/terms';
static const String policy = '/policy';
static const String userAgreements = '/user/agreements/web/{userUuid}';
static const String triggerScene = '/scene/tap-to-run/{sceneId}/trigger';
static const String updateAutomationStatus =
'/projects/{projectId}/automations/{automationId}';
static const String getUnitAutomation =
'/projects/{projectId}/communities/{communityId}/spaces/{unitUuid}/automations';
}

View File

@ -406,6 +406,7 @@ class Assets {
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 successIcon = 'assets/icons/success_icon.svg';
static const String spaceLocationIcon = 'assets/icons/spaceLocationIcon.svg';
static const String scenesPlayIcon = 'assets/icons/scenesPlayIcon.svg';
}