Compare commits

..

44 Commits

Author SHA1 Message Date
132cafcaa2 Enhanced the side tree design 2025-02-05 11:52:44 +03:00
572520eed5 Fixed issues 2025-02-04 01:54:18 +03:00
506531e16a Fetch devices based on selection 2025-02-03 11:15:36 +03:00
5ae07688cb fixed issues in the space tree 2025-02-02 02:46:13 +03:00
6e546a4831 Merged with dev 2025-01-30 12:17:06 +03:00
b098202fd8 Merge pull request #74 from SyncrowIOT/bugfix/subspace-name-validatio
add subspace validation
2025-01-30 13:13:10 +04:00
a294988558 add subspace and space information 2025-01-30 10:09:21 +04:00
43c17d1c18 Implemented the selection behavior of the side tree 2025-01-30 04:03:54 +03:00
09c1a785e5 add subspace validation 2025-01-29 22:03:56 +04:00
e70b9ea9e2 Merge pull request #73 from SyncrowIOT/bugfix/edit-space
Bugfix/edit space
2025-01-29 12:46:19 +04:00
c173df934d added all tags 2025-01-29 12:24:12 +04:00
e4262d08a5 fixed ok button 2025-01-29 11:40:53 +04:00
4bae7bb9fb fixed update space 2025-01-29 11:14:12 +04:00
073916d4ac updated text theme 2025-01-29 10:21:06 +04:00
8870467fe4 fixed the edit flow of space 2025-01-29 09:56:10 +04:00
9331193e90 added tags and subspace to update space 2025-01-29 09:55:36 +04:00
45b0b67fe0 make counter widget work only for add device 2025-01-29 09:53:06 +04:00
9e0184f19d Merge pull request #72 from SyncrowIOT/bugfix/edit-subspace
Bugfix/edit-subspace
2025-01-28 13:30:03 +04:00
9091af2661 fixed text style 2025-01-28 13:27:05 +04:00
4b7f4d4279 fixed calculating products on add another device 2025-01-28 11:39:34 +04:00
e61cfd7e49 added option to update subspace 2025-01-28 10:48:14 +04:00
788fb75a68 fixed UI alignment 2025-01-27 18:12:57 +04:00
ea5b6597f5 Merge pull request #71 from SyncrowIOT/feat/update-create-edit-space
Feat/update-create-edit-space
2025-01-27 17:02:01 +04:00
c72297e0c8 updated the duplicate 2025-01-27 14:21:07 +04:00
d0c6b13072 updated edit flow 2025-01-27 01:25:04 +04:00
812dc4792b fixed duplicate 2025-01-27 01:19:08 +04:00
4907eebc42 added duplicate 2025-01-27 00:33:50 +04:00
2221d9ae7b Merged with dev 2025-01-26 21:00:05 +03:00
9167c8da29 fixed space model updates 2025-01-25 01:29:21 +04:00
4258ccdfbd fix header issue 2025-01-24 20:43:45 +04:00
cb71b51565 community header 2025-01-24 20:41:31 +04:00
d4ed4efcd8 validation fix 2025-01-23 17:48:11 +04:00
dac045146e duplicate name validation 2025-01-23 17:02:32 +04:00
5563197e9d add validation 2025-01-23 15:20:36 +04:00
7268253e35 fixed button validation 2025-01-23 11:45:11 +04:00
921d352207 Merge pull request #70 from SyncrowIOT/chore/remove-unsupported-param
Chore/remove unsupported param
2025-01-23 10:46:23 +04:00
24f7ab6af8 changed subspace label 2025-01-23 10:18:20 +04:00
2fb6f30ccb Updated pubsepc file 2025-01-23 01:21:13 +03:00
508d8bbaa8 Merged with dev 2025-01-23 01:18:30 +03:00
65d00c923a updated tag issue for subspace 2025-01-23 00:02:28 +04:00
5060d2a66d Updated side tree branch 2025-01-21 15:28:59 +03:00
540f569b1f Added spaces devices 2025-01-05 00:21:10 +03:00
a98f7e77a3 Implemented side tree to devices and rountines screen 2025-01-04 17:45:15 +03:00
0341844ea9 SP-859 2024-12-26 12:25:37 +03:00
141 changed files with 4635 additions and 2595 deletions

View File

@ -0,0 +1,16 @@
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.9807 2.67199L16.6413 0.219727H7.91051C7.43122 0.219727 7.04266 0.608281 7.04266 1.08758V16.605C7.04266 17.0843 7.43122 17.4729 7.91051 17.4729H18.2257C18.705 17.4729 19.0935 17.0843 19.0935 16.605V16.5459H19.7312V3.8127L17.9807 2.67199Z" fill="#60B7FF"/>
<path d="M18.1513 6.23445H9.12553C8.95881 6.23445 8.82373 6.09934 8.82373 5.93266C8.82373 5.76598 8.95885 5.63086 9.12553 5.63086H18.1513C18.318 5.63086 18.4531 5.76598 18.4531 5.93266C18.4531 6.09934 18.318 6.23445 18.1513 6.23445Z" fill="#0055A3"/>
<path d="M18.1513 8.54891H9.12553C8.95881 8.54891 8.82373 8.41379 8.82373 8.24711C8.82373 8.08043 8.95885 7.94531 9.12553 7.94531H18.1513C18.318 7.94531 18.4531 8.08043 18.4531 8.24711C18.4531 8.41379 18.318 8.54891 18.1513 8.54891Z" fill="#0055A3"/>
<path d="M18.1513 10.8634H9.12553C8.95881 10.8634 8.82373 10.7282 8.82373 10.5616C8.82373 10.3949 8.95885 10.2598 9.12553 10.2598H18.1513C18.318 10.2598 18.4531 10.3949 18.4531 10.5616C18.4531 10.7282 18.318 10.8634 18.1513 10.8634Z" fill="#0055A3"/>
<path d="M18.1513 13.1778H9.12556C8.95884 13.1778 8.82376 13.0427 8.82376 12.876C8.82376 12.7093 8.95888 12.5742 9.12556 12.5742H18.1513C18.3181 12.5742 18.4531 12.7093 18.4531 12.876C18.4531 13.0427 18.3181 13.1778 18.1513 13.1778Z" fill="#0055A3"/>
<path d="M19.0935 3.39648V16.6044C19.0935 17.0837 18.7049 17.4722 18.2256 17.4722H19.3663C19.8456 17.4722 20.2342 17.0837 20.2342 16.6044V3.81203L19.0935 3.39648Z" fill="#26A6FE"/>
<path d="M17.5091 3.8127H20.2342L16.6413 0.219727V2.94484C16.6413 3.42414 17.0298 3.8127 17.5091 3.8127Z" fill="#004281"/>
<path d="M11.4172 19.7805C11.8965 19.7805 12.2851 19.392 12.2851 18.9127V18.8937H12.8297V6.12031L11.039 4.78906L9.83279 2.52734H1.10204C0.622747 2.52734 0.234192 2.9159 0.234192 3.3952V18.9127C0.234192 19.392 0.622747 19.7805 1.10204 19.7805H11.4172Z" fill="#D5EDFF"/>
<path d="M12.285 4.97852V6.11922V18.9116C12.285 19.3909 11.8964 19.7794 11.4171 19.7794H12.5578C13.0371 19.7794 13.4257 19.3909 13.4257 18.9116V6.11922L12.285 4.97852Z" fill="#D8ECFE"/>
<path d="M10.7006 6.12031H13.4258L9.83279 2.52734V5.25246C9.83276 5.73176 10.2213 6.12031 10.7006 6.12031Z" fill="#B3DAFE"/>
<path d="M11.3429 8.54891H2.31709C2.15037 8.54891 2.01529 8.41379 2.01529 8.24711C2.01529 8.08043 2.15041 7.94531 2.31709 7.94531H11.3429C11.5096 7.94531 11.6447 8.08043 11.6447 8.24711C11.6447 8.41379 11.5096 8.54891 11.3429 8.54891Z" fill="#82AEE3"/>
<path d="M11.3428 10.8634H2.31706C2.15034 10.8634 2.01526 10.7282 2.01526 10.5616C2.01526 10.3949 2.15038 10.2598 2.31706 10.2598H11.3428C11.5096 10.2598 11.6446 10.3949 11.6446 10.5616C11.6446 10.7282 11.5096 10.8634 11.3428 10.8634Z" fill="#82AEE3"/>
<path d="M11.3428 13.1778H2.31706C2.15034 13.1778 2.01526 13.0427 2.01526 12.876C2.01526 12.7093 2.15038 12.5742 2.31706 12.5742H11.3428C11.5096 12.5742 11.6446 12.7093 11.6446 12.876C11.6446 13.0427 11.5096 13.1778 11.3428 13.1778Z" fill="#82AEE3"/>
<path d="M11.3428 15.4923H2.31706C2.15034 15.4923 2.01526 15.3571 2.01526 15.1905C2.01526 15.0238 2.15038 14.8887 2.31706 14.8887H11.3428C11.5096 14.8887 11.6446 15.0238 11.6446 15.1905C11.6447 15.3571 11.5096 15.4923 11.3428 15.4923Z" fill="#82AEE3"/>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,22 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_4618_3290)">
<path d="M18.7967 7.35156V19.8515C18.7967 21.0362 17.8329 22 16.6482 22H4.14825C2.9636 22 1.99982 21.0362 1.99982 19.8515V7.35156C1.99982 6.16691 2.9636 5.20312 4.14825 5.20312H16.6482C17.8329 5.20312 18.7967 6.16691 18.7967 7.35156Z" fill="#EDF2F2"/>
<path d="M18.7967 19.8515C18.7967 21.0361 17.8329 21.9999 16.6482 21.9999H4.14825C3.55591 21.9999 3.0188 21.7589 2.62978 21.3699L18.1667 5.83301C18.5557 6.22203 18.7967 6.75914 18.7967 7.35148V19.8515Z" fill="#C9DCDC"/>
<path d="M9.28417 14.7153C9.12722 14.5583 9.07241 14.3262 9.14265 14.1157L9.97128 11.6297C10 11.5434 10.0485 11.465 10.1128 11.4007L17.8468 3.66674C18.0756 3.43791 18.4466 3.43791 18.6754 3.66674L20.3327 5.324C20.5615 5.55283 20.5615 5.9238 20.3327 6.15263L12.5987 13.8866C12.5344 13.9509 12.456 13.9994 12.3697 14.0281L9.88374 14.8567C9.67323 14.927 9.44109 14.8722 9.28417 14.7153Z" fill="#4998EE"/>
<path d="M19.504 4.49512L9.28413 14.715C9.44105 14.8719 9.6732 14.9267 9.88374 14.8565L12.3697 14.0279C12.456 13.9992 12.5344 13.9507 12.5987 13.8864L20.3327 6.15242C20.5615 5.92359 20.5615 5.55261 20.3327 5.32379L19.504 4.49512Z" fill="#176EDE"/>
<path d="M20.3327 6.15305L17.8467 3.66711L19.2278 2.28602C19.6092 1.90466 20.2275 1.90466 20.6089 2.28602L21.7137 3.39087C22.0951 3.77223 22.0951 4.39055 21.7137 4.77192L20.3327 6.15305Z" fill="#FFE137"/>
<path d="M21.1613 2.83789L19.0897 4.90949L20.3327 6.15245L21.7138 4.77136C22.0951 4.39 22.0951 3.77168 21.7138 3.39031L21.1613 2.83789Z" fill="#FAC814"/>
</g>
<defs>
<filter id="filter0_d_4618_3290" x="-0.000183105" y="0" width="24" height="24" 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"/>
<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.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_4618_3290"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_4618_3290" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,9 @@
<svg width="16" height="20" viewBox="0 0 16 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.8069 19.9998H3.50035C2.16411 19.9998 1.08087 18.9165 1.08087 17.5804V4.51562H14.2262V17.5804C14.2262 18.9165 13.1432 19.9998 11.8069 19.9998Z" fill="#D8D8D8"/>
<path d="M1.08087 4.51562V5.48335H12.3715C12.8168 5.48335 13.1779 5.84438 13.1779 6.2898V17.3384C13.1779 18.2292 12.4557 18.9513 11.5649 18.9513H2.4519C2.05409 18.9513 1.67887 18.8547 1.34775 18.6844C1.74907 19.4652 2.56207 19.9998 3.50035 19.9998H11.8069C13.1432 19.9998 14.2262 18.9165 14.2262 17.5803V4.51562H1.08087Z" fill="#BABABA"/>
<path d="M14.2667 1.77417H10.7439L10.1633 0.612956C9.9742 0.234837 9.5941 0 9.17142 0H6.13594C5.71311 0 5.33316 0.234837 5.1441 0.612956L4.56349 1.77417H1.04063C0.595221 1.77417 0.234192 2.1352 0.234192 2.58061V3.70978C0.234192 4.15504 0.595221 4.51622 1.04063 4.51622H14.2667C14.7121 4.51622 15.0732 4.15504 15.0732 3.70978V2.58077C15.0732 2.1352 14.7121 1.77417 14.2667 1.77417ZM5.68503 0.8835C5.77094 0.71153 5.94368 0.604869 6.13594 0.604869H9.17142C9.36354 0.604869 9.53627 0.71153 9.62218 0.8835L10.0676 1.77417H5.23977L5.68503 0.8835Z" fill="#757575"/>
<path d="M14.2668 1.77441H12.9763C13.4217 1.77441 13.7829 2.13544 13.7829 2.58086V3.71003C13.7829 4.15529 13.4217 4.51647 12.9763 4.51647H14.2668C14.7122 4.51647 15.0732 4.15529 15.0732 3.71003V2.58101C15.0732 2.13544 14.7122 1.77441 14.2668 1.77441Z" fill="#595959"/>
<path d="M11.3634 17.5C10.918 17.5 10.5569 17.139 10.5569 16.6935V9.15312C10.5569 8.70771 10.918 8.34668 11.3634 8.34668C11.8088 8.34668 12.1698 8.70771 12.1698 9.15312V16.6935C12.1698 17.139 11.8088 17.5 11.3634 17.5Z" fill="#757575"/>
<path d="M3.94398 17.5C4.38924 17.5 4.75043 17.139 4.75043 16.6935V9.15312C4.75043 8.70771 4.38924 8.34668 3.94398 8.34668C3.49857 8.34668 3.13739 8.70771 3.13739 9.15312V16.6935C3.13739 17.139 3.49857 17.5 3.94398 17.5Z" fill="#757575"/>
<path d="M7.65361 17.5C7.2082 17.5 6.84717 17.139 6.84717 16.6935V9.15312C6.84717 8.70771 7.2082 8.34668 7.65361 8.34668C8.09902 8.34668 8.46005 8.70771 8.46005 9.15312V16.6935C8.46005 17.139 8.09902 17.5 7.65361 17.5Z" fill="#757575"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

39
lib/common/edit_chip.dart Normal file
View File

@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class EditChip extends StatelessWidget {
final String label;
final VoidCallback onTap;
final Color labelColor;
final Color backgroundColor;
final Color borderColor;
final double borderRadius;
const EditChip({
Key? key,
this.label = 'Edit',
required this.onTap,
this.labelColor = ColorsManager.spaceColor,
this.backgroundColor = ColorsManager.whiteColors,
this.borderColor = ColorsManager.spaceColor,
this.borderRadius = 16.0,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Chip(
label: Text(
label,
style: Theme.of(context).textTheme.bodySmall!.copyWith(color: labelColor)
),
backgroundColor: backgroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(borderRadius),
side: BorderSide(color: borderColor),
),
),
);
}
}

View File

@ -46,14 +46,13 @@ class CustomSearchBar extends StatelessWidget {
filled: true, filled: true,
fillColor: ColorsManager.textFieldGreyColor, fillColor: ColorsManager.textFieldGreyColor,
hintText: hintText, hintText: hintText,
hintStyle: TextStyle( hintStyle: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: Color(0xB2999999), color: ColorsManager.lightGrayColor,
fontSize: 12, fontSize: 12,
fontFamily: 'Aftika', fontWeight: FontWeight.w400,
fontWeight: FontWeight.w400, height: 0,
height: 0, letterSpacing: -0.24,
letterSpacing: -0.24, ),
),
suffixIcon: Padding( suffixIcon: Padding(
padding: const EdgeInsets.only(right: 16), padding: const EdgeInsets.only(right: 16),
child: SvgPicture.asset( child: SvgPicture.asset(

View File

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
class SpacesSideTree extends StatefulWidget {
final List<CommunityModel> communities;
final String? selectedSpaceUuid;
const SpacesSideTree({
super.key,
required this.communities,
this.selectedSpaceUuid,
});
@override
State<SpacesSideTree> createState() => _SpacesSideTreeState();
}
class _SpacesSideTreeState extends State<SpacesSideTree> {
String _searchQuery = '';
String? _selectedSpaceUuid;
String? _selectedId;
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}

View File

@ -6,7 +6,9 @@ import 'package:go_router/go_router.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart'; import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart'; import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/services/locator.dart'; import 'package:syncrow_web/services/locator.dart';
import 'package:syncrow_web/utils/app_routes.dart'; import 'package:syncrow_web/utils/app_routes.dart';
@ -15,8 +17,7 @@ import 'package:syncrow_web/utils/theme/theme.dart';
Future<void> main() async { Future<void> main() async {
try { try {
const environment = const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development');
String.fromEnvironment('FLAVOR', defaultValue: 'development');
await dotenv.load(fileName: '.env.$environment'); await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
initialSetup(); initialSetup();
@ -48,14 +49,16 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MultiBlocProvider( return MultiBlocProvider(
providers: [ providers: [
BlocProvider( BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>( BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(), create: (context) => VisitorPasswordBloc(),
), ),
BlocProvider<RoutineBloc>( BlocProvider<RoutineBloc>(
create: (context) => RoutineBloc(), create: (context) => RoutineBloc(),
), ),
BlocProvider<SpaceTreeBloc>(
create: (context) => SpaceTreeBloc()..add(InitialEvent()),
),
], ],
child: MaterialApp.router( child: MaterialApp.router(
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,

View File

@ -19,12 +19,14 @@ class DefaultButton extends StatelessWidget {
this.padding, this.padding,
this.borderColor, this.borderColor,
this.elevation, this.elevation,
this.borderWidth = 1.0,
}); });
final void Function()? onPressed; final void Function()? onPressed;
final Widget child; final Widget child;
final double? height; final double? height;
final bool isSecondary; final bool isSecondary;
final double? borderRadius; final double? borderRadius;
final double borderWidth;
final bool enabled; final bool enabled;
final double? padding; final double? padding;
final bool isDone; final bool isDone;
@ -66,13 +68,16 @@ class DefaultButton extends StatelessWidget {
}), }),
shape: WidgetStateProperty.all( shape: WidgetStateProperty.all(
RoundedRectangleBorder( RoundedRectangleBorder(
side: BorderSide(color: borderColor ?? Colors.transparent), side: BorderSide(
color: borderColor ?? Colors.transparent,
width: borderWidth,
),
borderRadius: BorderRadius.circular(borderRadius ?? 20), borderRadius: BorderRadius.circular(borderRadius ?? 20),
), ),
), ),
fixedSize: height != null fixedSize: height != null
? WidgetStateProperty.all(Size.fromHeight(height!)) ? WidgetStateProperty.all(Size.fromHeight(height!))
: null, : null,
padding: WidgetStateProperty.all( padding: WidgetStateProperty.all(
EdgeInsets.all(padding ?? 10), EdgeInsets.all(padding ?? 10),
), ),

View File

@ -1,13 +1,14 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/services/devices_mang_api.dart'; import 'package:syncrow_web/services/devices_mang_api.dart';
part 'device_managment_event.dart'; part 'device_managment_event.dart';
part 'device_managment_state.dart'; part 'device_managment_state.dart';
class DeviceManagementBloc class DeviceManagementBloc extends Bloc<DeviceManagementEvent, DeviceManagementState> {
extends Bloc<DeviceManagementEvent, DeviceManagementState> {
int _selectedIndex = 0; int _selectedIndex = 0;
List<AllDevicesModel> _devices = []; List<AllDevicesModel> _devices = [];
int _onlineCount = 0; int _onlineCount = 0;
@ -30,11 +31,23 @@ class DeviceManagementBloc
on<UpdateSelection>(_onUpdateSelection); on<UpdateSelection>(_onUpdateSelection);
} }
Future<void> _onFetchDevices( Future<void> _onFetchDevices(FetchDevices event, Emitter<DeviceManagementState> emit) async {
FetchDevices event, Emitter<DeviceManagementState> emit) async {
emit(DeviceManagementLoading()); emit(DeviceManagementLoading());
try { try {
final devices = await DevicesManagementApi().fetchDevices(); List<AllDevicesModel> devices = [];
_devices.clear();
var spaceBloc = event.context.read<SpaceTreeBloc>();
if (spaceBloc.state.selectedCommunities.isEmpty) {
devices = await DevicesManagementApi().fetchDevices('', '');
} else {
for (var community in spaceBloc.state.selectedCommunities) {
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[community] ?? [];
for (var space in spacesList) {
devices.addAll(await DevicesManagementApi().fetchDevices(community, space));
}
}
}
_selectedDevices.clear(); _selectedDevices.clear();
_devices = devices; _devices = devices;
_filteredDevices = devices; _filteredDevices = devices;
@ -53,8 +66,7 @@ class DeviceManagementBloc
} }
} }
void _onFilterDevices( void _onFilterDevices(FilterDevices event, Emitter<DeviceManagementState> emit) async {
FilterDevices event, Emitter<DeviceManagementState> emit) async {
if (_devices.isNotEmpty) { if (_devices.isNotEmpty) {
_filteredDevices = List.from(_devices.where((device) { _filteredDevices = List.from(_devices.where((device) {
switch (event.filter) { switch (event.filter) {
@ -85,8 +97,7 @@ class DeviceManagementBloc
} }
} }
Future<void> _onResetFilters( Future<void> _onResetFilters(ResetFilters event, Emitter<DeviceManagementState> emit) async {
ResetFilters event, Emitter<DeviceManagementState> emit) async {
currentProductName = ''; currentProductName = '';
_selectedDevices.clear(); _selectedDevices.clear();
_filteredDevices = List.from(_devices); _filteredDevices = List.from(_devices);
@ -102,8 +113,7 @@ class DeviceManagementBloc
)); ));
} }
void _onResetSelectedDevices( void _onResetSelectedDevices(ResetSelectedDevices event, Emitter<DeviceManagementState> emit) {
ResetSelectedDevices event, Emitter<DeviceManagementState> emit) {
_selectedDevices.clear(); _selectedDevices.clear();
if (state is DeviceManagementLoaded) { if (state is DeviceManagementLoaded) {
@ -129,14 +139,12 @@ class DeviceManagementBloc
} }
} }
void _onSelectedFilterChanged( void _onSelectedFilterChanged(SelectedFilterChanged event, Emitter<DeviceManagementState> emit) {
SelectedFilterChanged event, Emitter<DeviceManagementState> emit) {
_selectedIndex = event.selectedIndex; _selectedIndex = event.selectedIndex;
add(FilterDevices(_getFilterFromIndex(_selectedIndex))); add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
} }
void _onSelectDevice( void _onSelectDevice(SelectDevice event, Emitter<DeviceManagementState> emit) {
SelectDevice event, Emitter<DeviceManagementState> emit) {
final selectedUuid = event.selectedDevice.uuid; final selectedUuid = event.selectedDevice.uuid;
if (_selectedDevices.any((device) => device.uuid == selectedUuid)) { if (_selectedDevices.any((device) => device.uuid == selectedUuid)) {
@ -147,8 +155,7 @@ class DeviceManagementBloc
List<AllDevicesModel> clonedSelectedDevices = List.from(_selectedDevices); List<AllDevicesModel> clonedSelectedDevices = List.from(_selectedDevices);
bool isControlButtonEnabled = bool isControlButtonEnabled = _checkIfControlButtonEnabled(clonedSelectedDevices);
_checkIfControlButtonEnabled(clonedSelectedDevices);
if (state is DeviceManagementLoaded) { if (state is DeviceManagementLoaded) {
emit(DeviceManagementLoaded( emit(DeviceManagementLoaded(
@ -157,8 +164,7 @@ class DeviceManagementBloc
onlineCount: _onlineCount, onlineCount: _onlineCount,
offlineCount: _offlineCount, offlineCount: _offlineCount,
lowBatteryCount: _lowBatteryCount, lowBatteryCount: _lowBatteryCount,
selectedDevice: selectedDevice: clonedSelectedDevices.isNotEmpty ? clonedSelectedDevices : null,
clonedSelectedDevices.isNotEmpty ? clonedSelectedDevices : null,
isControlButtonEnabled: isControlButtonEnabled, isControlButtonEnabled: isControlButtonEnabled,
)); ));
} else if (state is DeviceManagementFiltered) { } else if (state is DeviceManagementFiltered) {
@ -168,15 +174,13 @@ class DeviceManagementBloc
onlineCount: _onlineCount, onlineCount: _onlineCount,
offlineCount: _offlineCount, offlineCount: _offlineCount,
lowBatteryCount: _lowBatteryCount, lowBatteryCount: _lowBatteryCount,
selectedDevice: selectedDevice: clonedSelectedDevices.isNotEmpty ? clonedSelectedDevices : null,
clonedSelectedDevices.isNotEmpty ? clonedSelectedDevices : null,
isControlButtonEnabled: isControlButtonEnabled, isControlButtonEnabled: isControlButtonEnabled,
)); ));
} }
} }
void _onUpdateSelection( void _onUpdateSelection(UpdateSelection event, Emitter<DeviceManagementState> emit) {
UpdateSelection event, Emitter<DeviceManagementState> emit) {
List<AllDevicesModel> selectedDevices = []; List<AllDevicesModel> selectedDevices = [];
List<AllDevicesModel> devicesToSelectFrom = []; List<AllDevicesModel> devicesToSelectFrom = [];
@ -219,8 +223,7 @@ class DeviceManagementBloc
bool _checkIfControlButtonEnabled(List<AllDevicesModel> selectedDevices) { bool _checkIfControlButtonEnabled(List<AllDevicesModel> selectedDevices) {
if (selectedDevices.length > 1) { if (selectedDevices.length > 1) {
final productTypes = final productTypes = selectedDevices.map((device) => device.productType).toSet();
selectedDevices.map((device) => device.productType).toSet();
return productTypes.length == 1; return productTypes.length == 1;
} else if (selectedDevices.length == 1) { } else if (selectedDevices.length == 1) {
return true; return true;
@ -231,10 +234,8 @@ class DeviceManagementBloc
void _calculateDeviceCounts() { void _calculateDeviceCounts() {
_onlineCount = _devices.where((device) => device.online == true).length; _onlineCount = _devices.where((device) => device.online == true).length;
_offlineCount = _devices.where((device) => device.online == false).length; _offlineCount = _devices.where((device) => device.online == false).length;
_lowBatteryCount = _devices _lowBatteryCount =
.where((device) => _devices.where((device) => device.batteryLevel != null && device.batteryLevel! < 20).length;
device.batteryLevel != null && device.batteryLevel! < 20)
.length;
} }
String _getFilterFromIndex(int index) { String _getFilterFromIndex(int index) {
@ -250,8 +251,7 @@ class DeviceManagementBloc
} }
} }
void _onSearchDevices( void _onSearchDevices(SearchDevices event, Emitter<DeviceManagementState> emit) {
SearchDevices event, Emitter<DeviceManagementState> emit) {
if ((event.community == null || event.community!.isEmpty) && if ((event.community == null || event.community!.isEmpty) &&
(event.unitName == null || event.unitName!.isEmpty) && (event.unitName == null || event.unitName!.isEmpty) &&
(event.productName == null || event.productName!.isEmpty)) { (event.productName == null || event.productName!.isEmpty)) {
@ -280,33 +280,22 @@ class DeviceManagementBloc
final filteredDevices = devicesToSearch.where((device) { final filteredDevices = devicesToSearch.where((device) {
final matchesCommunity = event.community == null || final matchesCommunity = event.community == null ||
event.community!.isEmpty || event.community!.isEmpty ||
(device.community?.name (device.community?.name?.toLowerCase().contains(event.community!.toLowerCase()) ??
?.toLowerCase()
.contains(event.community!.toLowerCase()) ??
false); false);
final matchesUnit = event.unitName == null || final matchesUnit = event.unitName == null ||
event.unitName!.isEmpty || event.unitName!.isEmpty ||
(device.spaces != null && (device.spaces != null &&
device.spaces!.isNotEmpty && device.spaces!.isNotEmpty &&
device.spaces![0].spaceName device.spaces![0].spaceName!.toLowerCase().contains(event.unitName!.toLowerCase()));
!.toLowerCase()
.contains(event.unitName!.toLowerCase()));
final matchesProductName = event.productName == null || final matchesProductName = event.productName == null ||
event.productName!.isEmpty || event.productName!.isEmpty ||
(device.name (device.name?.toLowerCase().contains(event.productName!.toLowerCase()) ?? false);
?.toLowerCase()
.contains(event.productName!.toLowerCase()) ??
false);
final matchesDeviceName = event.productName == null || final matchesDeviceName = event.productName == null ||
event.productName!.isEmpty || event.productName!.isEmpty ||
(device.categoryName (device.categoryName?.toLowerCase().contains(event.productName!.toLowerCase()) ??
?.toLowerCase()
.contains(event.productName!.toLowerCase()) ??
false); false);
return matchesCommunity && return matchesCommunity && matchesUnit && (matchesProductName || matchesDeviceName);
matchesUnit &&
(matchesProductName || matchesDeviceName);
}).toList(); }).toList();
emit(DeviceManagementFiltered( emit(DeviceManagementFiltered(

View File

@ -7,7 +7,15 @@ abstract class DeviceManagementEvent extends Equatable {
List<Object?> get props => []; List<Object?> get props => [];
} }
class FetchDevices extends DeviceManagementEvent {} class FetchDevices extends DeviceManagementEvent {
// final Map<String, List<String>> selectedCommunitiesSpaces;
// final String spaceId;
final BuildContext context;
const FetchDevices(this.context);
@override
List<Object?> get props => [context];
}
class FilterDevices extends DeviceManagementEvent { class FilterDevices extends DeviceManagementEvent {
final String filter; final String filter;

View File

@ -0,0 +1,47 @@
class DeviceSubspace {
final String uuid;
final DateTime? createdAt;
final DateTime? updatedAt;
final String subspaceName;
final bool disabled;
DeviceSubspace({
required this.uuid,
this.createdAt,
this.updatedAt,
required this.subspaceName,
required this.disabled,
});
factory DeviceSubspace.fromJson(Map<String, dynamic> json) {
return DeviceSubspace(
uuid: json['uuid'] as String,
createdAt: json['createdAt'] != null
? DateTime.tryParse(json['createdAt'].toString())
: null,
updatedAt: json['updatedAt'] != null
? DateTime.tryParse(json['updatedAt'].toString())
: null,
subspaceName: json['subspaceName'] as String,
disabled: json['disabled'] as bool,
);
}
Map<String, dynamic> toJson() {
return {
'uuid': uuid,
'createdAt': createdAt?.toIso8601String(),
'updatedAt': updatedAt?.toIso8601String(),
'subspaceName': subspaceName,
'disabled': disabled,
};
}
static List<DeviceSubspace> listFromJson(List<dynamic> jsonList) {
return jsonList.map((json) => DeviceSubspace.fromJson(json)).toList();
}
static List<Map<String, dynamic>> listToJson(List<DeviceSubspace> subspaces) {
return subspaces.map((subspace) => subspace.toJson()).toList();
}
}

View File

@ -1,12 +1,13 @@
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_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_space_model.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/room.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/device_managment/all_devices/models/unit.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart'; import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/one_gang_switch/one_gang_switch.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/three_gang_switch/three_gang_switch.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/three_gang_switch/three_gang_switch.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/two_gang_switch/two_gang_switch.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/two_gang_switch/two_gang_switch.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/enum/device_types.dart'; import 'package:syncrow_web/utils/enum/device_types.dart';
@ -47,6 +48,7 @@ class AllDevicesModel {
*/ */
DevicesModelRoom? room; DevicesModelRoom? room;
DeviceSubspace? subspace;
DevicesModelUnit? unit; DevicesModelUnit? unit;
DeviceCommunityModel? community; DeviceCommunityModel? community;
String? productUuid; String? productUuid;
@ -77,6 +79,7 @@ class AllDevicesModel {
AllDevicesModel({ AllDevicesModel({
this.room, this.room,
this.subspace,
this.unit, this.unit,
this.community, this.community,
this.productUuid, this.productUuid,
@ -110,6 +113,9 @@ class AllDevicesModel {
room = (json['room'] != null && (json['room'] is Map)) room = (json['room'] != null && (json['room'] is Map))
? DevicesModelRoom.fromJson(json['room']) ? DevicesModelRoom.fromJson(json['room'])
: null; : null;
subspace = (json['subspace'] != null && (json['subspace'] is Map))
? DeviceSubspace.fromJson(json['subspace'])
: null;
unit = (json['unit'] != null && (json['unit'] is Map)) unit = (json['unit'] != null && (json['unit'] is Map))
? DevicesModelUnit.fromJson(json['unit']) ? DevicesModelUnit.fromJson(json['unit'])
: null; : null;
@ -142,9 +148,7 @@ class AllDevicesModel {
productName = json['productName']?.toString(); productName = json['productName']?.toString();
if (json['spaces'] != null && json['spaces'] is List) { if (json['spaces'] != null && json['spaces'] is List) {
spaces = (json['spaces'] as List) spaces = (json['spaces'] as List).map((space) => DeviceSpaceModel.fromJson(space)).toList();
.map((space) => DeviceSpaceModel.fromJson(space))
.toList();
} }
} }
@ -192,8 +196,7 @@ SOS
String tempIcon = ''; String tempIcon = '';
if (type == DeviceType.LightBulb) { if (type == DeviceType.LightBulb) {
tempIcon = Assets.lightBulb; tempIcon = Assets.lightBulb;
} else if (type == DeviceType.CeilingSensor || } else if (type == DeviceType.CeilingSensor || type == DeviceType.WallSensor) {
type == DeviceType.WallSensor) {
tempIcon = Assets.sensors; tempIcon = Assets.sensors;
} else if (type == DeviceType.AC) { } else if (type == DeviceType.AC) {
tempIcon = Assets.ac; tempIcon = Assets.ac;
@ -248,34 +251,25 @@ SOS
case '1G': case '1G':
return [ return [
OneGangSwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''), OneGangSwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
OneGangCountdownFunction( OneGangCountdownFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
deviceId: uuid ?? '', deviceName: name ?? ''),
]; ];
case '2G': case '2G':
return [ return [
TwoGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''), TwoGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''), TwoGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown1Function( TwoGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
deviceId: uuid ?? '', deviceName: name ?? ''), TwoGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown2Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
]; ];
case '3G': case '3G':
return [ return [
ThreeGangSwitch1Function( ThreeGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
deviceId: uuid ?? '', deviceName: name ?? ''), ThreeGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangSwitch2Function( ThreeGangSwitch3Function(deviceId: uuid ?? '', deviceName: name ?? ''),
deviceId: uuid ?? '', deviceName: name ?? ''), ThreeGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangSwitch3Function( ThreeGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
deviceId: uuid ?? '', deviceName: name ?? ''), ThreeGangCountdown3Function(deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown1Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown2Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown3Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
]; ];
default: default:
@ -288,6 +282,9 @@ SOS
if (room != null) { if (room != null) {
data['room'] = room!.toJson(); data['room'] = room!.toJson();
} }
if (subspace != null) {
data['subspace'] = subspace!.toJson();
}
if (unit != null) { if (unit != null) {
data['unit'] = unit!.toJson(); data['unit'] = unit!.toJson();
} }
@ -330,6 +327,7 @@ SOS
return other is AllDevicesModel && return other is AllDevicesModel &&
other.room == room && other.room == room &&
other.subspace == subspace &&
other.unit == unit && other.unit == unit &&
other.productUuid == productUuid && other.productUuid == productUuid &&
other.productType == productType && other.productType == productType &&
@ -360,6 +358,7 @@ SOS
@override @override
int get hashCode { int get hashCode {
return room.hashCode ^ return room.hashCode ^
subspace.hashCode ^
unit.hashCode ^ unit.hashCode ^
productUuid.hashCode ^ productUuid.hashCode ^
productType.hashCode ^ productType.hashCode ^

View File

@ -3,9 +3,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_managment_body.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_managment_body.dart';
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart'; import 'package:syncrow_web/pages/routines/view/create_new_routine_view.dart';
import 'package:syncrow_web/pages/routiens/view/routines_view.dart'; import 'package:syncrow_web/pages/routines/view/routines_view.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -19,7 +19,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
return MultiBlocProvider( return MultiBlocProvider(
providers: [ providers: [
BlocProvider( BlocProvider(
create: (context) => DeviceManagementBloc()..add(FetchDevices()), create: (context) => DeviceManagementBloc()..add(FetchDevices(context)),
), ),
], ],
child: WebScaffold( child: WebScaffold(
@ -80,7 +80,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>( return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
builder: (context, deviceState) { builder: (context, deviceState) {
if (deviceState is DeviceManagementLoading) { if (deviceState is DeviceManagementLoading) {
return const Center(child: CircularProgressIndicator()); return const DeviceManagementBody(devices: []);
} else if (deviceState is DeviceManagementLoaded) { } else if (deviceState is DeviceManagementLoaded) {
return DeviceManagementBody(devices: deviceState.devices); return DeviceManagementBody(devices: deviceState.devices);
} else if (deviceState is DeviceManagementFiltered) { } else if (deviceState is DeviceManagementFiltered) {

View File

@ -8,6 +8,8 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_search_filters.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_search_filters.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_batch_control_dialog.dart'; import 'package:syncrow_web/pages/device_managment/shared/device_batch_control_dialog.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_control_dialog.dart'; import 'package:syncrow_web/pages/device_managment/shared/device_control_dialog.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
import 'package:syncrow_web/utils/format_date_time.dart'; import 'package:syncrow_web/utils/format_date_time.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
@ -59,118 +61,153 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
final buttonLabel = (selectedDevices.length > 1) ? 'Batch Control' : 'Control'; final buttonLabel = (selectedDevices.length > 1) ? 'Batch Control' : 'Control';
return Column( return Row(
children: [ children: [
Container( Expanded(child: SpaceTreeView(
padding: isLargeScreenSize(context) ? const EdgeInsets.all(30) : const EdgeInsets.all(15), onSelect: () {
child: Column( context.read<DeviceManagementBloc>().add(FetchDevices(context));
crossAxisAlignment: CrossAxisAlignment.start, },
children: [ )),
FilterWidget( Expanded(
size: MediaQuery.of(context).size, flex: 4,
tabs: tabs, child: state is DeviceManagementLoading
selectedIndex: selectedIndex, ? const Center(child: CircularProgressIndicator())
onTabChanged: (index) { : Column(
context.read<DeviceManagementBloc>().add(SelectedFilterChanged(index)); children: [
}, Container(
), padding: isLargeScreenSize(context)
const SizedBox(height: 20), ? const EdgeInsets.all(30)
const DeviceSearchFilters(), : const EdgeInsets.all(15),
const SizedBox(height: 12), child: Column(
Container( crossAxisAlignment: CrossAxisAlignment.start,
height: 45, children: [
width: 125, FilterWidget(
decoration: containerDecoration, size: MediaQuery.of(context).size,
child: Center( tabs: tabs,
child: DefaultButton( selectedIndex: selectedIndex,
onPressed: isControlButtonEnabled onTabChanged: (index) {
? () { context
if (selectedDevices.length == 1) { .read<DeviceManagementBloc>()
showDialog( .add(SelectedFilterChanged(index));
context: context, },
builder: (context) => DeviceControlDialog( ),
device: selectedDevices.first, const SizedBox(height: 20),
), const DeviceSearchFilters(),
); const SizedBox(height: 12),
} else if (selectedDevices.length > 1) { Container(
final productTypes = selectedDevices.map((device) => device.productType).toSet(); height: 45,
if (productTypes.length == 1) { width: 125,
showDialog( decoration: containerDecoration,
context: context, child: Center(
builder: (context) => DeviceBatchControlDialog( child: DefaultButton(
devices: selectedDevices, onPressed: isControlButtonEnabled
? () {
if (selectedDevices.length == 1) {
showDialog(
context: context,
builder: (context) => DeviceControlDialog(
device: selectedDevices.first,
),
);
} else if (selectedDevices.length > 1) {
final productTypes = selectedDevices
.map((device) => device.productType)
.toSet();
if (productTypes.length == 1) {
showDialog(
context: context,
builder: (context) => DeviceBatchControlDialog(
devices: selectedDevices,
),
);
}
}
}
: null,
borderRadius: 9,
child: Text(
buttonLabel,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: isControlButtonEnabled ? Colors.white : Colors.grey,
), ),
); ),
} ),
} ),
} ),
: null, ],
borderRadius: 9,
child: Text(
buttonLabel,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: isControlButtonEnabled ? Colors.white : Colors.grey,
), ),
), ),
), Expanded(
), child: Padding(
), padding: isLargeScreenSize(context)
], ? const EdgeInsets.all(30)
), : const EdgeInsets.all(15),
), child: DynamicTable(
Expanded( withSelectAll: true,
child: Padding( cellDecoration: containerDecoration,
padding: isLargeScreenSize(context) ? const EdgeInsets.all(30) : const EdgeInsets.all(15), onRowSelected: (index, isSelected, row) {
child: DynamicTable( final selectedDevice = devicesToShow[index];
withSelectAll: true, context
cellDecoration: containerDecoration, .read<DeviceManagementBloc>()
onRowSelected: (index, isSelected, row) { .add(SelectDevice(selectedDevice));
final selectedDevice = devicesToShow[index]; },
context.read<DeviceManagementBloc>().add(SelectDevice(selectedDevice)); withCheckBox: true,
}, size: MediaQuery.of(context).size,
withCheckBox: true, uuidIndex: 2,
size: MediaQuery.of(context).size, headers: const [
uuidIndex: 2, 'Device Name',
headers: const [ 'Product Name',
'Device Name', 'Device ID',
'Product Name', 'Space Name',
'Device ID', 'location',
'Space Name', 'Battery Level',
'location', 'Installation Date and Time',
'Battery Level', 'Status',
'Installation Date and Time', 'Last Offline Date and Time',
'Status', ],
'Last Offline Date and Time', data: devicesToShow.map((device) {
], final combinedSpaceNames = device.spaces != null
data: devicesToShow.map((device) { ? device.spaces!.map((space) => space.spaceName).join(' > ') +
final combinedSpaceNames = device.spaces != null (device.community != null
? device.spaces!.map((space) => space.spaceName).join(' > ') + ? ' > ${device.community!.name}'
(device.community != null ? ' > ${device.community!.name}' : '') : '')
: (device.community != null ? device.community!.name : ''); : (device.community != null ? device.community!.name : '');
return [ return [
device.name ?? '', device.name ?? '',
device.productName ?? '', device.productName ?? '',
device.uuid ?? '', device.uuid ?? '',
(device.spaces != null && device.spaces!.isNotEmpty) ? device.spaces![0].spaceName : '', (device.spaces != null && device.spaces!.isNotEmpty)
combinedSpaceNames, ? device.spaces![0].spaceName
device.batteryLevel != null ? '${device.batteryLevel}%' : '-', : '',
formatDateTime(DateTime.fromMillisecondsSinceEpoch((device.createTime ?? 0) * 1000)), combinedSpaceNames,
device.online == true ? 'Online' : 'Offline', device.batteryLevel != null ? '${device.batteryLevel}%' : '-',
formatDateTime(DateTime.fromMillisecondsSinceEpoch((device.updateTime ?? 0) * 1000)), formatDateTime(DateTime.fromMillisecondsSinceEpoch(
]; (device.createTime ?? 0) * 1000)),
}).toList(), device.online == true ? 'Online' : 'Offline',
onSelectionChanged: (selectedRows) { formatDateTime(DateTime.fromMillisecondsSinceEpoch(
context.read<DeviceManagementBloc>().add(UpdateSelection(selectedRows)); (device.updateTime ?? 0) * 1000)),
}, ];
initialSelectedIds: }).toList(),
context.read<DeviceManagementBloc>().selectedDevices.map((device) => device.uuid!).toList(), onSelectionChanged: (selectedRows) {
isEmpty: devicesToShow.isEmpty, context
), .read<DeviceManagementBloc>()
), .add(UpdateSelection(selectedRows));
) },
initialSelectedIds: context
.read<DeviceManagementBloc>()
.selectedDevices
.map((device) => device.uuid!)
.toList(),
isEmpty: devicesToShow.isEmpty,
),
),
)
],
),
),
], ],
); );
}, },

View File

@ -12,8 +12,7 @@ class DeviceSearchFilters extends StatefulWidget {
State<DeviceSearchFilters> createState() => _DeviceSearchFiltersState(); State<DeviceSearchFilters> createState() => _DeviceSearchFiltersState();
} }
class _DeviceSearchFiltersState extends State<DeviceSearchFilters> class _DeviceSearchFiltersState extends State<DeviceSearchFilters> with HelperResponsiveLayout {
with HelperResponsiveLayout {
final TextEditingController communityController = TextEditingController(); final TextEditingController communityController = TextEditingController();
final TextEditingController unitNameController = TextEditingController(); final TextEditingController unitNameController = TextEditingController();
final TextEditingController productNameController = TextEditingController(); final TextEditingController productNameController = TextEditingController();
@ -27,8 +26,7 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
const SizedBox(width: 20), const SizedBox(width: 20),
_buildSearchField("Space Name", unitNameController, 200), _buildSearchField("Space Name", unitNameController, 200),
const SizedBox(width: 20), const SizedBox(width: 20),
_buildSearchField( _buildSearchField("Device Name / Product Name", productNameController, 300),
"Device Name / Product Name", productNameController, 300),
const SizedBox(width: 20), const SizedBox(width: 20),
_buildSearchResetButtons(), _buildSearchResetButtons(),
], ],
@ -53,8 +51,7 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
); );
} }
Widget _buildSearchField( Widget _buildSearchField(String title, TextEditingController controller, double width) {
String title, TextEditingController controller, double width) {
return Container( return Container(
child: StatefulTextField( child: StatefulTextField(
title: title, title: title,
@ -88,7 +85,7 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
productNameController.clear(); productNameController.clear();
context.read<DeviceManagementBloc>() context.read<DeviceManagementBloc>()
..add(ResetFilters()) ..add(ResetFilters())
..add(FetchDevices()); ..add(FetchDevices(context));
}, },
); );
} }

View File

@ -95,8 +95,9 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
]), ]),
TableRow( TableRow(
children: [ children: [
_buildInfoRow('Space Name:', device.unit?.name ?? 'N/A'), _buildInfoRow('Space Name:',
_buildInfoRow('Room:', device.room?.name ?? 'N/A'), device.spaces?.firstOrNull?.spaceName ?? 'N/A'),
_buildInfoRow('Room:', device.subspace?.subspaceName ?? 'N/A'),
], ],
), ),
TableRow( TableRow(
@ -111,9 +112,13 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
), ),
_buildInfoRow( _buildInfoRow(
'Battery Level:', 'Battery Level:',
device.batteryLevel != null ? '${device.batteryLevel ?? 0}%' : "-", device.batteryLevel != null
? '${device.batteryLevel ?? 0}%'
: "-",
statusColor: device.batteryLevel != null statusColor: device.batteryLevel != null
? (device.batteryLevel! < 20 ? ColorsManager.red : ColorsManager.green) ? (device.batteryLevel! < 20
? ColorsManager.red
: ColorsManager.green)
: null, : null,
), ),
], ],

View File

@ -1,49 +1,49 @@
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:graphview/GraphView.dart'; // import 'package:graphview/GraphView.dart';
import 'package:syncrow_web/pages/auth/model/user_model.dart'; import 'package:syncrow_web/pages/auth/model/user_model.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart'; import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/home/bloc/home_state.dart'; import 'package:syncrow_web/pages/home/bloc/home_state.dart';
import 'package:syncrow_web/pages/home/home_model/home_item_model.dart'; import 'package:syncrow_web/pages/home/home_model/home_item_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/services/home_api.dart'; import 'package:syncrow_web/services/home_api.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart';
class HomeBloc extends Bloc<HomeEvent, HomeState> { class HomeBloc extends Bloc<HomeEvent, HomeState> {
final Graph graph = Graph()..isTree = true; // final Graph graph = Graph()..isTree = true;
final BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration(); // final BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration();
List<Node> sourcesList = []; // List<Node> sourcesList = [];
List<Node> destinationsList = []; // List<Node> destinationsList = [];
UserModel? user; UserModel? user;
String terms = ''; String terms = '';
String policy = ''; String policy = '';
HomeBloc() : super((HomeInitial())) { HomeBloc() : super((HomeInitial())) {
on<CreateNewNode>(_createNode); // on<CreateNewNode>(_createNode);
on<FetchUserInfo>(_fetchUserInfo); on<FetchUserInfo>(_fetchUserInfo);
on<FetchTermEvent>(_fetchTerms); on<FetchTermEvent>(_fetchTerms);
on<FetchPolicyEvent>(_fetchPolicy); on<FetchPolicyEvent>(_fetchPolicy);
on<ConfirmUserAgreementEvent>(_confirmUserAgreement); on<ConfirmUserAgreementEvent>(_confirmUserAgreement);
} }
void _createNode(CreateNewNode event, Emitter<HomeState> emit) async { // void _createNode(CreateNewNode event, Emitter<HomeState> emit) async {
emit(HomeInitial()); // emit(HomeInitial());
sourcesList.add(event.sourceNode); // sourcesList.add(event.sourceNode);
destinationsList.add(event.destinationNode); // destinationsList.add(event.destinationNode);
for (int i = 0; i < sourcesList.length; i++) { // for (int i = 0; i < sourcesList.length; i++) {
graph.addEdge(sourcesList[i], destinationsList[i]); // graph.addEdge(sourcesList[i], destinationsList[i]);
} // }
builder // builder
..siblingSeparation = (100) // ..siblingSeparation = (100)
..levelSeparation = (150) // ..levelSeparation = (150)
..subtreeSeparation = (150) // ..subtreeSeparation = (150)
..orientation = (BuchheimWalkerConfiguration.ORIENTATION_TOP_BOTTOM); // ..orientation = (BuchheimWalkerConfiguration.ORIENTATION_TOP_BOTTOM);
emit(HomeUpdateTree(graph: graph, builder: builder)); // emit(HomeUpdateTree(graph: graph, builder: builder));
} // }
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async { Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async {
try { try {

View File

@ -1,5 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:graphview/GraphView.dart'; // import 'package:graphview/GraphView.dart';
abstract class HomeEvent extends Equatable { abstract class HomeEvent extends Equatable {
const HomeEvent(); const HomeEvent();
@ -8,20 +8,22 @@ abstract class HomeEvent extends Equatable {
List<Object> get props => []; List<Object> get props => [];
} }
class CreateNewNode extends HomeEvent { // class CreateNewNode extends HomeEvent {
final Node sourceNode; // final Node sourceNode;
final Node destinationNode; // final Node destinationNode;
const CreateNewNode( // const CreateNewNode(
{required this.sourceNode, required this.destinationNode}); // {required this.sourceNode, required this.destinationNode});
@override // @override
List<Object> get props => [sourceNode, destinationNode]; // List<Object> get props => [sourceNode, destinationNode];
} // }
class FetchUserInfo extends HomeEvent { class FetchUserInfo extends HomeEvent {
const FetchUserInfo(); const FetchUserInfo();
}class FetchTermEvent extends HomeEvent {} }
class FetchTermEvent extends HomeEvent {}
class FetchPolicyEvent extends HomeEvent {} class FetchPolicyEvent extends HomeEvent {}
class ConfirmUserAgreementEvent extends HomeEvent {} class ConfirmUserAgreementEvent extends HomeEvent {}

View File

@ -1,5 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:graphview/GraphView.dart'; // import 'package:graphview/GraphView.dart';
abstract class HomeState extends Equatable { abstract class HomeState extends Equatable {
const HomeState(); const HomeState();
@ -7,26 +7,26 @@ abstract class HomeState extends Equatable {
@override @override
List<Object> get props => []; List<Object> get props => [];
} }
class LoadingHome extends HomeState {} class LoadingHome extends HomeState {}
class HomeInitial extends HomeState {} class HomeInitial extends HomeState {}
class TermsAgreement extends HomeState {} class TermsAgreement extends HomeState {}
class PolicyAgreement extends HomeState {} class PolicyAgreement extends HomeState {}
class HomeCounterState extends HomeState { // class HomeCounterState extends HomeState {
final int counter; // final int counter;
const HomeCounterState(this.counter); // const HomeCounterState(this.counter);
} // }
class HomeUpdateTree extends HomeState { // class HomeUpdateTree extends HomeState {
final Graph graph; // final Graph graph;
final BuchheimWalkerConfiguration builder; // final BuchheimWalkerConfiguration builder;
const HomeUpdateTree({required this.graph, required this.builder}); // const HomeUpdateTree({required this.graph, required this.builder});
@override // @override
List<Object> get props => [graph, builder]; // List<Object> get props => [graph, builder];
} // }
//FetchTermEvent

View File

@ -41,8 +41,7 @@ class HomeMobilePage extends StatelessWidget {
SizedBox(height: size.height * 0.05), SizedBox(height: size.height * 0.05),
const Text( const Text(
'ACCESS YOUR APPS', 'ACCESS YOUR APPS',
style: style: TextStyle(fontSize: 20, fontWeight: FontWeight.w700),
TextStyle(fontSize: 20, fontWeight: FontWeight.w700),
), ),
const SizedBox(height: 30), const SizedBox(height: 30),
Expanded( Expanded(
@ -51,9 +50,8 @@ class HomeMobilePage extends StatelessWidget {
height: size.height * 0.6, height: size.height * 0.6,
width: size.width * 0.68, width: size.width * 0.68,
child: GridView.builder( child: GridView.builder(
itemCount: 8, itemCount: 3,
gridDelegate: gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisCount: 2,
crossAxisSpacing: 20.0, crossAxisSpacing: 20.0,
mainAxisSpacing: 20.0, mainAxisSpacing: 20.0,
@ -65,8 +63,7 @@ class HomeMobilePage extends StatelessWidget {
active: homeItems[index]['active'], active: homeItems[index]['active'],
name: homeItems[index]['title'], name: homeItems[index]['title'],
img: homeItems[index]['icon'], img: homeItems[index]['icon'],
onTap: () => onTap: () => homeBloc.homeItems[index].onPress(context),
homeBloc.homeItems[index].onPress(context),
); );
}, },
), ),
@ -97,33 +94,33 @@ class HomeMobilePage extends StatelessWidget {
'icon': Assets.devicesIcon, 'icon': Assets.devicesIcon,
'active': true, 'active': true,
}, },
{ // {
'title': 'Move in', // 'title': 'Move in',
'icon': Assets.moveinIcon, // 'icon': Assets.moveinIcon,
'active': false, // 'active': false,
}, // },
{ // {
'title': 'Construction', // 'title': 'Construction',
'icon': Assets.constructionIcon, // 'icon': Assets.constructionIcon,
'active': false, // 'active': false,
}, // },
{ // {
'title': 'Energy', // 'title': 'Energy',
'icon': Assets.energyIcon, // 'icon': Assets.energyIcon,
'color': ColorsManager.slidingBlueColor.withOpacity(0.2), // 'color': ColorsManager.slidingBlueColor.withOpacity(0.2),
'active': false, // 'active': false,
}, // },
{ // {
'title': 'Integrations', // 'title': 'Integrations',
'icon': Assets.integrationsIcon, // 'icon': Assets.integrationsIcon,
'color': ColorsManager.slidingBlueColor.withOpacity(0.2), // 'color': ColorsManager.slidingBlueColor.withOpacity(0.2),
'active': false, // 'active': false,
}, // },
{ // {
'title': 'Asset', // 'title': 'Asset',
'icon': Assets.assetIcon, // 'icon': Assets.assetIcon,
'color': ColorsManager.slidingBlueColor.withOpacity(0.2), // 'color': ColorsManager.slidingBlueColor.withOpacity(0.2),
'active': false, // 'active': false,
}, // },
]; ];
} }

View File

@ -58,45 +58,48 @@ class HomeWebPage extends StatelessWidget {
scaffoldBody: SizedBox( scaffoldBody: SizedBox(
height: size.height, height: size.height,
width: size.width, width: size.width,
child: Column( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
SizedBox(height: size.height * 0.1), Column(
Text( mainAxisAlignment: MainAxisAlignment.center,
'ACCESS YOUR APPS', crossAxisAlignment: CrossAxisAlignment.center,
style: Theme.of(context) children: [
.textTheme SizedBox(height: size.height * 0.1),
.headlineLarge! Text(
.copyWith(color: Colors.black, fontSize: 40), 'ACCESS YOUR APPS',
), style: Theme.of(context)
const SizedBox(height: 30), .textTheme
Expanded( .headlineLarge!
flex: 4, .copyWith(color: Colors.black, fontSize: 40),
child: SizedBox(
height: size.height * 0.6,
width: size.width * 0.68,
child: GridView.builder(
itemCount: 3, //8
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 20.0,
mainAxisSpacing: 20.0,
childAspectRatio: 1.5,
),
itemBuilder: (context, index) {
return HomeCard(
index: index,
active: homeBloc.homeItems[index].active!,
name: homeBloc.homeItems[index].title!,
img: homeBloc.homeItems[index].icon!,
onTap: () =>
homeBloc.homeItems[index].onPress(context),
);
},
), ),
), const SizedBox(height: 30),
Expanded(
flex: 4,
child: SizedBox(
height: size.height * 0.6,
width: size.width * 0.68,
child: GridView.builder(
itemCount: 3, //8
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, //4
crossAxisSpacing: 20.0,
mainAxisSpacing: 20.0,
childAspectRatio: 1.5,
),
itemBuilder: (context, index) {
return HomeCard(
index: index,
active: homeBloc.homeItems[index].active!,
name: homeBloc.homeItems[index].title!,
img: homeBloc.homeItems[index].icon!,
onTap: () => homeBloc.homeItems[index].onPress(context),
);
},
),
),
),
],
), ),
], ],
), ),

View File

@ -1,185 +1,185 @@
import 'package:flutter/material.dart'; // import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; // import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:graphview/GraphView.dart'; // import 'package:graphview/GraphView.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; // import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart'; // import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/home/bloc/home_state.dart'; // import 'package:syncrow_web/pages/home/bloc/home_state.dart';
class TreeWidget extends StatelessWidget { // class TreeWidget extends StatelessWidget {
const TreeWidget({super.key}); // const TreeWidget({super.key});
@override // @override
Widget build(BuildContext context) { // Widget build(BuildContext context) {
// final HomeBloc homeBloc = BlocProvider.of<HomeBloc>(context); // // final HomeBloc homeBloc = BlocProvider.of<HomeBloc>(context);
String firstNodeName = ''; // String firstNodeName = '';
String secondNodeName = ''; // String secondNodeName = '';
return SafeArea( // return SafeArea(
child: Container( // child: Container(
padding: const EdgeInsets.all(24), // padding: const EdgeInsets.all(24),
width: MediaQuery.sizeOf(context).width, // width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height, // height: MediaQuery.sizeOf(context).height,
alignment: AlignmentDirectional.center, // alignment: AlignmentDirectional.center,
child: Column( // child: Column(
mainAxisSize: MainAxisSize.max, // mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center, // crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, // mainAxisAlignment: MainAxisAlignment.center,
children: [ // children: [
BlocBuilder<HomeBloc, HomeState>(builder: (context, state) { // BlocBuilder<HomeBloc, HomeState>(builder: (context, state) {
if (state is HomeInitial) { // if (state is HomeInitial) {
return Wrap( // return Wrap(
children: [ // children: [
SizedBox( // SizedBox(
width: 100, // width: 100,
child: TextFormField( // child: TextFormField(
decoration: const InputDecoration( // decoration: const InputDecoration(
labelText: "Subtree separation"), // labelText: "Subtree separation"),
onChanged: (text) { // onChanged: (text) {
firstNodeName = text; // firstNodeName = text;
}, // },
), // ),
), // ),
const SizedBox( // const SizedBox(
width: 8, // width: 8,
), // ),
Container( // Container(
width: 100, // width: 100,
child: TextFormField( // child: TextFormField(
decoration: InputDecoration(labelText: "Node Name"), // decoration: InputDecoration(labelText: "Node Name"),
onChanged: (text) { // onChanged: (text) {
secondNodeName = text; // secondNodeName = text;
}, // },
), // ),
), // ),
ElevatedButton( // ElevatedButton(
onPressed: () { // onPressed: () {
final node1 = Node.Id(firstNodeName); // final node1 = Node.Id(firstNodeName);
final node2 = Node.Id(secondNodeName); // final node2 = Node.Id(secondNodeName);
context.read<HomeBloc>().add(CreateNewNode( // context.read<HomeBloc>().add(CreateNewNode(
sourceNode: node1, destinationNode: node2)); // sourceNode: node1, destinationNode: node2));
}, // },
child: Text("Add"), // child: Text("Add"),
) // )
], // ],
); // );
} // }
if (state is HomeUpdateTree) { // if (state is HomeUpdateTree) {
return Expanded( // return Expanded(
child: InteractiveViewer( // child: InteractiveViewer(
constrained: false, // constrained: false,
boundaryMargin: const EdgeInsets.all(100), // boundaryMargin: const EdgeInsets.all(100),
minScale: 0.01, // minScale: 0.01,
maxScale: 5.6, // maxScale: 5.6,
child: GraphView( // child: GraphView(
graph: state.graph, // graph: state.graph,
algorithm: BuchheimWalkerAlgorithm( // algorithm: BuchheimWalkerAlgorithm(
state.builder, TreeEdgeRenderer(state.builder)), // state.builder, TreeEdgeRenderer(state.builder)),
paint: Paint() // paint: Paint()
..color = Colors.green // ..color = Colors.green
..strokeWidth = 1 // ..strokeWidth = 1
..style = PaintingStyle.stroke, // ..style = PaintingStyle.stroke,
builder: (Node node) { // builder: (Node node) {
// I can decide what widget should be shown here based on the id // // I can decide what widget should be shown here based on the id
var nodeName = node.key!.value; // var nodeName = node.key!.value;
return rectangleWidget(nodeName, node, context); // return rectangleWidget(nodeName, node, context);
}, // },
)), // )),
); // );
} else { // } else {
return Container(); // return Container();
} // }
}) // })
], // ],
), // ),
), // ),
); // );
} // }
} // }
Widget rectangleWidget(String text, Node node, BuildContext blocContext) { // Widget rectangleWidget(String text, Node node, BuildContext blocContext) {
String nodeName = ''; // String nodeName = '';
return InkWell( // return InkWell(
onTap: () { // onTap: () {
showDialog( // showDialog(
context: blocContext, // context: blocContext,
builder: (BuildContext context) { // builder: (BuildContext context) {
return AlertDialog( // return AlertDialog(
title: const Text('Add a child'), // title: const Text('Add a child'),
content: TextField( // content: TextField(
decoration: // decoration:
const InputDecoration(hintText: 'Enter your text here'), // const InputDecoration(hintText: 'Enter your text here'),
onChanged: (value) { // onChanged: (value) {
nodeName = value; // nodeName = value;
}, // },
), // ),
actions: <Widget>[ // actions: <Widget>[
TextButton( // TextButton(
onPressed: () { // onPressed: () {
Navigator.of(context).pop(); // Navigator.of(context).pop();
}, // },
child: Text('Close'), // child: Text('Close'),
), // ),
TextButton( // TextButton(
onPressed: () { // onPressed: () {
if (nodeName.isNotEmpty) { // if (nodeName.isNotEmpty) {
final newNode = Node.Id(nodeName); // final newNode = Node.Id(nodeName);
blocContext.read<HomeBloc>().add(CreateNewNode( // blocContext.read<HomeBloc>().add(CreateNewNode(
sourceNode: node, destinationNode: newNode)); // sourceNode: node, destinationNode: newNode));
} // }
Navigator.of(context).pop(); // Navigator.of(context).pop();
}, // },
child: Text('Add'), // child: Text('Add'),
), // ),
], // ],
); // );
}, // },
); // );
}, // },
child: Container( // child: Container(
width: MediaQuery.of(blocContext).size.width * 0.2, // width: MediaQuery.of(blocContext).size.width * 0.2,
margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0), // margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
padding: EdgeInsets.all(20.0), // padding: EdgeInsets.all(20.0),
decoration: BoxDecoration( // decoration: BoxDecoration(
color: Colors.white, // color: Colors.white,
borderRadius: BorderRadius.circular(10.0), // borderRadius: BorderRadius.circular(10.0),
boxShadow: [ // boxShadow: [
BoxShadow( // BoxShadow(
color: Colors.grey.withOpacity(0.5), // color: Colors.grey.withOpacity(0.5),
spreadRadius: 2, // spreadRadius: 2,
blurRadius: 5, // blurRadius: 5,
offset: Offset(0, 3), // changes position of shadow // offset: Offset(0, 3), // changes position of shadow
), // ),
], // ],
), // ),
child: Row( // child: Row(
children: [ // children: [
const SizedBox( // const SizedBox(
child: Icon( // child: Icon(
Icons.location_on, // Icons.location_on,
color: Colors.blue, // color: Colors.blue,
size: 40.0, // size: 40.0,
), // ),
), // ),
const SizedBox(width: 10.0), // const SizedBox(width: 10.0),
SizedBox( // SizedBox(
child: Text( // child: Text(
text, // text,
style: const TextStyle( // style: const TextStyle(
fontSize: 24.0, // fontSize: 24.0,
fontWeight: FontWeight.bold, // fontWeight: FontWeight.bold,
), // ),
), // ),
), // ),
const Spacer(), // const Spacer(),
Container( // Container(
child: const Icon( // child: const Icon(
Icons.add_circle_outline, // Icons.add_circle_outline,
color: Colors.grey, // color: Colors.grey,
size: 24.0, // size: 24.0,
), // ),
), // ),
], // ],
), // ),
), // ),
); // );
} // }

View File

@ -1,85 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/ac_dialog.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/one_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/three_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/two_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
class DeviceDialogHelper {
static Future<Map<String, dynamic>?> showDeviceDialog(
BuildContext context,
Map<String, dynamic> data, {
required bool removeComparetors,
}) async {
final functions = data['functions'] as List<DeviceFunction>;
try {
final result = await _getDialogForDeviceType(
context,
data['productType'],
data,
functions,
removeComparetors: removeComparetors,
);
if (result != null) {
return result;
}
} catch (e) {
debugPrint('Error: $e');
}
return null;
}
static Future<Map<String, dynamic>?> _getDialogForDeviceType(
BuildContext context,
String productType,
Map<String, dynamic> data,
List<DeviceFunction> functions,
{required bool removeComparetors}) async {
final routineBloc = context.read<RoutineBloc>();
final deviceSelectedFunctions =
routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? [];
switch (productType) {
case 'AC':
return ACHelper.showACFunctionsDialog(
context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
case '1G':
return OneGangSwitchHelper.showSwitchFunctionsDialog(
context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
case '2G':
return TwoGangSwitchHelper.showSwitchFunctionsDialog(
context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
case '3G':
return ThreeGangSwitchHelper.showSwitchFunctionsDialog(
context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
default:
return null;
}
}
}

View File

@ -1,69 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart';
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/fetch_routine_scenes_automation.dart';
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/routine_view_card.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class RoutinesView extends StatefulWidget {
const RoutinesView({super.key});
@override
State<RoutinesView> createState() => _RoutinesViewState();
}
class _RoutinesViewState extends State<RoutinesView> {
@override
void initState() {
super.initState();
context.read<RoutineBloc>().add(FetchDevicesInRoutine());
}
@override
Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) {
if (state.createRoutineView) {
return const CreateNewRoutineView();
}
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Create New Routines",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(
height: 10,
),
RoutineViewCard(
onTap: () {
context.read<RoutineBloc>().add(
(ResetRoutineState()),
);
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(createRoutineView: true),
);
},
icon: Icons.add,
textString: '',
),
const SizedBox(
height: 15,
),
const Expanded(child: FetchRoutineScenesAutomation()),
],
),
);
},
);
}
}

View File

@ -1,143 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/routine_view_card.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 FetchRoutineScenesAutomation extends StatefulWidget {
const FetchRoutineScenesAutomation({super.key});
@override
State<FetchRoutineScenesAutomation> createState() => _FetchRoutineScenesState();
}
class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
with HelperResponsiveLayout {
@override
void initState() {
super.initState();
context.read<RoutineBloc>()
..add(const LoadScenes(spaceId, communityId))
..add(const LoadAutomation(spaceId));
}
@override
Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) {
return state.isLoading
? const Center(
child: CircularProgressIndicator(),
)
: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Scenes (Tab to Run)",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
if (state.scenes.isEmpty)
Text(
"No scenes found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
),
),
if (state.scenes.isNotEmpty)
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: isSmallScreenSize(context) ? 160 : 170,
),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: state.scenes.length,
itemBuilder: (context, index) => Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: RoutineViewCard(
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(createRoutineView: true),
);
context.read<RoutineBloc>().add(
GetSceneDetails(
sceneId: state.scenes[index].id,
isTabToRun: true,
isUpdate: true,
),
);
},
textString: state.scenes[index].name,
icon: state.scenes[index].icon ?? Assets.logoHorizontal,
isFromScenes: true,
iconInBytes: state.scenes[index].iconInBytes,
),
),
),
),
const SizedBox(height: 15),
Text(
"Automations",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
if (state.automations.isEmpty)
Text(
"No automations found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
),
),
if (state.automations.isNotEmpty)
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: isSmallScreenSize(context) ? 160 : 170,
),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: state.automations.length,
itemBuilder: (context, index) => Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: RoutineViewCard(
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(createRoutineView: true),
);
context.read<RoutineBloc>().add(
GetAutomationDetails(
automationId: state.automations[index].id,
isAutomation: true,
isUpdate: true),
);
},
textString: state.automations[index].name,
icon: state.automations[index].icon ?? Assets.automation,
),
),
),
),
],
),
),
);
},
);
}
}

View File

@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_event.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_state.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart';
class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> { class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {

View File

@ -1,5 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart';
abstract class EffectPeriodEvent extends Equatable { abstract class EffectPeriodEvent extends Equatable {

View File

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
part 'functions_bloc_event.dart'; part 'functions_bloc_event.dart';
part 'functions_bloc_state.dart'; part 'functions_bloc_state.dart';
@ -26,8 +26,7 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
functionCode: event.functionData.functionCode, functionCode: event.functionData.functionCode,
operationName: event.functionData.operationName, operationName: event.functionData.operationName,
value: event.functionData.value ?? existingData.value, value: event.functionData.value ?? existingData.value,
valueDescription: event.functionData.valueDescription ?? valueDescription: event.functionData.valueDescription ?? existingData.valueDescription,
existingData.valueDescription,
condition: event.functionData.condition ?? existingData.condition, condition: event.functionData.condition ?? existingData.condition,
); );
} else { } else {
@ -59,10 +58,8 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
); );
} }
FutureOr<void> _onSelectFunction( FutureOr<void> _onSelectFunction(SelectFunction event, Emitter<FunctionBlocState> emit) {
SelectFunction event, Emitter<FunctionBlocState> emit) {
emit(state.copyWith( emit(state.copyWith(
selectedFunction: event.functionCode, selectedFunction: event.functionCode, selectedOperationName: event.operationName));
selectedOperationName: event.operationName));
} }
} }

View File

@ -4,12 +4,12 @@ import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart'; import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_scene_model.dart';
import 'package:syncrow_web/pages/routiens/models/delay/delay_fucntions.dart'; import 'package:syncrow_web/pages/routines/models/delay/delay_fucntions.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/routine_details_model.dart'; import 'package:syncrow_web/pages/routines/models/routine_details_model.dart';
import 'package:syncrow_web/pages/routiens/models/routine_model.dart'; import 'package:syncrow_web/pages/routines/models/routine_model.dart';
import 'package:syncrow_web/services/devices_mang_api.dart'; import 'package:syncrow_web/services/devices_mang_api.dart';
import 'package:syncrow_web/services/routines_api.dart'; import 'package:syncrow_web/services/routines_api.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
@ -19,8 +19,8 @@ import 'package:uuid/uuid.dart';
part 'routine_event.dart'; part 'routine_event.dart';
part 'routine_state.dart'; part 'routine_state.dart';
const spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6'; String spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6';
const communityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9'; String communityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9';
class RoutineBloc extends Bloc<RoutineEvent, RoutineState> { class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
RoutineBloc() : super(const RoutineState()) { RoutineBloc() : super(const RoutineState()) {
@ -57,8 +57,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(routineTab: event.isRoutineTab, createRoutineView: false)); emit(state.copyWith(routineTab: event.isRoutineTab, createRoutineView: false));
add(ResetRoutineState()); add(ResetRoutineState());
if (event.isRoutineTab) { if (event.isRoutineTab) {
add(const LoadScenes(spaceId, communityId)); add(LoadScenes(spaceId, communityId));
add(const LoadAutomation(spaceId)); add(LoadAutomation(spaceId));
} }
} }
@ -156,18 +156,25 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(isLoading: true, errorMessage: null)); emit(state.copyWith(isLoading: true, errorMessage: null));
try { try {
final scenes = await SceneApi.getScenesByUnitId(event.unitId, event.communityId); spaceId = event.spaceId;
communityId = event.communityId;
List<ScenesModel> scenes = [];
if (communityId.isNotEmpty && spaceId.isNotEmpty) {
scenes = await SceneApi.getScenes(event.spaceId, event.communityId);
}
emit(state.copyWith( emit(state.copyWith(
scenes: scenes, scenes: scenes,
isLoading: false, isLoading: false,
)); ));
} catch (e) { } catch (e) {
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
loadScenesErrorMessage: 'Failed to load scenes', loadScenesErrorMessage: 'Failed to load scenes',
errorMessage: '', errorMessage: '',
loadAutomationErrorMessage: '', loadAutomationErrorMessage: '',
)); scenes: []));
} }
} }
@ -175,27 +182,22 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(isLoading: true, errorMessage: null)); emit(state.copyWith(isLoading: true, errorMessage: null));
try { try {
final automations = await SceneApi.getAutomationByUnitId(event.unitId); spaceId = event.spaceId;
if (automations.isNotEmpty) { List<ScenesModel> automations = [];
emit(state.copyWith( if (spaceId.isNotEmpty) {
automations: automations, automations = await SceneApi.getAutomation(event.spaceId);
isLoading: false, }
)); emit(state.copyWith(
} else { automations: automations,
emit(state.copyWith( isLoading: false,
));
} catch (e) {
emit(state.copyWith(
isLoading: false, isLoading: false,
loadAutomationErrorMessage: 'Failed to load automations', loadAutomationErrorMessage: 'Failed to load automations',
errorMessage: '', errorMessage: '',
loadScenesErrorMessage: '', loadScenesErrorMessage: '',
)); automations: []));
}
} catch (e) {
emit(state.copyWith(
isLoading: false,
loadAutomationErrorMessage: 'Failed to load automations',
errorMessage: '',
loadScenesErrorMessage: '',
));
} }
} }
@ -290,8 +292,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final result = await SceneApi.createScene(createSceneModel); final result = await SceneApi.createScene(createSceneModel);
if (result['success']) { if (result['success']) {
add(ResetRoutineState()); add(ResetRoutineState());
add(const LoadScenes(spaceId, communityId)); add(LoadScenes(spaceId, communityId));
add(const LoadAutomation(spaceId)); add(LoadAutomation(spaceId));
} else { } else {
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
@ -419,8 +421,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final result = await SceneApi.createAutomation(createAutomationModel); final result = await SceneApi.createAutomation(createAutomationModel);
if (result['success']) { if (result['success']) {
add(ResetRoutineState()); add(ResetRoutineState());
add(const LoadAutomation(spaceId)); add(LoadAutomation(spaceId));
add(const LoadScenes(spaceId, communityId)); add(LoadScenes(spaceId, communityId));
} else { } else {
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
@ -785,8 +787,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
SceneApi.deleteAutomation(unitUuid: spaceId, automationId: state.automationId ?? ''); SceneApi.deleteAutomation(unitUuid: spaceId, automationId: state.automationId ?? '');
} }
add(const LoadScenes(spaceId, communityId)); add(LoadScenes(spaceId, communityId));
add(const LoadAutomation(spaceId)); add(LoadAutomation(spaceId));
add(ResetRoutineState()); add(ResetRoutineState());
emit(state.copyWith(isLoading: false, createRoutineView: false)); emit(state.copyWith(isLoading: false, createRoutineView: false));
} catch (e) { } catch (e) {
@ -814,7 +816,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)); emit(state.copyWith(isLoading: true));
try { try {
final devices = await DevicesManagementApi().fetchDevices(); final devices = await DevicesManagementApi().fetchDevices('', '');
emit(state.copyWith(isLoading: false, devices: devices)); emit(state.copyWith(isLoading: false, devices: devices));
} catch (e) { } catch (e) {
@ -892,8 +894,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final result = await SceneApi.updateScene(createSceneModel, state.sceneId ?? ''); final result = await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
if (result['success']) { if (result['success']) {
add(ResetRoutineState()); add(ResetRoutineState());
add(const LoadScenes(spaceId, communityId)); add(LoadScenes(spaceId, communityId));
add(const LoadAutomation(spaceId)); add(LoadAutomation(spaceId));
} else { } else {
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
@ -1021,8 +1023,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (result['success']) { if (result['success']) {
add(ResetRoutineState()); add(ResetRoutineState());
add(const LoadAutomation(spaceId)); add(LoadAutomation(spaceId));
add(const LoadScenes(spaceId, communityId)); add(LoadScenes(spaceId, communityId));
} else { } else {
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,

View File

@ -27,22 +27,22 @@ class AddToThenContainer extends RoutineEvent {
} }
class LoadScenes extends RoutineEvent { class LoadScenes extends RoutineEvent {
final String unitId; final String spaceId;
final String communityId; final String communityId;
const LoadScenes(this.unitId, this.communityId); const LoadScenes(this.spaceId, this.communityId);
@override @override
List<Object> get props => [unitId, communityId]; List<Object> get props => [spaceId, communityId];
} }
class LoadAutomation extends RoutineEvent { class LoadAutomation extends RoutineEvent {
final String unitId; final String spaceId;
const LoadAutomation(this.unitId); const LoadAutomation(this.spaceId);
@override @override
List<Object> get props => [unitId]; List<Object> get props => [spaceId];
} }
class AddFunctionToRoutine extends RoutineEvent { class AddFunctionToRoutine extends RoutineEvent {

View File

@ -1,7 +1,7 @@
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_event.dart'; import 'package:syncrow_web/pages/routines/bloc/setting_bloc/setting_event.dart';
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_state.dart'; import 'package:syncrow_web/pages/routines/bloc/setting_bloc/setting_state.dart';
import 'package:syncrow_web/pages/routiens/models/icon_model.dart'; import 'package:syncrow_web/pages/routines/models/icon_model.dart';
import 'package:syncrow_web/services/routines_api.dart'; import 'package:syncrow_web/services/routines_api.dart';
class SettingBloc extends Bloc<SettingEvent, SettingState> { class SettingBloc extends Bloc<SettingEvent, SettingState> {

View File

@ -1,5 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/routiens/models/icon_model.dart'; import 'package:syncrow_web/pages/routines/models/icon_model.dart';
abstract class SettingState extends Equatable { abstract class SettingState extends Equatable {
const SettingState(); const SettingState();

View File

@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ac_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
class DeviceDialogHelper {
static Future<Map<String, dynamic>?> showDeviceDialog(
BuildContext context,
Map<String, dynamic> data, {
required bool removeComparetors,
}) async {
final functions = data['functions'] as List<DeviceFunction>;
try {
final result = await _getDialogForDeviceType(
context,
data['productType'],
data,
functions,
removeComparetors: removeComparetors,
);
if (result != null) {
return result;
}
} catch (e) {
debugPrint('Error: $e');
}
return null;
}
static Future<Map<String, dynamic>?> _getDialogForDeviceType(BuildContext context,
String productType, Map<String, dynamic> data, List<DeviceFunction> functions,
{required bool removeComparetors}) async {
final routineBloc = context.read<RoutineBloc>();
final deviceSelectedFunctions =
routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? [];
switch (productType) {
case 'AC':
return ACHelper.showACFunctionsDialog(context, functions, data['device'],
deviceSelectedFunctions, data['uniqueCustomId'], removeComparetors);
case '1G':
return OneGangSwitchHelper.showSwitchFunctionsDialog(context, functions, data['device'],
deviceSelectedFunctions, data['uniqueCustomId'], removeComparetors);
case '2G':
return TwoGangSwitchHelper.showSwitchFunctionsDialog(context, functions, data['device'],
deviceSelectedFunctions, data['uniqueCustomId'], removeComparetors);
case '3G':
return ThreeGangSwitchHelper.showSwitchFunctionsDialog(context, functions, data['device'],
deviceSelectedFunctions, data['uniqueCustomId'], removeComparetors);
default:
return null;
}
}
}

View File

@ -3,9 +3,9 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';

View File

@ -1,6 +1,6 @@
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart'; import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_operational_value.dart'; import 'package:syncrow_web/pages/routines/models/ac/ac_operational_value.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';

View File

@ -1,5 +1,5 @@
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class DelayFunction extends BaseSwitchFunction { class DelayFunction extends BaseSwitchFunction {

View File

@ -1,5 +1,5 @@
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
abstract class BaseSwitchFunction extends DeviceFunction<bool> { abstract class BaseSwitchFunction extends DeviceFunction<bool> {
BaseSwitchFunction({ BaseSwitchFunction({

View File

@ -1,5 +1,5 @@
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class OneGangSwitchFunction extends BaseSwitchFunction { class OneGangSwitchFunction extends BaseSwitchFunction {

View File

@ -1,5 +1,5 @@
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class ThreeGangSwitch1Function extends BaseSwitchFunction { class ThreeGangSwitch1Function extends BaseSwitchFunction {
@ -26,8 +26,7 @@ class ThreeGangSwitch1Function extends BaseSwitchFunction {
} }
class ThreeGangCountdown1Function extends BaseSwitchFunction { class ThreeGangCountdown1Function extends BaseSwitchFunction {
ThreeGangCountdown1Function( ThreeGangCountdown1Function({required super.deviceId, required super.deviceName})
{required super.deviceId, required super.deviceName})
: super( : super(
code: 'countdown_1', code: 'countdown_1',
operationName: 'Light 1 Countdown', operationName: 'Light 1 Countdown',
@ -71,8 +70,7 @@ class ThreeGangSwitch2Function extends BaseSwitchFunction {
} }
class ThreeGangCountdown2Function extends BaseSwitchFunction { class ThreeGangCountdown2Function extends BaseSwitchFunction {
ThreeGangCountdown2Function( ThreeGangCountdown2Function({required super.deviceId, required super.deviceName})
{required super.deviceId, required super.deviceName})
: super( : super(
code: 'countdown_2', code: 'countdown_2',
operationName: 'Light 2 Countdown', operationName: 'Light 2 Countdown',
@ -116,8 +114,7 @@ class ThreeGangSwitch3Function extends BaseSwitchFunction {
} }
class ThreeGangCountdown3Function extends BaseSwitchFunction { class ThreeGangCountdown3Function extends BaseSwitchFunction {
ThreeGangCountdown3Function( ThreeGangCountdown3Function({required super.deviceId, required super.deviceName})
{required super.deviceId, required super.deviceName})
: super( : super(
code: 'countdown_3', code: 'countdown_3',
operationName: 'Light 3 Countdown', operationName: 'Light 3 Countdown',

View File

@ -1,5 +1,5 @@
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class TwoGangSwitch1Function extends BaseSwitchFunction { class TwoGangSwitch1Function extends BaseSwitchFunction {
@ -49,8 +49,7 @@ class TwoGangSwitch2Function extends BaseSwitchFunction {
} }
class TwoGangCountdown1Function extends BaseSwitchFunction { class TwoGangCountdown1Function extends BaseSwitchFunction {
TwoGangCountdown1Function( TwoGangCountdown1Function({required super.deviceId, required super.deviceName})
{required super.deviceId, required super.deviceName})
: super( : super(
code: 'countdown_1', code: 'countdown_1',
operationName: 'Light 1 Countdown', operationName: 'Light 1 Countdown',
@ -71,8 +70,7 @@ class TwoGangCountdown1Function extends BaseSwitchFunction {
} }
class TwoGangCountdown2Function extends BaseSwitchFunction { class TwoGangCountdown2Function extends BaseSwitchFunction {
TwoGangCountdown2Function( TwoGangCountdown2Function({required super.deviceId, required super.deviceName})
{required super.deviceId, required super.deviceName})
: super( : super(
code: 'countdown_2', code: 'countdown_2',
operationName: 'Light 2 Countdown', operationName: 'Light 2 Countdown',

View File

@ -1,7 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart'; import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_scene_model.dart';
class RoutineDetailsModel { class RoutineDetailsModel {
final String spaceUuid; final String spaceUuid;

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/routiens/widgets/conditions_routines_devices_view.dart'; import 'package:syncrow_web/pages/routines/widgets/conditions_routines_devices_view.dart';
import 'package:syncrow_web/pages/routiens/widgets/if_container.dart'; import 'package:syncrow_web/pages/routines/widgets/if_container.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_search_and_buttons.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_search_and_buttons.dart';
import 'package:syncrow_web/pages/routiens/widgets/then_container.dart'; import 'package:syncrow_web/pages/routines/widgets/then_container.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class CreateNewRoutineView extends StatelessWidget { class CreateNewRoutineView extends StatelessWidget {
@ -68,7 +68,7 @@ class CreateNewRoutineView extends StatelessWidget {
width: double.infinity, width: double.infinity,
color: ColorsManager.dialogBlueTitle, color: ColorsManager.dialogBlueTitle,
), ),
/// THEN Container /// THEN Container
Expanded( Expanded(
child: Card( child: Card(

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/effictive_period_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/effictive_period_dialog.dart';
import 'package:syncrow_web/pages/routiens/widgets/period_option.dart'; import 'package:syncrow_web/pages/routines/widgets/period_option.dart';
import 'package:syncrow_web/pages/routiens/widgets/repeat_days.dart'; import 'package:syncrow_web/pages/routines/widgets/repeat_days.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class EffectivePeriodView extends StatelessWidget { class EffectivePeriodView extends StatelessWidget {

View File

@ -0,0 +1,91 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/view/create_new_routine_view.dart';
import 'package:syncrow_web/pages/routines/widgets/main_routine_view/fetch_routine_scenes_automation.dart';
import 'package:syncrow_web/pages/routines/widgets/main_routine_view/routine_view_card.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/snack_bar.dart';
class RoutinesView extends StatefulWidget {
const RoutinesView({super.key});
@override
State<RoutinesView> createState() => _RoutinesViewState();
}
class _RoutinesViewState extends State<RoutinesView> {
@override
void initState() {
super.initState();
context.read<RoutineBloc>().add(FetchDevicesInRoutine());
}
@override
Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) {
if (state.createRoutineView) {
return const CreateNewRoutineView();
}
return Row(
children: [
Expanded(
child: SpaceTreeView(
onSelect: () {},
)),
Expanded(
flex: 4,
child: ListView(children: [
Container(
padding: const EdgeInsets.all(16),
height: MediaQuery.sizeOf(context).height,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Create New Routines",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(
height: 10,
),
RoutineViewCard(
onTap: () {
if (context.read<SpaceTreeBloc>().selectedCommunityId.isNotEmpty &&
context.read<SpaceTreeBloc>().selectedSpaceId.isNotEmpty) {
context.read<RoutineBloc>().add(
(ResetRoutineState()),
);
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(createRoutineView: true),
);
} else {
CustomSnackBar.redSnackBar('Please select a space');
}
},
icon: Icons.add,
textString: '',
),
const SizedBox(
height: 15,
),
const Expanded(child: FetchRoutineScenesAutomation()),
],
),
),
]),
),
],
);
},
);
}
}

View File

@ -1,11 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart'; import 'package:syncrow_web/pages/routines/widgets/dragable_card.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_devices.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_devices.dart';
import 'package:syncrow_web/pages/routiens/widgets/routines_title_widget.dart'; import 'package:syncrow_web/pages/routines/widgets/routines_title_widget.dart';
import 'package:syncrow_web/pages/routiens/widgets/scenes_and_automations.dart'; import 'package:syncrow_web/pages/routines/widgets/scenes_and_automations.dart';
import 'package:syncrow_web/pages/routiens/widgets/search_bar_condition_title.dart'; import 'package:syncrow_web/pages/routines/widgets/search_bar_condition_title.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class ConditionsRoutinesDevicesView extends StatelessWidget { class ConditionsRoutinesDevicesView extends StatelessWidget {

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/custom_dialog.dart'; import 'package:syncrow_web/pages/common/custom_dialog.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class DeleteSceneWidget extends StatelessWidget { class DeleteSceneWidget extends StatelessWidget {

View File

@ -3,8 +3,8 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/helper/dialog_helper/device_dialog_helper.dart'; import 'package:syncrow_web/pages/routines/helper/dialog_helper/device_dialog_helper.dart';
import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart'; import 'package:syncrow_web/pages/routines/widgets/dragable_card.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -26,9 +26,7 @@ class IfContainer extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const Text('IF', const Text('IF', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold)),
if (state.isAutomation && state.ifItems.isNotEmpty) if (state.isAutomation && state.ifItems.isNotEmpty)
AutomationOperatorSelector( AutomationOperatorSelector(
selectedOperator: state.selectedAutomationOperator), selectedOperator: state.selectedAutomationOperator),
@ -55,44 +53,34 @@ class IfContainer extends StatelessWidget {
(index) => GestureDetector( (index) => GestureDetector(
onTap: () async { onTap: () async {
if (!state.isTabToRun) { if (!state.isTabToRun) {
final result = await DeviceDialogHelper final result = await DeviceDialogHelper.showDeviceDialog(
.showDeviceDialog( context, state.ifItems[index],
context, state.ifItems[index], removeComparetors: false);
removeComparetors: false);
if (result != null) { if (result != null) {
context.read<RoutineBloc>().add( context
AddToIfContainer( .read<RoutineBloc>()
state.ifItems[index], false)); .add(AddToIfContainer(state.ifItems[index], false));
} else if (![ } else if (!['AC', '1G', '2G', '3G']
'AC', .contains(state.ifItems[index]['productType'])) {
'1G', context
'2G', .read<RoutineBloc>()
'3G' .add(AddToIfContainer(state.ifItems[index], false));
].contains(
state.ifItems[index]['productType'])) {
context.read<RoutineBloc>().add(
AddToIfContainer(
state.ifItems[index], false));
} }
} }
}, },
child: DraggableCard( child: DraggableCard(
imagePath: imagePath: state.ifItems[index]['imagePath'] ?? '',
state.ifItems[index]['imagePath'] ?? '',
title: state.ifItems[index]['title'] ?? '', title: state.ifItems[index]['title'] ?? '',
deviceData: state.ifItems[index], deviceData: state.ifItems[index],
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
horizontal: 4, vertical: 8),
isFromThen: false, isFromThen: false,
isFromIf: true, isFromIf: true,
onRemove: () { onRemove: () {
context.read<RoutineBloc>().add( context.read<RoutineBloc>().add(RemoveDragCard(
RemoveDragCard( index: index,
index: index, isFromThen: false,
isFromThen: false, key: state.ifItems[index]['uniqueCustomId']));
key: state.ifItems[index]
['uniqueCustomId']));
}, },
), ),
)), )),
@ -113,23 +101,15 @@ class IfContainer extends StatelessWidget {
if (!state.isTabToRun) { if (!state.isTabToRun) {
if (mutableData['deviceId'] == 'tab_to_run') { if (mutableData['deviceId'] == 'tab_to_run') {
context context.read<RoutineBloc>().add(AddToIfContainer(mutableData, true));
.read<RoutineBloc>()
.add(AddToIfContainer(mutableData, true));
} else { } else {
final result = await DeviceDialogHelper.showDeviceDialog( final result = await DeviceDialogHelper.showDeviceDialog(context, mutableData,
context, mutableData,
removeComparetors: false); removeComparetors: false);
if (result != null) { if (result != null) {
context context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false));
.read<RoutineBloc>() } else if (!['AC', '1G', '2G', '3G'].contains(mutableData['productType'])) {
.add(AddToIfContainer(mutableData, false)); context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false));
} else if (!['AC', '1G', '2G', '3G']
.contains(mutableData['productType'])) {
context
.read<RoutineBloc>()
.add(AddToIfContainer(mutableData, false));
} }
} }
} }
@ -175,9 +155,7 @@ class AutomationOperatorSelector extends StatelessWidget {
), ),
), ),
onPressed: () { onPressed: () {
context context.read<RoutineBloc>().add(const ChangeAutomationOperator(operator: 'or'));
.read<RoutineBloc>()
.add(const ChangeAutomationOperator(operator: 'or'));
}, },
), ),
Container( Container(
@ -203,9 +181,7 @@ class AutomationOperatorSelector extends StatelessWidget {
), ),
), ),
onPressed: () { onPressed: () {
context context.read<RoutineBloc>().add(const ChangeAutomationOperator(operator: 'and'));
.read<RoutineBloc>()
.add(const ChangeAutomationOperator(operator: 'and'));
}, },
), ),
], ],

View File

@ -0,0 +1,143 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/widgets/main_routine_view/routine_view_card.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.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 FetchRoutineScenesAutomation extends StatefulWidget {
const FetchRoutineScenesAutomation({super.key});
@override
State<FetchRoutineScenesAutomation> createState() => _FetchRoutineScenesState();
}
class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
with HelperResponsiveLayout {
@override
void initState() {
super.initState();
context.read<RoutineBloc>()
..add(LoadScenes(context.read<SpaceTreeBloc>().selectedSpaceId,
context.read<SpaceTreeBloc>().selectedCommunityId))
..add(LoadAutomation(context.read<SpaceTreeBloc>().selectedSpaceId));
}
@override
Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) {
return state.isLoading
? const Center(
child: CircularProgressIndicator(),
)
: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Scenes (Tab to Run)",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
if (state.scenes.isEmpty)
Text(
"No scenes found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
),
),
if (state.scenes.isNotEmpty)
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: isSmallScreenSize(context) ? 160 : 170,
maxWidth: MediaQuery.sizeOf(context).width * 0.7),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: state.scenes.length,
itemBuilder: (context, index) => Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: RoutineViewCard(
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(createRoutineView: true),
);
context.read<RoutineBloc>().add(
GetSceneDetails(
sceneId: state.scenes[index].id,
isTabToRun: true,
isUpdate: true,
),
);
},
textString: state.scenes[index].name,
icon: state.scenes[index].icon ?? Assets.logoHorizontal,
isFromScenes: true,
iconInBytes: state.scenes[index].iconInBytes,
),
),
),
),
const SizedBox(height: 15),
Text(
"Automations",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
if (state.automations.isEmpty)
Text(
"No automations found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
),
),
if (state.automations.isNotEmpty)
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: isSmallScreenSize(context) ? 160 : 170,
maxWidth: MediaQuery.sizeOf(context).width * 0.7),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: state.automations.length,
itemBuilder: (context, index) => Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: RoutineViewCard(
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(createRoutineView: true),
);
context.read<RoutineBloc>().add(
GetAutomationDetails(
automationId: state.automations[index].id,
isAutomation: true,
isUpdate: true),
);
},
textString: state.automations[index].name,
icon: state.automations[index].icon ?? Assets.automation,
),
),
),
),
],
),
);
},
);
}
}

View File

@ -70,15 +70,13 @@ class RoutineViewCard extends StatelessWidget with HelperResponsiveLayout {
height: iconSize, height: iconSize,
width: iconSize, width: iconSize,
child: (isFromScenes ?? false) child: (isFromScenes ?? false)
? (iconInBytes != null && ? (iconInBytes != null && iconInBytes?.isNotEmpty == true)
iconInBytes?.isNotEmpty == true)
? Image.memory( ? Image.memory(
iconInBytes!, iconInBytes!,
height: iconSize, height: iconSize,
width: iconSize, width: iconSize,
fit: BoxFit.contain, fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) => errorBuilder: (context, error, stackTrace) => Image.asset(
Image.asset(
Assets.logo, Assets.logo,
height: iconSize, height: iconSize,
width: iconSize, width: iconSize,

View File

@ -1,9 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_event.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_state.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/effictive_period_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/effictive_period_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart';

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_event.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_state.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class RepeatDays extends StatelessWidget { class RepeatDays extends StatelessWidget {

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart'; import 'package:syncrow_web/pages/routines/widgets/dragable_card.dart';
class RoutineDevices extends StatelessWidget { class RoutineDevices extends StatelessWidget {
const RoutineDevices({super.key}); const RoutineDevices({super.key});
@ -35,9 +35,7 @@ class RoutineDevices extends StatelessWidget {
children: deviceList.asMap().entries.map((entry) { children: deviceList.asMap().entries.map((entry) {
final device = entry.value; final device = entry.value;
if (state.searchText != null && state.searchText!.isNotEmpty) { if (state.searchText != null && state.searchText!.isNotEmpty) {
return device.name! return device.name!.toLowerCase().contains(state.searchText!.toLowerCase())
.toLowerCase()
.contains(state.searchText!.toLowerCase())
? DraggableCard( ? DraggableCard(
imagePath: device.getDefaultIcon(device.productType), imagePath: device.getDefaultIcon(device.productType),
title: device.name ?? '', title: device.name ?? '',

View File

@ -1,16 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart'; import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_operational_value.dart'; import 'package:syncrow_web/pages/routines/models/ac/ac_operational_value.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/functions_bloc/functions_bloc_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
class ACHelper { class ACHelper {
static Future<Map<String, dynamic>?> showACFunctionsDialog( static Future<Map<String, dynamic>?> showACFunctionsDialog(
@ -27,16 +27,15 @@ class ACHelper {
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => FunctionBloc() create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog( child: AlertDialog(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>( content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) { builder: (context, state) {
final selectedFunction = state.selectedFunction; final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName; final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions final selectedFunctionData =
.firstWhere((f) => f.functionCode == selectedFunction, state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData( orElse: () => DeviceFunctionData(
entityId: '', entityId: '',
functionCode: selectedFunction ?? '', functionCode: selectedFunction ?? '',
@ -66,10 +65,8 @@ class ACHelper {
child: _buildFunctionsList( child: _buildFunctionsList(
context: context, context: context,
acFunctions: acFunctions, acFunctions: acFunctions,
onFunctionSelected: onFunctionSelected: (functionCode, operationName) =>
(functionCode, operationName) => context context.read<FunctionBloc>().add(SelectFunction(
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: functionCode, functionCode: functionCode,
operationName: operationName, operationName: operationName,
)), )),
@ -184,7 +181,7 @@ class ACHelper {
bool? removeComparators, bool? removeComparators,
}) { }) {
if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') { if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') {
final initialValue = selectedFunctionData?.value ?? 200; final initialValue = selectedFunctionData?.value ?? 250;
return _buildTemperatureSelector( return _buildTemperatureSelector(
context: context, context: context,
initialValue: initialValue, initialValue: initialValue,
@ -197,8 +194,7 @@ class ACHelper {
); );
} }
final selectedFn = final selectedFn = acFunctions.firstWhere((f) => f.code == selectedFunction);
acFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues(); final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList( return _buildOperationalValuesList(
@ -294,8 +290,7 @@ class ACHelper {
minHeight: 40.0, minHeight: 40.0,
minWidth: 40.0, minWidth: 40.0,
), ),
isSelected: isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
children: conditions.map((c) => Text(c)).toList(), children: conditions.map((c) => Text(c)).toList(),
); );
} }
@ -333,10 +328,10 @@ class ACHelper {
String selectCode, String selectCode,
) { ) {
return Slider( return Slider(
value: initialValue is int ? initialValue.toDouble() : 160.0, value: initialValue is int ? initialValue.toDouble() : 200.0,
min: 160, min: 200,
max: 300, max: 300,
divisions: 14, divisions: 10,
label: '${((initialValue ?? 160) / 10).toInt()}°C', label: '${((initialValue ?? 160) / 10).toInt()}°C',
onChanged: (value) { onChanged: (value) {
context.read<FunctionBloc>().add( context.read<FunctionBloc>().add(
@ -389,13 +384,9 @@ class ACHelper {
style: context.textTheme.bodyMedium, style: context.textTheme.bodyMedium,
), ),
trailing: Icon( trailing: Icon(
isSelected isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
size: 24, size: 24,
color: isSelected color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray,
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
if (!isSelected) { if (!isSelected) {
@ -407,8 +398,7 @@ class ACHelper {
operationName: operationName, operationName: operationName,
value: value.value, value: value.value,
condition: selectedFunctionData?.condition, condition: selectedFunctionData?.condition,
valueDescription: valueDescription: selectedFunctionData?.valueDescription,
selectedFunctionData?.valueDescription,
), ),
), ),
); );

View File

@ -1,10 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class AutomationDialog extends StatefulWidget { class AutomationDialog extends StatefulWidget {
@ -31,10 +31,8 @@ class _AutomationDialogState extends State<AutomationDialog> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
List<DeviceFunctionData>? functions = context List<DeviceFunctionData>? functions =
.read<RoutineBloc>() context.read<RoutineBloc>().state.selectedFunctions[widget.uniqueCustomId];
.state
.selectedFunctions[widget.uniqueCustomId];
for (DeviceFunctionData data in functions ?? []) { for (DeviceFunctionData data in functions ?? []) {
if (data.entityId == widget.automationId) { if (data.entityId == widget.automationId) {
selectedAutomationActionExecutor = data.value; selectedAutomationActionExecutor = data.value;
@ -67,8 +65,7 @@ class _AutomationDialogState extends State<AutomationDialog> {
}), }),
), ),
ListTile( ListTile(
leading: leading: SvgPicture.asset(Assets.acPowerOff, width: 24, height: 24),
SvgPicture.asset(Assets.acPowerOff, width: 24, height: 24),
title: const Text('Disable'), title: const Text('Disable'),
trailing: Radio<String?>( trailing: Radio<String?>(
value: 'rule_disable', value: 'rule_disable',

View File

@ -1,10 +1,10 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
class DelayHelper { class DelayHelper {
static Future<Map<String, dynamic>?> showDelayPickerDialog( static Future<Map<String, dynamic>?> showDelayPickerDialog(

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_event.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';

View File

@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/functions_bloc/functions_bloc_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/helper/duration_format_helper.dart'; import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -22,23 +22,21 @@ class OneGangSwitchHelper {
String uniqueCustomId, String uniqueCustomId,
bool removeComparetors, bool removeComparetors,
) async { ) async {
List<BaseSwitchFunction> acFunctions = List<BaseSwitchFunction> acFunctions = functions.whereType<BaseSwitchFunction>().toList();
functions.whereType<BaseSwitchFunction>().toList();
return showDialog<Map<String, dynamic>?>( return showDialog<Map<String, dynamic>?>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => FunctionBloc() create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog( child: AlertDialog(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>( content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) { builder: (context, state) {
final selectedFunction = state.selectedFunction; final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName; final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions final selectedFunctionData =
.firstWhere((f) => f.functionCode == selectedFunction, state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData( orElse: () => DeviceFunctionData(
entityId: '', entityId: '',
functionCode: selectedFunction ?? '', functionCode: selectedFunction ?? '',
@ -85,12 +83,9 @@ class OneGangSwitchHelper {
color: ColorsManager.textGray, color: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
context context.read<FunctionBloc>().add(SelectFunction(
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: function.code, functionCode: function.code,
operationName: operationName: function.operationName,
function.operationName,
)); ));
}, },
); );
@ -180,8 +175,7 @@ class OneGangSwitchHelper {
); );
} }
final selectedFn = final selectedFn = acFunctions.firstWhere((f) => f.code == selectedFunction);
acFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues(); final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList( return _buildOperationalValuesList(
@ -218,11 +212,11 @@ class OneGangSwitchHelper {
selectedFunctionData, selectedFunctionData,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownDisplay(context, initialValue, device, operationName, _buildCountDownDisplay(
selectedFunctionData, selectCode), context, initialValue, device, operationName, selectedFunctionData, selectCode),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownSlider(context, initialValue, device, operationName, _buildCountDownSlider(
selectedFunctionData, selectCode), context, initialValue, device, operationName, selectedFunctionData, selectCode),
], ],
); );
} }
@ -263,8 +257,7 @@ class OneGangSwitchHelper {
minHeight: 40.0, minHeight: 40.0,
minWidth: 40.0, minWidth: 40.0,
), ),
isSelected: isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
children: conditions.map((c) => Text(c)).toList(), children: conditions.map((c) => Text(c)).toList(),
); );
} }
@ -312,8 +305,7 @@ class OneGangSwitchHelper {
value: (initialValue ?? 0).toDouble(), value: (initialValue ?? 0).toDouble(),
min: operationalValues.minValue?.toDouble() ?? 0.0, min: operationalValues.minValue?.toDouble() ?? 0.0,
max: operationalValues.maxValue?.toDouble() ?? 0.0, max: operationalValues.maxValue?.toDouble() ?? 0.0,
divisions: (((operationalValues.maxValue ?? 0) - divisions: (((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
(operationalValues.minValue ?? 0)) /
(operationalValues.stepValue ?? 1)) (operationalValues.stepValue ?? 1))
.round(), .round(),
onChanged: (value) { onChanged: (value) {
@ -365,13 +357,9 @@ class OneGangSwitchHelper {
style: context.textTheme.bodyMedium, style: context.textTheme.bodyMedium,
), ),
trailing: Icon( trailing: Icon(
isSelected isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
size: 24, size: 24,
color: isSelected color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray,
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
if (!isSelected) { if (!isSelected) {
@ -383,8 +371,7 @@ class OneGangSwitchHelper {
operationName: operationName, operationName: operationName,
value: value.value, value: value.value,
condition: selectedFunctionData?.condition, condition: selectedFunctionData?.condition,
valueDescription: valueDescription: selectedFunctionData?.valueDescription,
selectedFunctionData?.valueDescription,
), ),
), ),
); );

View File

@ -1,17 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_event.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_state.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/setting_bloc/setting_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_event.dart'; import 'package:syncrow_web/pages/routines/bloc/setting_bloc/setting_event.dart';
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_state.dart'; import 'package:syncrow_web/pages/routines/bloc/setting_bloc/setting_state.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/pages/routiens/models/icon_model.dart'; import 'package:syncrow_web/pages/routines/models/icon_model.dart';
import 'package:syncrow_web/pages/routiens/view/effective_period_view.dart'; import 'package:syncrow_web/pages/routines/view/effective_period_view.dart';
import 'package:syncrow_web/pages/routiens/widgets/delete_scene.dart'; import 'package:syncrow_web/pages/routines/widgets/delete_scene.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';

View File

@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/functions_bloc/functions_bloc_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/helper/duration_format_helper.dart'; import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -22,23 +22,21 @@ class ThreeGangSwitchHelper {
String uniqueCustomId, String uniqueCustomId,
bool removeComparetors, bool removeComparetors,
) async { ) async {
List<BaseSwitchFunction> switchFunctions = List<BaseSwitchFunction> switchFunctions = functions.whereType<BaseSwitchFunction>().toList();
functions.whereType<BaseSwitchFunction>().toList();
return showDialog<Map<String, dynamic>?>( return showDialog<Map<String, dynamic>?>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => FunctionBloc() create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog( child: AlertDialog(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>( content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) { builder: (context, state) {
final selectedFunction = state.selectedFunction; final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName; final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions final selectedFunctionData =
.firstWhere((f) => f.functionCode == selectedFunction, state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData( orElse: () => DeviceFunctionData(
entityId: '', entityId: '',
functionCode: selectedFunction ?? '', functionCode: selectedFunction ?? '',
@ -85,12 +83,9 @@ class ThreeGangSwitchHelper {
color: ColorsManager.textGray, color: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
context context.read<FunctionBloc>().add(SelectFunction(
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: function.code, functionCode: function.code,
operationName: operationName: function.operationName,
function.operationName,
)); ));
}, },
); );
@ -182,8 +177,7 @@ class ThreeGangSwitchHelper {
); );
} }
final selectedFn = final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
switchFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues(); final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList( return _buildOperationalValuesList(
@ -220,11 +214,11 @@ class ThreeGangSwitchHelper {
selectedFunctionData, selectedFunctionData,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownDisplay(context, initialValue, device, operationName, _buildCountDownDisplay(
selectedFunctionData, selectCode), context, initialValue, device, operationName, selectedFunctionData, selectCode),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownSlider(context, initialValue, device, operationName, _buildCountDownSlider(
selectedFunctionData, selectCode), context, initialValue, device, operationName, selectedFunctionData, selectCode),
], ],
); );
} }
@ -265,8 +259,7 @@ class ThreeGangSwitchHelper {
minHeight: 40.0, minHeight: 40.0,
minWidth: 40.0, minWidth: 40.0,
), ),
isSelected: isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
children: conditions.map((c) => Text(c)).toList(), children: conditions.map((c) => Text(c)).toList(),
); );
} }
@ -314,8 +307,7 @@ class ThreeGangSwitchHelper {
value: (initialValue ?? 0).toDouble(), value: (initialValue ?? 0).toDouble(),
min: operationalValues.minValue?.toDouble() ?? 0.0, min: operationalValues.minValue?.toDouble() ?? 0.0,
max: operationalValues.maxValue?.toDouble() ?? 0.0, max: operationalValues.maxValue?.toDouble() ?? 0.0,
divisions: (((operationalValues.maxValue ?? 0) - divisions: (((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
(operationalValues.minValue ?? 0)) /
(operationalValues.stepValue ?? 1)) (operationalValues.stepValue ?? 1))
.round(), .round(),
onChanged: (value) { onChanged: (value) {
@ -367,13 +359,9 @@ class ThreeGangSwitchHelper {
style: context.textTheme.bodyMedium, style: context.textTheme.bodyMedium,
), ),
trailing: Icon( trailing: Icon(
isSelected isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
size: 24, size: 24,
color: isSelected color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray,
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
if (!isSelected) { if (!isSelected) {
@ -385,8 +373,7 @@ class ThreeGangSwitchHelper {
operationName: operationName, operationName: operationName,
value: value.value, value: value.value,
condition: selectedFunctionData?.condition, condition: selectedFunctionData?.condition,
valueDescription: valueDescription: selectedFunctionData?.valueDescription,
selectedFunctionData?.valueDescription,
), ),
), ),
); );

View File

@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/functions_bloc/functions_bloc_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/helper/duration_format_helper.dart'; import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/base_switch_function.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/switch_operational_value.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_footer.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
import 'package:syncrow_web/pages/routiens/widgets/dialog_header.dart'; import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -22,23 +22,21 @@ class TwoGangSwitchHelper {
String uniqueCustomId, String uniqueCustomId,
bool removeComparetors, bool removeComparetors,
) async { ) async {
List<BaseSwitchFunction> switchFunctions = List<BaseSwitchFunction> switchFunctions = functions.whereType<BaseSwitchFunction>().toList();
functions.whereType<BaseSwitchFunction>().toList();
return showDialog<Map<String, dynamic>?>( return showDialog<Map<String, dynamic>?>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => FunctionBloc() create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog( child: AlertDialog(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>( content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) { builder: (context, state) {
final selectedFunction = state.selectedFunction; final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName; final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions final selectedFunctionData =
.firstWhere((f) => f.functionCode == selectedFunction, state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData( orElse: () => DeviceFunctionData(
entityId: '', entityId: '',
functionCode: selectedFunction ?? '', functionCode: selectedFunction ?? '',
@ -85,12 +83,9 @@ class TwoGangSwitchHelper {
color: ColorsManager.textGray, color: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
context context.read<FunctionBloc>().add(SelectFunction(
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: function.code, functionCode: function.code,
operationName: operationName: function.operationName,
function.operationName,
)); ));
}, },
); );
@ -166,8 +161,7 @@ class TwoGangSwitchHelper {
required String operationName, required String operationName,
required bool removeComparetors, required bool removeComparetors,
}) { }) {
if (selectedFunction == 'countdown_1' || if (selectedFunction == 'countdown_1' || selectedFunction == 'countdown_2') {
selectedFunction == 'countdown_2') {
final initialValue = selectedFunctionData?.value ?? 200; final initialValue = selectedFunctionData?.value ?? 200;
return _buildTemperatureSelector( return _buildTemperatureSelector(
context: context, context: context,
@ -181,8 +175,7 @@ class TwoGangSwitchHelper {
); );
} }
final selectedFn = final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
switchFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues(); final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList( return _buildOperationalValuesList(
@ -219,11 +212,11 @@ class TwoGangSwitchHelper {
selectedFunctionData, selectedFunctionData,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownDisplay(context, initialValue, device, operationName, _buildCountDownDisplay(
selectedFunctionData, selectCode), context, initialValue, device, operationName, selectedFunctionData, selectCode),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownSlider(context, initialValue, device, operationName, _buildCountDownSlider(
selectedFunctionData, selectCode), context, initialValue, device, operationName, selectedFunctionData, selectCode),
], ],
); );
} }
@ -264,8 +257,7 @@ class TwoGangSwitchHelper {
minHeight: 40.0, minHeight: 40.0,
minWidth: 40.0, minWidth: 40.0,
), ),
isSelected: isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
children: conditions.map((c) => Text(c)).toList(), children: conditions.map((c) => Text(c)).toList(),
); );
} }
@ -313,8 +305,7 @@ class TwoGangSwitchHelper {
value: (initialValue ?? 0).toDouble(), value: (initialValue ?? 0).toDouble(),
min: operationalValues.minValue?.toDouble() ?? 0.0, min: operationalValues.minValue?.toDouble() ?? 0.0,
max: operationalValues.maxValue?.toDouble() ?? 0.0, max: operationalValues.maxValue?.toDouble() ?? 0.0,
divisions: (((operationalValues.maxValue ?? 0) - divisions: (((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
(operationalValues.minValue ?? 0)) /
(operationalValues.stepValue ?? 1)) (operationalValues.stepValue ?? 1))
.round(), .round(),
onChanged: (value) { onChanged: (value) {
@ -366,13 +357,9 @@ class TwoGangSwitchHelper {
style: context.textTheme.bodyMedium, style: context.textTheme.bodyMedium,
), ),
trailing: Icon( trailing: Icon(
isSelected isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
size: 24, size: 24,
color: isSelected color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray,
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
if (!isSelected) { if (!isSelected) {
@ -384,8 +371,7 @@ class TwoGangSwitchHelper {
operationName: operationName, operationName: operationName,
value: value.value, value: value.value,
condition: selectedFunctionData?.condition, condition: selectedFunctionData?.condition,
valueDescription: valueDescription: selectedFunctionData?.valueDescription,
selectedFunctionData?.valueDescription,
), ),
), ),
); );

View File

@ -1,10 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/helper/save_routine_helper.dart'; import 'package:syncrow_web/pages/routines/helper/save_routine_helper.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/discard_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/discard_dialog.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/setting_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/setting_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart'; import 'package:syncrow_web/pages/routines/widgets/dragable_card.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class ScenesAndAutomations extends StatefulWidget { class ScenesAndAutomations extends StatefulWidget {
@ -18,8 +19,9 @@ class _ScenesAndAutomationsState extends State<ScenesAndAutomations> {
void initState() { void initState() {
super.initState(); super.initState();
context.read<RoutineBloc>() context.read<RoutineBloc>()
..add(const LoadScenes(spaceId, communityId)) ..add(LoadScenes(context.read<SpaceTreeBloc>().selectedSpaceId,
..add(const LoadAutomation(spaceId)); context.read<SpaceTreeBloc>().selectedCommunityId))
..add(LoadAutomation(context.read<SpaceTreeBloc>().selectedSpaceId));
} }
@override @override
@ -34,9 +36,7 @@ class _ScenesAndAutomationsState extends State<ScenesAndAutomations> {
children: scenes.asMap().entries.map((entry) { children: scenes.asMap().entries.map((entry) {
final scene = entry.value; final scene = entry.value;
if (state.searchText != null && state.searchText!.isNotEmpty) { if (state.searchText != null && state.searchText!.isNotEmpty) {
return scene.name return scene.name.toLowerCase().contains(state.searchText!.toLowerCase())
.toLowerCase()
.contains(state.searchText!.toLowerCase())
? DraggableCard( ? DraggableCard(
imagePath: scene.icon ?? Assets.loginLogo, imagePath: scene.icon ?? Assets.loginLogo,
title: scene.name, title: scene.name,

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/text_field/custom_text_field.dart'; import 'package:syncrow_web/pages/common/text_field/custom_text_field.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/routines_title_widget.dart'; import 'package:syncrow_web/pages/routines/widgets/routines_title_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';

View File

@ -2,11 +2,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/automation_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/automation_dialog.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/delay_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/delay_dialog.dart';
import 'package:syncrow_web/pages/routiens/helper/dialog_helper/device_dialog_helper.dart'; import 'package:syncrow_web/pages/routines/helper/dialog_helper/device_dialog_helper.dart';
import 'package:syncrow_web/pages/routiens/widgets/dragable_card.dart'; import 'package:syncrow_web/pages/routines/widgets/dragable_card.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';

View File

@ -0,0 +1,291 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/services/space_mana_api.dart';
class SpaceTreeBloc extends Bloc<SpaceTreeEvent, SpaceTreeState> {
String selectedCommunityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9';
String selectedSpaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6';
SpaceTreeBloc() : super(const SpaceTreeState()) {
on<InitialEvent>(_fetchSpaces);
on<OnCommunityExpanded>(_onCommunityExpanded);
on<OnSpaceExpanded>(_onSpaceExpanded);
on<OnCommunitySelected>(_onCommunitySelected);
on<OnSpaceSelected>(_onSpaceSelected);
on<SearchQueryEvent>(_onSearch);
}
_fetchSpaces(InitialEvent event, Emitter<SpaceTreeState> emit) async {
emit(SpaceTreeLoadingState());
try {
List<CommunityModel> communities = await CommunitySpaceManagementApi().fetchCommunities();
List<CommunityModel> updatedCommunities = await Future.wait(
communities.map((community) async {
List<SpaceModel> spaces =
await CommunitySpaceManagementApi().getSpaceHierarchy(community.uuid);
return CommunityModel(
uuid: community.uuid,
createdAt: community.createdAt,
updatedAt: community.updatedAt,
name: community.name,
description: community.description,
spaces: spaces,
region: community.region,
);
}).toList(),
);
emit(state.copyWith(
communitiesList: updatedCommunities, expandedCommunity: [], expandedSpaces: []));
} catch (e) {
emit(SpaceTreeErrorState('Error loading communities and spaces: $e'));
}
}
_onCommunityExpanded(OnCommunityExpanded event, Emitter<SpaceTreeState> emit) async {
try {
List<String> updatedExpandedCommunityList = List.from(state.expandedCommunities);
if (updatedExpandedCommunityList.contains(event.communityId)) {
updatedExpandedCommunityList.remove(event.communityId);
} else {
updatedExpandedCommunityList.add(event.communityId);
}
emit(state.copyWith(
expandedCommunity: updatedExpandedCommunityList,
));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
_onSpaceExpanded(OnSpaceExpanded event, Emitter<SpaceTreeState> emit) async {
try {
List<String> updatedExpandedSpacesList = List.from(state.expandedSpaces);
if (updatedExpandedSpacesList.contains(event.spaceId)) {
updatedExpandedSpacesList.remove(event.spaceId);
} else {
updatedExpandedSpacesList.add(event.spaceId);
}
emit(state.copyWith(expandedSpaces: updatedExpandedSpacesList));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
_onCommunitySelected(OnCommunitySelected event, Emitter<SpaceTreeState> emit) async {
try {
List<String> updatedSelectedCommunities =
List.from(state.selectedCommunities.toSet().toList());
List<String> updatedSelectedSpaces = List.from(state.selectedSpaces.toSet().toList());
List<String> updatedSoldChecks = List.from(state.soldCheck.toSet().toList());
Map<String, List<String>> communityAndSpaces = Map.from(state.selectedCommunityAndSpaces);
List<String> childrenIds = _getAllChildIds(event.children);
if (!updatedSelectedCommunities.contains(event.communityId)) {
// Select the community and all its children
updatedSelectedCommunities.add(event.communityId);
updatedSelectedSpaces.addAll(childrenIds);
} else {
// Unselect the community and all its children
updatedSelectedCommunities.remove(event.communityId);
updatedSelectedSpaces.removeWhere(childrenIds.contains);
updatedSoldChecks.removeWhere(childrenIds.contains);
}
communityAndSpaces[event.communityId] = updatedSelectedSpaces;
emit(state.copyWith(
selectedCommunities: updatedSelectedCommunities,
selectedSpaces: updatedSelectedSpaces,
soldCheck: updatedSoldChecks,
selectedCommunityAndSpaces: communityAndSpaces));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
_onSpaceSelected(OnSpaceSelected event, Emitter<SpaceTreeState> emit) async {
try {
List<String> updatedSelectedCommunities =
List.from(state.selectedCommunities.toSet().toList());
List<String> updatedSelectedSpaces = List.from(state.selectedSpaces.toSet().toList());
List<String> updatedSoldChecks = List.from(state.soldCheck.toSet().toList());
Map<String, List<String>> communityAndSpaces = Map.from(state.selectedCommunityAndSpaces);
List<String> childrenIds = _getAllChildIds(event.children);
bool isChildSelected = false;
for (String id in childrenIds) {
if (updatedSelectedSpaces.contains(id)) {
isChildSelected = true;
}
}
if (!updatedSelectedSpaces.contains(event.spaceId) &&
!updatedSoldChecks.contains(event.spaceId)) {
// First click: Select the space and all its children
updatedSelectedSpaces.add(event.spaceId);
updatedSelectedCommunities.add(event.communityId);
if (childrenIds.isNotEmpty) {
updatedSelectedSpaces.addAll(childrenIds);
}
List<String> spaces = _getThePathToChild(event.communityId, event.spaceId);
for (String space in spaces) {
if (!updatedSelectedSpaces.contains(space) && !updatedSoldChecks.contains(space)) {
updatedSoldChecks.add(space);
}
}
} else if (!updatedSoldChecks.contains(event.spaceId) &&
childrenIds.isNotEmpty &&
isChildSelected) {
// Second click: Unselect space but keep children
updatedSelectedSpaces.remove(event.spaceId);
updatedSoldChecks.add(event.spaceId);
} else {
// Third click: Unselect space and all its children
updatedSelectedSpaces.remove(event.spaceId);
if (childrenIds.isNotEmpty) {
updatedSelectedSpaces.removeWhere(childrenIds.contains);
updatedSoldChecks.removeWhere(childrenIds.contains);
}
updatedSoldChecks.remove(event.spaceId);
List<String> parents = _getThePathToChild(event.communityId, event.spaceId);
if (!_parentSelected(parents, updatedSelectedSpaces)) {
updatedSoldChecks.removeWhere(parents.contains);
}
if (!_anySpacesSelectedInCommunity(
event.communityId, updatedSelectedSpaces, updatedSoldChecks)) {
updatedSelectedCommunities.remove(event.communityId);
}
}
communityAndSpaces[event.communityId] = updatedSelectedSpaces;
emit(state.copyWith(
selectedCommunities: updatedSelectedCommunities,
selectedSpaces: updatedSelectedSpaces,
soldCheck: updatedSoldChecks,
selectedCommunityAndSpaces: communityAndSpaces));
emit(state.copyWith(selectedSpaces: updatedSelectedSpaces));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
_parentSelected(List<String> parents, List<String> selectedSpaces) {
for (String space in parents) {
if (selectedSpaces.contains(space)) {
return true;
}
}
return false;
}
_onSearch(SearchQueryEvent event, Emitter<SpaceTreeState> emit) async {
try {
List<CommunityModel> communities = List.from(state.communityList);
List<CommunityModel> filteredCommunity = [];
// Filter communities and expand only those that match the query
filteredCommunity = communities.where((community) {
final containsQueryInCommunity =
community.name.toLowerCase().contains(event.searchQuery.toLowerCase());
final containsQueryInSpaces =
community.spaces.any((space) => _containsQuery(space, event.searchQuery.toLowerCase()));
return containsQueryInCommunity || containsQueryInSpaces;
}).toList();
emit(state.copyWith(
filteredCommunity: filteredCommunity, isSearching: event.searchQuery.isNotEmpty));
} catch (e) {
emit(const SpaceTreeErrorState('Something went wrong'));
}
}
// Helper function to determine if any space or its children match the search query
bool _containsQuery(SpaceModel space, String query) {
final matchesSpace = space.name.toLowerCase().contains(query);
final matchesChildren =
space.children.any((child) => _containsQuery(child, query)); // Recursive check for children
return matchesSpace || matchesChildren;
}
List<String> _getAllChildIds(List<SpaceModel> spaces) {
List<String> ids = [];
for (var child in spaces) {
ids.add(child.uuid!);
ids.addAll(_getAllChildIds(child.children));
}
return ids;
}
bool _anySpacesSelectedInCommunity(
String communityId, List<String> selectedSpaces, List<String> partialCheckedList) {
bool result = false;
for (var community in state.communityList) {
if (community.uuid == communityId) {
List<String> ids = _getAllChildIds(community.spaces);
for (var id in ids) {
result = selectedSpaces.contains(id) || partialCheckedList.contains(id);
if (result) {
return result;
}
}
}
}
return result;
}
List<String> _getThePathToChild(String communityId, String selectedSpaceId) {
List<String> ids = [];
for (var community in state.communityList) {
if (community.uuid == communityId) {
for (var space in community.spaces) {
List<String> list = [];
list.add(space.uuid!);
ids = _getAllParentsIds(space, selectedSpaceId, List.from(list));
if (ids.isNotEmpty) {
return ids;
}
}
}
}
return ids;
}
List<String> _getAllParentsIds(SpaceModel child, String spaceId, List<String> listIds) {
List<String> ids = listIds;
ids.add(child.uuid ?? '');
if (child.uuid == spaceId) {
return ids;
}
if (child.children.isNotEmpty) {
for (var space in child.children) {
var result = _getAllParentsIds(space, spaceId, List.from(ids));
if (result.isNotEmpty) {
return result;
}
}
}
ids.removeLast();
return [];
}
}

View File

@ -0,0 +1,69 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
class SpaceTreeEvent extends Equatable {
const SpaceTreeEvent();
@override
List<Object> get props => [];
}
class InitialEvent extends SpaceTreeEvent {}
class SearchForSpace extends SpaceTreeEvent {
final String searchQuery;
const SearchForSpace(this.searchQuery);
@override
List<Object> get props => [searchQuery];
}
class OnCommunityExpanded extends SpaceTreeEvent {
final String communityId;
const OnCommunityExpanded(this.communityId);
@override
List<Object> get props => [communityId];
}
class OnCommunitySelected extends SpaceTreeEvent {
final String communityId;
final List<SpaceModel> children;
const OnCommunitySelected(this.communityId, this.children);
@override
List<Object> get props => [communityId, children];
}
class OnSpaceExpanded extends SpaceTreeEvent {
final String communityId;
final String spaceId;
const OnSpaceExpanded(this.communityId, this.spaceId);
@override
List<Object> get props => [communityId, spaceId];
}
class OnSpaceSelected extends SpaceTreeEvent {
final String communityId;
final String spaceId;
final List<SpaceModel> children;
const OnSpaceSelected(this.communityId, this.spaceId, this.children);
@override
List<Object> get props => [communityId, spaceId, children];
}
class SearchQueryEvent extends SpaceTreeEvent {
final String searchQuery;
const SearchQueryEvent(this.searchQuery);
@override
List<Object> get props => [searchQuery];
}

View File

@ -0,0 +1,70 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
class SpaceTreeState extends Equatable {
final Map<String, List<String>> selectedCommunityAndSpaces;
final List<CommunityModel> communityList;
final List<CommunityModel> filteredCommunity;
final List<String> expandedCommunities;
final List<String> expandedSpaces;
final List<String> selectedCommunities;
final List<String> selectedSpaces;
final List<String> soldCheck;
final bool isSearching;
const SpaceTreeState(
{this.communityList = const [],
this.filteredCommunity = const [],
this.expandedCommunities = const [],
this.expandedSpaces = const [],
this.selectedCommunities = const [],
this.selectedSpaces = const [],
this.soldCheck = const [],
this.isSearching = false,
this.selectedCommunityAndSpaces = const {}});
SpaceTreeState copyWith(
{List<CommunityModel>? communitiesList,
List<CommunityModel>? filteredCommunity,
List<String>? expandedSpaces,
List<String>? expandedCommunity,
List<String>? selectedCommunities,
List<String>? selectedSpaces,
List<String>? soldCheck,
bool? isSearching,
Map<String, List<String>>? selectedCommunityAndSpaces}) {
return SpaceTreeState(
communityList: communitiesList ?? this.communityList,
filteredCommunity: filteredCommunity ?? this.filteredCommunity,
expandedSpaces: expandedSpaces ?? this.expandedSpaces,
expandedCommunities: expandedCommunity ?? this.expandedCommunities,
selectedCommunities: selectedCommunities ?? this.selectedCommunities,
selectedSpaces: selectedSpaces ?? this.selectedSpaces,
soldCheck: soldCheck ?? this.soldCheck,
isSearching: isSearching ?? this.isSearching,
selectedCommunityAndSpaces: selectedCommunityAndSpaces ?? this.selectedCommunityAndSpaces);
}
@override
List<Object?> get props => [
communityList,
filteredCommunity,
expandedSpaces,
expandedCommunities,
selectedCommunities,
selectedSpaces,
soldCheck,
isSearching,
selectedCommunityAndSpaces
];
}
class SpaceTreeLoadingState extends SpaceTreeState {}
class SpaceTreeErrorState extends SpaceTreeState {
final String message;
const SpaceTreeErrorState(this.message);
@override
List<Object?> get props => [message];
}

View File

@ -0,0 +1,99 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class CustomExpansionTileSpaceTree extends StatelessWidget {
final String? spaceId;
final String title;
final List<Widget>? children;
final bool isSelected;
final bool isSoldCheck;
final bool isExpanded;
final Function? onExpansionChanged;
final Function? onItemSelected;
const CustomExpansionTileSpaceTree(
{super.key,
this.spaceId,
required this.title,
this.children,
this.isExpanded = false,
this.onExpansionChanged,
this.onItemSelected,
required this.isSelected,
this.isSoldCheck = false});
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
Checkbox(
value: isSoldCheck ? null : isSelected,
onChanged: (bool? value) {
if (onItemSelected != null) {
onItemSelected!();
}
},
tristate: true,
side: WidgetStateBorderSide.resolveWith((states) {
return const BorderSide(color: ColorsManager.grayBorder);
}),
fillColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return ColorsManager.blue1;
} else {
return ColorsManager.checkBoxFillColor;
}
}),
checkColor: ColorsManager.whiteColors,
),
if (children != null && children!.isNotEmpty)
InkWell(
onTap: () {
if (onExpansionChanged != null) {
onExpansionChanged!();
}
},
child: Icon(
isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right,
color: ColorsManager.lightGrayColor,
size: 16.0,
),
),
Expanded(
child: GestureDetector(
onTap: () {
if (onItemSelected != null) {
onItemSelected!();
}
},
child: Text(
_capitalizeFirstLetter(title),
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: isSelected
? ColorsManager.blackColor // Change color to black when selected
: ColorsManager.lightGrayColor, // Gray when not selected
fontWeight: FontWeight.w400,
),
),
),
),
],
),
if (isExpanded && children != null && children!.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 48.0),
child: Column(
children: children ?? [],
),
),
],
);
}
String _capitalizeFirstLetter(String text) {
if (text.isEmpty) return text;
return text[0].toUpperCase() + text.substring(1);
}
}

View File

@ -0,0 +1,220 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/common/widgets/search_bar.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart';
import 'package:syncrow_web/pages/space_tree/view/custom_expansion.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/utils/color_manager.dart';
import 'package:syncrow_web/utils/style.dart';
class SpaceTreeView extends StatefulWidget {
final Function onSelect;
const SpaceTreeView({required this.onSelect, super.key});
@override
State<SpaceTreeView> createState() => _SpaceTreeViewState();
}
class _SpaceTreeViewState extends State<SpaceTreeView> {
final ScrollController _scrollController = ScrollController();
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) {
List<CommunityModel> list = state.isSearching ? state.filteredCommunity : state.communityList;
return Container(
height: MediaQuery.sizeOf(context).height,
decoration: subSectionContainerDecoration,
child: state is SpaceTreeLoadingState
? const Center(child: CircularProgressIndicator())
: Column(
children: [
CustomSearchBar(
onSearchChanged: (query) {
context.read<SpaceTreeBloc>().add(SearchQueryEvent(query));
},
),
const SizedBox(height: 16),
Expanded(
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
children: [
Container(
width: MediaQuery.sizeOf(context).width * 0.5,
padding: const EdgeInsets.all(8.0),
child: list.isEmpty
? Center(
child: Text(
'No results found',
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: ColorsManager.lightGrayColor,
fontWeight: FontWeight.w400,
),
),
)
: Scrollbar(
scrollbarOrientation: ScrollbarOrientation.left,
thumbVisibility: true,
controller: _scrollController,
child: Padding(
padding: const EdgeInsets.only(left: 16),
child: ListView(
controller: _scrollController,
shrinkWrap: true,
children: list
.map(
(community) => CustomExpansionTileSpaceTree(
title: community.name,
isSelected: state.selectedCommunities
.contains(community.uuid),
isSoldCheck: state.selectedCommunities
.contains(community.uuid),
onExpansionChanged: () {
context
.read<SpaceTreeBloc>()
.add(OnCommunityExpanded(community.uuid));
},
isExpanded: state.expandedCommunities
.contains(community.uuid),
onItemSelected: () {
context.read<SpaceTreeBloc>().add(
OnCommunitySelected(
community.uuid, community.spaces));
widget.onSelect();
},
children: community.spaces.map((space) {
return CustomExpansionTileSpaceTree(
title: space.name,
isExpanded:
state.expandedSpaces.contains(space.uuid),
onItemSelected: () {
context.read<SpaceTreeBloc>().add(
OnSpaceSelected(community.uuid,
space.uuid ?? '', space.children));
widget.onSelect();
},
onExpansionChanged: () {
context.read<SpaceTreeBloc>().add(
OnSpaceExpanded(
community.uuid, space.uuid ?? ''));
},
isSelected:
state.selectedSpaces.contains(space.uuid) ||
state.soldCheck.contains(space.uuid),
isSoldCheck: state.soldCheck.contains(space.uuid),
children: _buildNestedSpaces(
context, state, space, community.uuid),
);
}).toList(),
),
)
.toList(),
),
),
),
),
],
),
),
// Expanded(
// child: Padding(
// padding: const EdgeInsets.all(8.0),
// child: list.isEmpty
// ? Center(
// child: Text(
// 'No results found',
// style: Theme.of(context).textTheme.bodySmall!.copyWith(
// color: ColorsManager.lightGrayColor, // Gray when not selected
// fontWeight: FontWeight.w400,
// ),
// ),
// )
// : ListView(
// shrinkWrap: true,
// children: list
// .map(
// (community) => CustomExpansionTileSpaceTree(
// title: community.name,
// isSelected:
// state.selectedCommunities.contains(community.uuid),
// isSoldCheck:
// state.selectedCommunities.contains(community.uuid),
// onExpansionChanged: () {
// context
// .read<SpaceTreeBloc>()
// .add(OnCommunityExpanded(community.uuid));
// },
// isExpanded:
// state.expandedCommunities.contains(community.uuid),
// onItemSelected: () {
// context.read<SpaceTreeBloc>().add(
// OnCommunitySelected(community.uuid, community.spaces));
// onSelect();
// },
// children: community.spaces.map((space) {
// return CustomExpansionTileSpaceTree(
// title: space.name,
// isExpanded: state.expandedSpaces.contains(space.uuid),
// onItemSelected: () {
// context.read<SpaceTreeBloc>().add(OnSpaceSelected(
// community.uuid, space.uuid ?? '', space.children));
// onSelect();
// },
// onExpansionChanged: () {
// context.read<SpaceTreeBloc>().add(
// OnSpaceExpanded(community.uuid, space.uuid ?? ''));
// },
// isSelected: state.selectedSpaces.contains(space.uuid) ||
// state.soldCheck.contains(space.uuid),
// isSoldCheck: state.soldCheck.contains(space.uuid),
// children: _buildNestedSpaces(
// context, state, space, community.uuid),
// );
// }).toList(),
// ),
// )
// .toList(),
// ),
// ),
// ),
],
),
);
});
}
List<Widget> _buildNestedSpaces(
BuildContext context, SpaceTreeState state, SpaceModel space, String communityId) {
return space.children.map((child) {
return CustomExpansionTileSpaceTree(
isSelected:
state.selectedSpaces.contains(child.uuid) || state.soldCheck.contains(child.uuid),
isSoldCheck: state.soldCheck.contains(child.uuid),
title: child.name,
isExpanded: state.expandedSpaces.contains(child.uuid),
onItemSelected: () {
context
.read<SpaceTreeBloc>()
.add(OnSpaceSelected(communityId, child.uuid ?? '', child.children));
widget.onSelect();
},
onExpansionChanged: () {
context.read<SpaceTreeBloc>().add(OnSpaceExpanded(communityId, child.uuid ?? ''));
},
children: _buildNestedSpaces(context, state, child, communityId),
);
}).toList();
}
}

View File

@ -1,38 +1,74 @@
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_state.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_type_model_event.dart'; import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_type_model_event.dart';
class AddDeviceTypeBloc class AddDeviceTypeBloc extends Bloc<AddDeviceTypeEvent, AddDeviceState> {
extends Bloc<AddDeviceTypeEvent, List<SelectedProduct>> { AddDeviceTypeBloc() : super(AddDeviceInitial()) {
AddDeviceTypeBloc(List<SelectedProduct> initialProducts) on<InitializeDevice>(_onInitializeTagModels);
: super(initialProducts) {
on<UpdateProductCountEvent>(_onUpdateProductCount); on<UpdateProductCountEvent>(_onUpdateProductCount);
} }
void _onUpdateProductCount( void _onInitializeTagModels(
UpdateProductCountEvent event, Emitter<List<SelectedProduct>> emit) { InitializeDevice event, Emitter<AddDeviceState> emit) {
final existingProduct = state.firstWhere( emit(AddDeviceLoaded(
(p) => p.productId == event.productId, selectedProducts: event.addedProducts,
orElse: () => SelectedProduct(productId: event.productId, count: 0,productName: event.productName,product: event.product ), initialTag: event.initialTags,
); ));
}
if (event.count > 0) { void _onUpdateProductCount(
if (!state.contains(existingProduct)) { UpdateProductCountEvent event, Emitter<AddDeviceState> emit) {
emit([ final currentState = state;
...state,
SelectedProduct(productId: event.productId, count: event.count, productName: event.productName, product: event.product) if (currentState is AddDeviceLoaded) {
]); final existingProduct = currentState.selectedProducts.firstWhere(
(p) => p.productId == event.productId,
orElse: () => SelectedProduct(
productId: event.productId,
count: 0,
productName: event.productName,
product: event.product,
),
);
List<SelectedProduct> updatedProducts;
if (event.count > 0) {
if (!currentState.selectedProducts.contains(existingProduct)) {
updatedProducts = [
...currentState.selectedProducts,
SelectedProduct(
productId: event.productId,
count: event.count,
productName: event.productName,
product: event.product,
),
];
} else {
updatedProducts = currentState.selectedProducts.map((p) {
if (p.productId == event.productId) {
return SelectedProduct(
productId: p.productId,
count: event.count,
productName: p.productName,
product: p.product,
);
}
return p;
}).toList();
}
} else { } else {
final updatedList = state.map((p) { // Remove the product if the count is 0
if (p.productId == event.productId) { updatedProducts = currentState.selectedProducts
return SelectedProduct(productId: p.productId, count: event.count, productName: p.productName,product: p.product); .where((p) => p.productId != event.productId)
} .toList();
return p;
}).toList();
emit(updatedList);
} }
} else {
emit(state.where((p) => p.productId != event.productId).toList()); // Emit the updated state
emit(AddDeviceLoaded(
selectedProducts: updatedProducts,
initialTag: currentState.initialTag));
} }
} }
} }

View File

@ -0,0 +1,36 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
abstract class AddDeviceState extends Equatable {
const AddDeviceState();
@override
List<Object> get props => [];
}
class AddDeviceInitial extends AddDeviceState {}
class AddDeviceLoading extends AddDeviceState {}
class AddDeviceLoaded extends AddDeviceState {
final List<SelectedProduct> selectedProducts;
final List<Tag> initialTag;
const AddDeviceLoaded({
required this.selectedProducts,
required this.initialTag,
});
@override
List<Object> get props => [selectedProducts, initialTag];
}
class AddDeviceError extends AddDeviceState {
final String errorMessage;
const AddDeviceError(this.errorMessage);
@override
List<Object> get props => [errorMessage];
}

View File

@ -1,7 +1,11 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
abstract class AddDeviceTypeEvent extends Equatable { abstract class AddDeviceTypeEvent extends Equatable {
const AddDeviceTypeEvent();
@override @override
List<Object> get props => []; List<Object> get props => [];
} }
@ -12,8 +16,26 @@ class UpdateProductCountEvent extends AddDeviceTypeEvent {
final String productName; final String productName;
final ProductModel product; final ProductModel product;
UpdateProductCountEvent({required this.productId, required this.count, required this.productName, required this.product}); UpdateProductCountEvent(
{required this.productId,
required this.count,
required this.productName,
required this.product});
@override @override
List<Object> get props => [productId, count]; List<Object> get props => [productId, count];
} }
class InitializeDevice extends AddDeviceTypeEvent {
final List<Tag> initialTags;
final List<SelectedProduct> addedProducts;
const InitializeDevice({
this.initialTags = const [],
required this.addedProducts,
});
@override
List<Object> get props => [initialTags, addedProducts];
}

View File

@ -1,17 +1,19 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_model_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_model_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_state.dart';
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_type_model_event.dart';
import 'package:syncrow_web/pages/spaces_management/add_device_type/widgets/scrollable_grid_view_widget.dart'; import 'package:syncrow_web/pages/spaces_management/add_device_type/widgets/scrollable_grid_view_widget.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
import 'package:syncrow_web/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart';
import 'package:syncrow_web/pages/spaces_management/tag_model/widgets/action_button_widget.dart'; import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class AddDeviceTypeWidget extends StatelessWidget { class AddDeviceTypeWidget extends StatelessWidget {
final List<ProductModel>? products; final List<ProductModel>? products;
final ValueChanged<List<SelectedProduct>>? onProductsSelected; final ValueChanged<List<SelectedProduct>>? onProductsSelected;
@ -20,11 +22,12 @@ class AddDeviceTypeWidget extends StatelessWidget {
final List<Tag>? spaceTags; final List<Tag>? spaceTags;
final List<String>? allTags; final List<String>? allTags;
final String spaceName; final String spaceName;
final Function(List<Tag>,List<SubspaceModel>?)? onSave; final bool isCreate;
final Function(List<Tag>, List<SubspaceModel>?)? onSave;
const AddDeviceTypeWidget( const AddDeviceTypeWidget(
{super.key, {super.key,
required this.isCreate,
this.products, this.products,
this.initialSelectedProducts, this.initialSelectedProducts,
this.onProductsSelected, this.onProductsSelected,
@ -44,30 +47,47 @@ class AddDeviceTypeWidget extends StatelessWidget {
: 3; : 3;
return BlocProvider( return BlocProvider(
create: (_) => AddDeviceTypeBloc(initialSelectedProducts ?? []), create: (_) => AddDeviceTypeBloc()
..add(InitializeDevice(
initialTags: spaceTags ?? [],
addedProducts: initialSelectedProducts ?? [],
)),
child: Builder( child: Builder(
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
title: const Text('Add Devices'), title: const Text('Add Devices'),
backgroundColor: ColorsManager.whiteColors, backgroundColor: ColorsManager.whiteColors,
content: SingleChildScrollView( content: BlocBuilder<AddDeviceTypeBloc, AddDeviceState>(
child: Container( builder: (context, state) {
width: size.width * 0.9, if (state is AddDeviceLoading) {
height: size.height * 0.65, return const Center(child: CircularProgressIndicator());
color: ColorsManager.textFieldGreyColor, }
child: Column( if (state is AddDeviceLoaded) {
children: [ return SingleChildScrollView(
const SizedBox(height: 16), child: Container(
Expanded( width: size.width * 0.9,
child: Padding( height: size.height * 0.65,
padding: const EdgeInsets.symmetric(horizontal: 20.0), color: ColorsManager.textFieldGreyColor,
child: ScrollableGridViewWidget( child: Column(
products: products, crossAxisCount: crossAxisCount), children: [
), const SizedBox(height: 16),
Expanded(
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 20.0),
child: ScrollableGridViewWidget(
initialProductCounts: state.selectedProducts,
products: products,
isCreate: isCreate,
crossAxisCount: crossAxisCount),
),
),
],
), ),
], ),
), );
), }
), return const SizedBox();
}),
actions: [ actions: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -78,72 +98,54 @@ class AddDeviceTypeWidget extends StatelessWidget {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
), ),
ActionButton( SizedBox(
label: 'Continue', width: 140,
backgroundColor: ColorsManager.secondaryColor, child: BlocBuilder<AddDeviceTypeBloc, AddDeviceState>(
foregroundColor: ColorsManager.whiteColors, builder: (context, state) {
onPressed: () async { final isDisabled = state is AddDeviceLoaded &&
final currentState = state.selectedProducts.isEmpty;
context.read<AddDeviceTypeBloc>().state; return DefaultButton(
Navigator.of(context).pop(); borderRadius: 10,
backgroundColor: ColorsManager.secondaryColor,
foregroundColor: isDisabled
? ColorsManager.whiteColorsWithOpacity
: ColorsManager.whiteColors,
onPressed: () async {
if (state is AddDeviceLoaded &&
state.selectedProducts.isNotEmpty) {
final initialTags =
TagHelper.generateInitialForTags(
spaceTags: spaceTags,
subspaces: subspaces,
);
Navigator.of(context).pop();
if (currentState.isNotEmpty) { final dialogTitle = initialTags.isNotEmpty
final initialTags = generateInitialTags( ? 'Edit Device'
spaceTags: spaceTags, : 'Assign Tags';
subspaces: subspaces, await showDialog<bool>(
); barrierDismissible: false,
context: context,
final dialogTitle = initialTags.isNotEmpty builder: (context) => AssignTagDialog(
? 'Edit Device' products: products,
: 'Assign Tags'; subspaces: subspaces,
await showDialog<bool>( addedProducts: state.selectedProducts,
barrierDismissible: false, allTags: allTags,
context: context, spaceName: spaceName,
builder: (context) => AssignTagDialog( initialTags: initialTags,
products: products, title: dialogTitle,
subspaces: subspaces, onSave: onSave),
addedProducts: currentState, );
allTags: allTags, }
spaceName: spaceName,
initialTags: initialTags,
title: dialogTitle,
onSave: (tags,subspaces){
onSave!(tags,subspaces);
}, },
), child: const Text('Next'),
); );
} },
}, )),
),
], ],
), ),
], ],
), ),
)); ));
} }
List<Tag> generateInitialTags({
List<Tag>? spaceTags,
List<SubspaceModel>? subspaces,
}) {
final List<Tag> initialTags = [];
if (spaceTags != null) {
initialTags.addAll(spaceTags);
}
if (subspaces != null) {
for (var subspace in subspaces) {
if (subspace.tags != null) {
initialTags.addAll(
subspace.tags!.map(
(tag) => tag.copyWith(location: subspace.subspaceName),
),
);
}
}
}
return initialTags;
}
} }

View File

@ -13,12 +13,13 @@ import 'package:syncrow_web/utils/constants/assets.dart';
class DeviceTypeTileWidget extends StatelessWidget { class DeviceTypeTileWidget extends StatelessWidget {
final ProductModel product; final ProductModel product;
final List<SelectedProduct> productCounts; final List<SelectedProduct> productCounts;
final bool isCreate;
const DeviceTypeTileWidget({ const DeviceTypeTileWidget(
super.key, {super.key,
required this.product, required this.product,
required this.productCounts, required this.productCounts,
}); required this.isCreate});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -48,7 +49,7 @@ class DeviceTypeTileWidget extends StatelessWidget {
DeviceNameWidget(name: product.name), DeviceNameWidget(name: product.name),
const SizedBox(height: 4), const SizedBox(height: 4),
CounterWidget( CounterWidget(
isCreate: false, isCreate: isCreate,
initialCount: selectedProduct.count, initialCount: selectedProduct.count,
onCountChanged: (newCount) { onCountChanged: (newCount) {
context.read<AddDeviceTypeBloc>().add( context.read<AddDeviceTypeBloc>().add(

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_model_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_model_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_state.dart';
import 'package:syncrow_web/pages/spaces_management/add_device_type/widgets/device_type_tile_widget.dart'; import 'package:syncrow_web/pages/spaces_management/add_device_type/widgets/device_type_tile_widget.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
@ -9,13 +10,14 @@ class ScrollableGridViewWidget extends StatelessWidget {
final List<ProductModel>? products; final List<ProductModel>? products;
final int crossAxisCount; final int crossAxisCount;
final List<SelectedProduct>? initialProductCounts; final List<SelectedProduct>? initialProductCounts;
final bool isCreate;
const ScrollableGridViewWidget({ const ScrollableGridViewWidget(
super.key, {super.key,
required this.products, required this.products,
required this.crossAxisCount, required this.crossAxisCount,
this.initialProductCounts, this.initialProductCounts,
}); required this.isCreate});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -24,8 +26,11 @@ class ScrollableGridViewWidget extends StatelessWidget {
return Scrollbar( return Scrollbar(
controller: scrollController, controller: scrollController,
thumbVisibility: true, thumbVisibility: true,
child: BlocBuilder<AddDeviceTypeBloc, List<SelectedProduct>>( child: BlocBuilder<AddDeviceTypeBloc, AddDeviceState>(
builder: (context, productCounts) { builder: (context, state) {
final productCounts = state is AddDeviceLoaded
? state.selectedProducts
: <SelectedProduct>[];
return GridView.builder( return GridView.builder(
controller: scrollController, controller: scrollController,
shrinkWrap: true, shrinkWrap: true,
@ -42,6 +47,7 @@ class ScrollableGridViewWidget extends StatelessWidget {
return DeviceTypeTileWidget( return DeviceTypeTileWidget(
product: product, product: product,
isCreate: isCreate,
productCounts: initialProductCount != null productCounts: initialProductCount != null
? [...productCounts, initialProductCount] ? [...productCounts, initialProductCount]
: productCounts, : productCounts,

View File

@ -5,12 +5,15 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_mod
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_state.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_state.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart';
import 'package:syncrow_web/services/product_api.dart'; import 'package:syncrow_web/services/product_api.dart';
import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/services/space_mana_api.dart';
import 'package:syncrow_web/services/space_model_mang_api.dart'; import 'package:syncrow_web/services/space_model_mang_api.dart';
import 'package:syncrow_web/utils/constants/action_enum.dart';
class SpaceManagementBloc class SpaceManagementBloc
extends Bloc<SpaceManagementEvent, SpaceManagementState> { extends Bloc<SpaceManagementEvent, SpaceManagementState> {
@ -341,7 +344,7 @@ class SpaceManagementBloc
products: _cachedProducts ?? [], products: _cachedProducts ?? [],
selectedCommunity: selectedCommunity, selectedCommunity: selectedCommunity,
selectedSpace: selectedSpace, selectedSpace: selectedSpace,
spaceModels: spaceModels ?? [])); spaceModels: spaceModels));
} }
} catch (e) { } catch (e) {
emit(SpaceManagementError('Error updating state: $e')); emit(SpaceManagementError('Error updating state: $e'));
@ -428,6 +431,76 @@ class SpaceManagementBloc
for (var space in orderedSpaces) { for (var space in orderedSpaces) {
try { try {
if (space.uuid != null && space.uuid!.isNotEmpty) { if (space.uuid != null && space.uuid!.isNotEmpty) {
List<TagModelUpdate> tagUpdates = [];
final prevSpace = await _api.getSpace(communityUuid, space.uuid!);
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
final List<SubspaceModel>? prevSubspaces = prevSpace?.subspaces;
final List<SubspaceModel>? newSubspaces = space.subspaces;
tagUpdates = processTagUpdates(prevSpace?.tags, space.tags);
if (prevSubspaces != null || newSubspaces != null) {
if (prevSubspaces != null && newSubspaces != null) {
for (var prevSubspace in prevSubspaces) {
final existsInNew = newSubspaces
.any((subspace) => subspace.uuid == prevSubspace.uuid);
if (!existsInNew) {
subspaceUpdates.add(UpdateSubspaceTemplateModel(
action: Action.delete, uuid: prevSubspace.uuid));
}
}
} else if (prevSubspaces != null && newSubspaces == null) {
for (var prevSubspace in prevSubspaces) {
subspaceUpdates.add(UpdateSubspaceTemplateModel(
action: Action.delete, uuid: prevSubspace.uuid));
}
}
if (newSubspaces != null) {
for (var newSubspace in newSubspaces) {
// Tag without UUID
if ((newSubspace.uuid == null || newSubspace.uuid!.isEmpty)) {
final List<TagModelUpdate> tagUpdates = [];
if (newSubspace.tags != null) {
for (var tag in newSubspace.tags!) {
tagUpdates.add(TagModelUpdate(
action: Action.add,
uuid: tag.uuid == '' ? null : tag.uuid,
tag: tag.tag,
productUuid: tag.product?.uuid));
}
}
subspaceUpdates.add(UpdateSubspaceTemplateModel(
action: Action.add,
subspaceName: newSubspace.subspaceName,
tags: tagUpdates));
}
}
}
if (prevSubspaces != null && newSubspaces != null) {
final newSubspaceMap = {
for (var subspace in newSubspaces) subspace.uuid: subspace
};
for (var prevSubspace in prevSubspaces) {
final newSubspace = newSubspaceMap[prevSubspace.uuid];
if (newSubspace != null) {
final List<TagModelUpdate> tagSubspaceUpdates =
processTagUpdates(prevSubspace.tags, newSubspace.tags);
subspaceUpdates.add(UpdateSubspaceTemplateModel(
action: Action.update,
uuid: newSubspace.uuid,
subspaceName: newSubspace.subspaceName,
tags: tagSubspaceUpdates));
}
}
}
}
final response = await _api.updateSpace( final response = await _api.updateSpace(
communityId: communityUuid, communityId: communityUuid,
spaceId: space.uuid!, spaceId: space.uuid!,
@ -436,6 +509,8 @@ class SpaceManagementBloc
isPrivate: space.isPrivate, isPrivate: space.isPrivate,
position: space.position, position: space.position,
icon: space.icon, icon: space.icon,
subspaces: subspaceUpdates,
tags: tagUpdates,
direction: space.incomingConnection?.direction, direction: space.incomingConnection?.direction,
); );
} else { } else {
@ -535,4 +610,79 @@ class SpaceManagementBloc
emit(SpaceManagementError('Error loading communities and spaces: $e')); emit(SpaceManagementError('Error loading communities and spaces: $e'));
} }
} }
List<TagModelUpdate> processTagUpdates(
List<Tag>? prevTags,
List<Tag>? newTags,
) {
final List<TagModelUpdate> tagUpdates = [];
final processedTags = <String?>{};
if (prevTags == null && newTags != null) {
for (var newTag in newTags) {
tagUpdates.add(TagModelUpdate(
action: Action.add,
tag: newTag.tag,
uuid: newTag.uuid,
productUuid: newTag.product?.uuid,
));
}
return tagUpdates;
}
if (newTags != null || prevTags != null) {
// Case 1: Tags deleted
if (prevTags != null && newTags != null) {
for (var prevTag in prevTags) {
final existsInNew =
newTags.any((newTag) => newTag.uuid == prevTag.uuid);
if (!existsInNew) {
tagUpdates
.add(TagModelUpdate(action: Action.delete, uuid: prevTag.uuid));
}
}
} else if (prevTags != null && newTags == null) {
for (var prevTag in prevTags) {
tagUpdates
.add(TagModelUpdate(action: Action.delete, uuid: prevTag.uuid));
}
}
// Case 2: Tags added
if (newTags != null) {
final prevTagUuids = prevTags?.map((t) => t.uuid).toSet() ?? {};
for (var newTag in newTags) {
// Tag without UUID
if ((newTag.uuid == null || !prevTagUuids.contains(newTag.uuid)) &&
!processedTags.contains(newTag.tag)) {
tagUpdates.add(TagModelUpdate(
action: Action.add,
tag: newTag.tag,
uuid: newTag.uuid == '' ? null : newTag.uuid,
productUuid: newTag.product?.uuid));
processedTags.add(newTag.tag);
}
}
}
// Case 3: Tags updated
if (prevTags != null && newTags != null) {
final newTagMap = {for (var tag in newTags) tag.uuid: tag};
for (var prevTag in prevTags) {
final newTag = newTagMap[prevTag.uuid];
if (newTag != null) {
tagUpdates.add(TagModelUpdate(
action: Action.update,
uuid: newTag.uuid,
tag: newTag.tag,
));
} else {}
}
}
}
return tagUpdates;
}
} }

View File

@ -0,0 +1,26 @@
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:uuid/uuid.dart';
abstract class BaseTag {
String? uuid;
String? tag;
final ProductModel? product;
String internalId;
String? location;
BaseTag({
this.uuid,
required this.tag,
this.product,
String? internalId,
this.location,
}) : internalId = internalId ?? const Uuid().v4();
Map<String, dynamic> toJson();
BaseTag copyWith({
String? tag,
ProductModel? product,
String? location,
String? internalId,
});
}

View File

@ -66,7 +66,6 @@ class SpaceModel {
final instance = SpaceModel( final instance = SpaceModel(
internalId: internalId, internalId: internalId,
uuid: json['uuid'] ?? '', uuid: json['uuid'] ?? '',
spaceTuyaUuid: json['spaceTuyaUuid'],
name: json['spaceName'], name: json['spaceName'],
isPrivate: json['isPrivate'] ?? false, isPrivate: json['isPrivate'] ?? false,
invitationCode: json['invitationCode'], invitationCode: json['invitationCode'],

View File

@ -1,5 +1,6 @@
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/utils/constants/action_enum.dart'; import 'package:syncrow_web/utils/constants/action_enum.dart';
import 'package:uuid/uuid.dart';
import 'tag.dart'; import 'tag.dart';
@ -8,19 +9,24 @@ class SubspaceModel {
String subspaceName; String subspaceName;
final bool disabled; final bool disabled;
List<Tag>? tags; List<Tag>? tags;
String internalId;
SubspaceModel({ SubspaceModel({
this.uuid, this.uuid,
required this.subspaceName, required this.subspaceName,
required this.disabled, required this.disabled,
this.tags, this.tags,
}); String? internalId,
}) : internalId = internalId ?? const Uuid().v4();
factory SubspaceModel.fromJson(Map<String, dynamic> json) { factory SubspaceModel.fromJson(Map<String, dynamic> json) {
final String internalId = json['internalId'] ?? const Uuid().v4();
return SubspaceModel( return SubspaceModel(
uuid: json['uuid'] ?? '', uuid: json['uuid'] ?? '',
subspaceName: json['subspaceName'] ?? '', subspaceName: json['subspaceName'] ?? '',
disabled: json['disabled'] ?? false, disabled: json['disabled'] ?? false,
internalId: internalId,
tags: (json['tags'] as List<dynamic>?) tags: (json['tags'] as List<dynamic>?)
?.map((item) => Tag.fromJson(item)) ?.map((item) => Tag.fromJson(item))
.toList() ?? .toList() ??
@ -43,7 +49,7 @@ class UpdateSubspaceModel {
final Action action; final Action action;
final String? subspaceName; final String? subspaceName;
final List<UpdateTag>? tags; final List<UpdateTag>? tags;
UpdateSubspaceModel({ UpdateSubspaceModel({
required this.action, required this.action,
required this.uuid, required this.uuid,
this.subspaceName, this.subspaceName,
@ -107,4 +113,4 @@ class UpdateTag {
'product': product?.toMap(), 'product': product?.toMap(),
}; };
} }
} }

View File

@ -1,22 +1,23 @@
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/base_tag.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
class Tag { class Tag extends BaseTag {
String? uuid; Tag({
String? tag; String? uuid,
final ProductModel? product; required String? tag,
String internalId; ProductModel? product,
String? location; String? internalId,
String? location,
Tag( }) : super(
{this.uuid, uuid: uuid,
required this.tag, tag: tag,
this.product, product: product,
String? internalId, internalId: internalId,
this.location}) location: location,
: internalId = internalId ?? const Uuid().v4(); );
factory Tag.fromJson(Map<String, dynamic> json) { factory Tag.fromJson(Map<String, dynamic> json) {
final String internalId = json['internalId'] ?? const Uuid().v4(); final String internalId = json['internalId'] ?? const Uuid().v4();
@ -31,15 +32,19 @@ class Tag {
); );
} }
@override
Tag copyWith({ Tag copyWith({
String? tag, String? tag,
ProductModel? product, ProductModel? product,
String? location, String? location,
String? internalId,
}) { }) {
return Tag( return Tag(
uuid: uuid,
tag: tag ?? this.tag, tag: tag ?? this.tag,
product: product ?? this.product, product: product ?? this.product,
location: location ?? this.location, location: location ?? this.location,
internalId: internalId ?? this.internalId,
); );
} }
@ -60,7 +65,7 @@ extension TagModelExtensions on Tag {
..productUuid = product?.uuid; ..productUuid = product?.uuid;
} }
CreateTagBodyModel toCreateTagBodyModel() { CreateTagBodyModel toCreateTagBodyModel() {
return CreateTagBodyModel() return CreateTagBodyModel()
..tag = tag ?? '' ..tag = tag ?? ''
..productUuid = product?.uuid; ..productUuid = product?.uuid;

Some files were not shown because too many files have changed in this diff Show More