From d7ee53639a4e9591dcdcf4faa40e3920803601a7 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 2 Sep 2024 15:45:21 +0400 Subject: [PATCH 001/122] added space mangement icons to assets --- assets/icons/bbq_icon.svg | 3 +++ assets/icons/building_icon.svg | 18 ++++++++++++++++++ assets/icons/desk_icon.png | Bin 0 -> 1268 bytes assets/icons/door_icon.svg | 4 ++++ assets/icons/gym_icon.svg | 3 +++ assets/icons/location_icon.svg | 4 ++++ assets/icons/parking_icon.svg | 6 ++++++ assets/icons/pool_icon.svg | 5 +++++ assets/icons/sauna_icon.svg | 6 ++++++ assets/icons/stair_icon.svg | 4 ++++ assets/icons/steam_room_icon.svg | 7 +++++++ assets/icons/street_icon.svg | 4 ++++ assets/icons/unit_icon.svg | 21 +++++++++++++++++++++ assets/icons/villa_icon.svg | 13 +++++++++++++ 14 files changed, 98 insertions(+) create mode 100644 assets/icons/bbq_icon.svg create mode 100644 assets/icons/building_icon.svg create mode 100644 assets/icons/desk_icon.png create mode 100644 assets/icons/door_icon.svg create mode 100644 assets/icons/gym_icon.svg create mode 100644 assets/icons/location_icon.svg create mode 100644 assets/icons/parking_icon.svg create mode 100644 assets/icons/pool_icon.svg create mode 100644 assets/icons/sauna_icon.svg create mode 100644 assets/icons/stair_icon.svg create mode 100644 assets/icons/steam_room_icon.svg create mode 100644 assets/icons/street_icon.svg create mode 100644 assets/icons/unit_icon.svg create mode 100644 assets/icons/villa_icon.svg diff --git a/assets/icons/bbq_icon.svg b/assets/icons/bbq_icon.svg new file mode 100644 index 00000000..00fb5639 --- /dev/null +++ b/assets/icons/bbq_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/building_icon.svg b/assets/icons/building_icon.svg new file mode 100644 index 00000000..60d32f23 --- /dev/null +++ b/assets/icons/building_icon.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/desk_icon.png b/assets/icons/desk_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..33b6dbb3b70095de07feafda3e944e04bf99c1c1 GIT binary patch literal 1268 zcmVg%B=wR^6M1k!P*2)E&CQ$vd6B%FY~ zJ1^hcwJra`u1{yy$e*7W5+rSG493aiGYf|Cy>&Oo4QZ(ywqQ96hZ0w#dWr-%0 zvl(2D?BhqPl<~sVnJ5yORuA4tZtf2Gz@VU)p5RNv$HaZ3jl@L6$BJE-J=~+z1RY+Y zcKQWu7IhIk2@(@=20N-ybof)AS8;A4Q&ax{egS&bY*I)}#5z=h&!#f)2MM{Ca$qtz z#uFgEv$tTfxIqdLpU46O9wEU4reoQQ@FnUyNyIm;-%Z_YN{vKTd37?Zi)T?#r^kP| zYPV&7l2Tyt8e{s7p&AY~URx@j|?az_WgfrlWi7*YJd6eiI1=?(>$<#dzhQKu! zV$Uw3NT0Ztdzuv+dDH^Ip5c<2HMw+da_Tjbh&q@KFlY^4>kkh4!u^9I{Audg2pzCY z`vuMF!hMa}wO*le?W*bZ*DT)?LsiRLlU;Ra&gdyqrvvxV=zuFcN?e6KT`A9X4bmWw z7g%VMAh(+P8@rxIaudS9il`sZGIoTi2)gzAqp8Tc)H&!~dygXXtX!)qROGCWL!Rci z3McZ(L}|*_!x?1D=U!XJ3ZIIIZ@|EcFnFyRjm*RIsS^^^!m7q09@DumNUTovI_R{u zNj!odUP+!4_MZ0_SPJaLi4_lFU_~S{ncB!ZKAT#nl+|>;zbz)39y0_or`f*KvOp{) z9e6Z?k3!<4F|8QCLW}`FiwpYBu9>F^tx5l8#d1$)Djc(g_dRVU^tn72?orW*w<64= zcq}6>v<%h_2qv10L27=N`X7s3>BiZ{WST7H~x(M~EQ=11sWu+Uad?AmgmQ zr8@mZKR84TWE)WB+sCTB!yo3F_Z?-nWIG6`c6azq(-_@a(%J4JQFrHNY`G4JWH+Ux zJ0zXfdGtu9YtAY*WVrltqqAel+2dgww}?`#_N3hnWLT)2R6V>gVm{F zcDc`)DPfAiaB-PY5)4QuK?5H!g|PGZnbx!&`G`7T)jZjr)VIKF$c=76J)1aU?4}$d z()3$QCl)w + + + diff --git a/assets/icons/gym_icon.svg b/assets/icons/gym_icon.svg new file mode 100644 index 00000000..cd98149f --- /dev/null +++ b/assets/icons/gym_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/location_icon.svg b/assets/icons/location_icon.svg new file mode 100644 index 00000000..f4ffd535 --- /dev/null +++ b/assets/icons/location_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/parking_icon.svg b/assets/icons/parking_icon.svg new file mode 100644 index 00000000..353c3245 --- /dev/null +++ b/assets/icons/parking_icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/pool_icon.svg b/assets/icons/pool_icon.svg new file mode 100644 index 00000000..72097e3a --- /dev/null +++ b/assets/icons/pool_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/sauna_icon.svg b/assets/icons/sauna_icon.svg new file mode 100644 index 00000000..62c77438 --- /dev/null +++ b/assets/icons/sauna_icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/stair_icon.svg b/assets/icons/stair_icon.svg new file mode 100644 index 00000000..a4690bd7 --- /dev/null +++ b/assets/icons/stair_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/steam_room_icon.svg b/assets/icons/steam_room_icon.svg new file mode 100644 index 00000000..a967f997 --- /dev/null +++ b/assets/icons/steam_room_icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/street_icon.svg b/assets/icons/street_icon.svg new file mode 100644 index 00000000..f0b3d969 --- /dev/null +++ b/assets/icons/street_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/unit_icon.svg b/assets/icons/unit_icon.svg new file mode 100644 index 00000000..a62232f0 --- /dev/null +++ b/assets/icons/unit_icon.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/villa_icon.svg b/assets/icons/villa_icon.svg new file mode 100644 index 00000000..edc6b6e1 --- /dev/null +++ b/assets/icons/villa_icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + From 2907aee6c230296d6b7fcc490fadfdb61e1909b8 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 2 Sep 2024 15:45:34 +0400 Subject: [PATCH 002/122] add assets --- lib/utils/constants/assets.dart | 74 ++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 49cefd2d..97ede8eb 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -13,10 +13,12 @@ class Assets { static const String rightLine = "assets/images/right_line.png"; static const String google = "assets/images/google.svg"; static const String facebook = "assets/images/facebook.svg"; - static const String invisiblePassword = "assets/images/Password_invisible.svg"; + static const String invisiblePassword = + "assets/images/Password_invisible.svg"; static const String visiblePassword = "assets/images/Password_visible.svg"; static const String accessIcon = "assets/images/access_icon.svg"; - static const String spaseManagementIcon = "assets/images/spase_management_icon.svg"; + static const String spaseManagementIcon = + "assets/images/spase_management_icon.svg"; static const String devicesIcon = "assets/images/devices_icon.svg"; static const String moveinIcon = "assets/images/movein_icon.svg"; static const String constructionIcon = "assets/images/construction_icon.svg"; @@ -29,13 +31,15 @@ class Assets { static const String emptyTable = "assets/images/empty_table.svg"; // General assets - static const String motionlessDetection = "assets/icons/motionless_detection.svg"; + static const String motionlessDetection = + "assets/icons/motionless_detection.svg"; static const String acHeating = "assets/icons/ac_heating.svg"; static const String acPowerOff = "assets/icons/ac_power_off.svg"; static const String acFanMiddle = "assets/icons/ac_fan_middle.svg"; static const String switchAlarmSound = "assets/icons/switch_alarm_sound.svg"; static const String resetOff = "assets/icons/reset_off.svg"; - static const String sensitivityOperationIcon = "assets/icons/sesitivity_operation_icon.svg"; + static const String sensitivityOperationIcon = + "assets/icons/sesitivity_operation_icon.svg"; static const String motionDetection = "assets/icons/motion_detection.svg"; static const String freezing = "assets/icons/freezing.svg"; static const String indicator = "assets/icons/indicator.svg"; @@ -56,7 +60,8 @@ class Assets { static const String celsiusDegrees = "assets/icons/celsius_degrees.svg"; static const String masterState = "assets/icons/master_state.svg"; static const String acPower = "assets/icons/ac_power.svg"; - static const String farDetectionFunction = "assets/icons/far_detection_function.svg"; + static const String farDetectionFunction = + "assets/icons/far_detection_function.svg"; static const String nobodyTime = "assets/icons/nobody_time.svg"; // Automation functions @@ -64,33 +69,47 @@ class Assets { "assets/icons/automation_functions/temp_password_unlock.svg"; static const String doorlockNormalOpen = "assets/icons/automation_functions/doorlock_normal_open.svg"; - static const String doorbell = "assets/icons/automation_functions/doorbell.svg"; + static const String doorbell = + "assets/icons/automation_functions/doorbell.svg"; static const String remoteUnlockViaApp = "assets/icons/automation_functions/remote_unlock_via_app.svg"; - static const String doubleLock = "assets/icons/automation_functions/double_lock.svg"; - static const String selfTestResult = "assets/icons/automation_functions/self_test_result.svg"; - static const String lockAlarm = "assets/icons/automation_functions/lock_alarm.svg"; - static const String presenceState = "assets/icons/automation_functions/presence_state.svg"; - static const String currentTemp = "assets/icons/automation_functions/current_temp.svg"; - static const String presence = "assets/icons/automation_functions/presence.svg"; + static const String doubleLock = + "assets/icons/automation_functions/double_lock.svg"; + static const String selfTestResult = + "assets/icons/automation_functions/self_test_result.svg"; + static const String lockAlarm = + "assets/icons/automation_functions/lock_alarm.svg"; + static const String presenceState = + "assets/icons/automation_functions/presence_state.svg"; + static const String currentTemp = + "assets/icons/automation_functions/current_temp.svg"; + static const String presence = + "assets/icons/automation_functions/presence.svg"; static const String residualElectricity = "assets/icons/automation_functions/residual_electricity.svg"; - static const String hijackAlarm = "assets/icons/automation_functions/hijack_alarm.svg"; - static const String passwordUnlock = "assets/icons/automation_functions/password_unlock.svg"; + static const String hijackAlarm = + "assets/icons/automation_functions/hijack_alarm.svg"; + static const String passwordUnlock = + "assets/icons/automation_functions/password_unlock.svg"; static const String remoteUnlockRequest = "assets/icons/automation_functions/remote_unlock_req.svg"; - static const String cardUnlock = "assets/icons/automation_functions/card_unlock.svg"; + static const String cardUnlock = + "assets/icons/automation_functions/card_unlock.svg"; static const String motion = "assets/icons/automation_functions/motion.svg"; static const String fingerprintUnlock = "assets/icons/automation_functions/fingerprint_unlock.svg"; // Presence Sensor Assets static const String sensorMotionIcon = "assets/icons/sensor_motion_ic.svg"; - static const String sensorPresenceIcon = "assets/icons/sensor_presence_ic.svg"; + static const String sensorPresenceIcon = + "assets/icons/sensor_presence_ic.svg"; static const String sensorVacantIcon = "assets/icons/sensor_vacant_ic.svg"; - static const String illuminanceRecordIcon = "assets/icons/illuminance_record_ic.svg"; - static const String presenceRecordIcon = "assets/icons/presence_record_ic.svg"; - static const String helpDescriptionIcon = "assets/icons/help_description_ic.svg"; + static const String illuminanceRecordIcon = + "assets/icons/illuminance_record_ic.svg"; + static const String presenceRecordIcon = + "assets/icons/presence_record_ic.svg"; + static const String helpDescriptionIcon = + "assets/icons/help_description_ic.svg"; static const String lightPulp = "assets/icons/light_pulb.svg"; static const String acDevice = "assets/icons/ac_device.svg"; @@ -123,5 +142,20 @@ class Assets { static const String dyi = 'assets/icons/dyi.svg'; static const String office = 'assets/icons/office.svg'; static const String parlour = 'assets/icons/parlour.svg'; - static const String grid = "assets/images/grid.svg"; + static const String grid = 'assets/images/grid.svg'; + + static const String bbq = 'assets/icons/bbq_icon.svg'; + static const String building = 'assets/icons/building_icon.svg'; + static const String desk = 'assets/icons/desk_icon.svg'; + static const String door = 'assets/icons/door_icon.svg'; + static const String gym = 'assets/icons/gym_icon.svg'; + static const String location = 'assets/icons/location_icon.svg'; + static const String parking = 'assets/icons/parking_icon.svg'; + static const String pool = 'assets/icons/pool_icon.svg'; + static const String sauna = 'assets/icons/sauna_icon.svg'; + static const String stair = 'assets/icons/stair_icon.svg'; + static const String steamRoom = 'assets/icons/steam_room_icon.svg'; + static const String street = 'assets/icons/street_icon.svg'; + static const String unit = 'assets/icons/unit_icon.svg'; + static const String villa = 'assets/icons/villa_icon.svg'; } From 1b71cc0985373dbd219de5e1210e91c7a1631d1c Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 2 Sep 2024 16:10:52 +0400 Subject: [PATCH 003/122] added icon for icon edit --- assets/icons/icon_edit_icon.svg | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 assets/icons/icon_edit_icon.svg diff --git a/assets/icons/icon_edit_icon.svg b/assets/icons/icon_edit_icon.svg new file mode 100644 index 00000000..39e1a5c8 --- /dev/null +++ b/assets/icons/icon_edit_icon.svg @@ -0,0 +1,4 @@ + + + + From 46689bde5f9621275506be6ca80d834428a163d9 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 2 Sep 2024 16:13:06 +0400 Subject: [PATCH 004/122] added asset --- lib/utils/constants/assets.dart | 1 + lib/utils/constants/routes_const.dart | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 97ede8eb..34b685d3 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -158,4 +158,5 @@ class Assets { static const String street = 'assets/icons/street_icon.svg'; static const String unit = 'assets/icons/unit_icon.svg'; static const String villa = 'assets/icons/villa_icon.svg'; + static const String iconEdit = 'assets/icons/icon_edit_icon.svg'; } diff --git a/lib/utils/constants/routes_const.dart b/lib/utils/constants/routes_const.dart index 093a61dc..622b090b 100644 --- a/lib/utils/constants/routes_const.dart +++ b/lib/utils/constants/routes_const.dart @@ -4,4 +4,5 @@ class RoutesConst { static const String visitorPassword = '/visitor-password'; static const String accessManagementPage = '/access-management-page'; static const String deviceManagementPage = '/device-management-page'; + static const String spacesManagementPage = '/spaces_management-page'; } From 0c3b820be0dd1f61671408d1f65302eeb6446b11 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 5 Sep 2024 12:11:35 +0400 Subject: [PATCH 005/122] space canvas initial commit --- .vscode/launch.json | 25 +++ assets/icons/desk_icon.svg | 3 + ios/Runner/AppDelegate.swift | 2 +- .../common/buttons/add_space_button.dart | 44 ++++ lib/pages/common/buttons/cancel_button.dart | 38 ++++ lib/pages/common/buttons/default_button.dart | 13 +- .../bloc/space_management_bloc.dart | 60 ++++++ .../spaces_management/model/space_model.dart | 6 + .../view/dialogs/create_space_dialog.dart | 198 +++++++++++++++++ .../spaces_management/view/space_widget.dart | 47 ++++ .../view/spaces_management_page.dart | 203 ++++++++++++++++++ lib/utils/app_routes.dart | 6 +- pubspec.lock | 24 +-- 13 files changed, 649 insertions(+), 20 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 assets/icons/desk_icon.svg create mode 100644 lib/pages/common/buttons/add_space_button.dart create mode 100644 lib/pages/common/buttons/cancel_button.dart create mode 100644 lib/pages/spaces_management/bloc/space_management_bloc.dart create mode 100644 lib/pages/spaces_management/model/space_model.dart create mode 100644 lib/pages/spaces_management/view/dialogs/create_space_dialog.dart create mode 100644 lib/pages/spaces_management/view/space_widget.dart create mode 100644 lib/pages/spaces_management/view/spaces_management_page.dart diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..e4d616c1 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "web", + "request": "launch", + "type": "dart" + }, + { + "name": "web (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "web (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file diff --git a/assets/icons/desk_icon.svg b/assets/icons/desk_icon.svg new file mode 100644 index 00000000..04b35926 --- /dev/null +++ b/assets/icons/desk_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 70693e4a..b6363034 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/lib/pages/common/buttons/add_space_button.dart b/lib/pages/common/buttons/add_space_button.dart new file mode 100644 index 00000000..8348b390 --- /dev/null +++ b/lib/pages/common/buttons/add_space_button.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +class AddSpaceButton extends StatelessWidget { + final VoidCallback onTap; + + const AddSpaceButton({super.key, required this.onTap}); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, // Handle tap event + child: Container( + width: 120, // Width of the button + height: 60, // Height of the button + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), // Rounded corners + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), // Shadow color + spreadRadius: 5, // Spread radius of the shadow + blurRadius: 7, // Blur effect + offset: const Offset(0, 3), // Shadow position + ), + ], + ), + child: Center( + child: Container( + width: 40, // Size of the inner circle + height: 40, + decoration: BoxDecoration( + color: const Color(0xFFF5F6F7), // Light gray background + shape: BoxShape.circle, // Circular shape for the icon container + ), + child: const Icon( + Icons.add, // Add icon + color: Colors.blue, // Icon color + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/common/buttons/cancel_button.dart b/lib/pages/common/buttons/cancel_button.dart new file mode 100644 index 00000000..da6dcdc7 --- /dev/null +++ b/lib/pages/common/buttons/cancel_button.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class CancelButton extends StatelessWidget { + final String label; + final VoidCallback? onPressed; + final double? height; // Optional height parameter for customization + final double? borderRadius; // Optional border radius customization + final double? width; + + const CancelButton({ + super.key, + required this.label, // Button label + required this.onPressed, // Button action + this.height = 40, // Default height + this.width = 140, + this.borderRadius = 10, // Default border radius + }); + + @override + Widget build(BuildContext context) { + return ElevatedButton( + onPressed: onPressed, + style: ButtonStyle( + backgroundColor: WidgetStateProperty.all(ColorsManager.boxColor), // White background + foregroundColor: WidgetStateProperty.all(Colors.black), // Black text color + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius ?? 10), + side: const BorderSide(color: ColorsManager.boxColor), // Black border + ), + ), + fixedSize: WidgetStateProperty.all(Size(width ?? 50, height ?? 40)), // Set button height + ), + child: Text(label), // Dynamic label + ); + } +} \ No newline at end of file diff --git a/lib/pages/common/buttons/default_button.dart b/lib/pages/common/buttons/default_button.dart index 5aa506f8..37320b26 100644 --- a/lib/pages/common/buttons/default_button.dart +++ b/lib/pages/common/buttons/default_button.dart @@ -15,7 +15,8 @@ class DefaultButton extends StatelessWidget { this.backgroundColor, this.foregroundColor, this.borderRadius, - this.height, + this.height = 40, + this.width = 140, this.padding, }); final void Function()? onPressed; @@ -31,6 +32,8 @@ class DefaultButton extends StatelessWidget { final ButtonStyle? customButtonStyle; final Color? backgroundColor; final Color? foregroundColor; + final double? width; + @override Widget build(BuildContext context) { return ElevatedButton( @@ -39,6 +42,7 @@ class DefaultButton extends StatelessWidget { ? null : customButtonStyle ?? ButtonStyle( + fixedSize: WidgetStateProperty.all(Size(width ?? 50, height ?? 40)), // Set button height textStyle: MaterialStateProperty.all( customTextStyle ?? Theme.of(context).textTheme.bodySmall!.copyWith( @@ -59,14 +63,11 @@ class DefaultButton extends StatelessWidget { ? backgroundColor ?? ColorsManager.primaryColor : Colors.black.withOpacity(0.2); }), - shape: MaterialStateProperty.all( + shape: WidgetStateProperty.all( RoundedRectangleBorder( - borderRadius: BorderRadius.circular(borderRadius ?? 20), + borderRadius: BorderRadius.circular(borderRadius ?? 10), ), ), - fixedSize: MaterialStateProperty.all( - const Size.fromHeight(50), - ), padding: MaterialStateProperty.all( EdgeInsets.all(padding ?? 10), ), diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart new file mode 100644 index 00000000..bb4afe19 --- /dev/null +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -0,0 +1,60 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; + +// Events +abstract class SpaceEvent extends Equatable { + @override + List get props => []; +} + +class LoadSpaces extends SpaceEvent {} + +class SelectSpace extends SpaceEvent { + final String selectedSpace; + + SelectSpace(this.selectedSpace); + + @override + List get props => [selectedSpace]; +} + +// States +abstract class SpaceState extends Equatable { + @override + List get props => []; +} + +class SpaceInitial extends SpaceState {} + +class SpaceLoaded extends SpaceState { + final List spaces; + final String selectedSpace; + + SpaceLoaded({required this.spaces, required this.selectedSpace}); + + @override + List get props => [spaces, selectedSpace]; +} + +// Bloc +class SpacesManagementBloc extends Bloc { + SpacesManagementBloc() : super(SpaceInitial()) { + on((event, emit) { + final List spaces = [ + SpaceModel(communityName: 'Downtown Dubai', subSpaces: ['Sub Space 1', 'Sub Space 2']), + SpaceModel(communityName: 'Dubai Creek Harbour', subSpaces: ['Sub Space 1', 'Sub Space 2']), + SpaceModel(communityName: 'Dubai Hills Estate', subSpaces: ['Sub Space 1', 'Sub Space 2']), + ]; + emit(SpaceLoaded(spaces: spaces, selectedSpace: spaces[0].communityName)); + }); + + on((event, emit) { + if (state is SpaceLoaded) { + final loadedState = state as SpaceLoaded; + emit(SpaceLoaded(spaces: loadedState.spaces, selectedSpace: event.selectedSpace)); + } + }); + } +} diff --git a/lib/pages/spaces_management/model/space_model.dart b/lib/pages/spaces_management/model/space_model.dart new file mode 100644 index 00000000..ee488441 --- /dev/null +++ b/lib/pages/spaces_management/model/space_model.dart @@ -0,0 +1,6 @@ +class SpaceModel { + final String communityName; + final List subSpaces; + + SpaceModel({required this.communityName, required this.subSpaces}); +} diff --git a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart new file mode 100644 index 00000000..d80de241 --- /dev/null +++ b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart @@ -0,0 +1,198 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.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/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class CreateSpaceDialog extends StatefulWidget { + // Add the onCreateSpace parameter as a required field + final Function(String, String) onCreateSpace; + + const CreateSpaceDialog({super.key, required this.onCreateSpace}); + + @override + CreateSpaceDialogState createState() => CreateSpaceDialogState(); +} + +class CreateSpaceDialogState extends State { + String selectedIcon = Assets.location; // Initially selected icon + String enteredName = ''; // Store entered space name + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Create New Space'), + backgroundColor: ColorsManager.whiteColors, + content: SizedBox( + width: 600, // Set width for the dialog + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Stack( + alignment: Alignment.center, + children: [ + Container( + width: 100, + height: 100, + decoration: const BoxDecoration( + color: Color(0xFFF5F6F7), + shape: BoxShape.circle, + ), + ), + SvgPicture.asset( + selectedIcon, // Display the selected icon here + width: 60, + height: 60, + ), + Positioned( + top: 2, + left: 2, + child: InkWell( + onTap: () => + _showIconSelectionDialog(), // Open the icon selection dialog + child: Container( + width: 20, + height: 20, + decoration: const BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + child: SvgPicture.asset(Assets.iconEdit, + width: 10, height: 10), + ), + ), + ), + ], + ), + const SizedBox(width: 16), + Expanded( + child: TextField( + onChanged: (value) { + enteredName = value; // Capture entered name + }, + decoration: InputDecoration( + hintText: 'Please enter the name', + filled: true, + fillColor: const Color(0xFFF5F6F7), + border: OutlineInputBorder( + borderSide: const BorderSide(color: Color(0xFFF5F6F7)), + borderRadius: BorderRadius.circular(10), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0xFFF5F6F7), + width: 1), // Default border + borderRadius: BorderRadius.circular(10), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: + BorderSide(color: Color(0xFFF5F6F7), width: 1), + ), + ), + ), + ), + ], + ), + ], + ), + ), + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: CancelButton( + label: 'Cancel', + onPressed: () => Navigator.of(context).pop(), + ), + ), + const SizedBox(width: 10), + Expanded( + child: DefaultButton( + onPressed: () { + if (enteredName.isNotEmpty) { + widget.onCreateSpace(enteredName, selectedIcon); // Pass name and icon back + Navigator.of(context).pop(); // Close dialog + } + }, + child: const Text('OK'), + backgroundColor: const Color(0xFF023DFE), + foregroundColor: Colors.white, + ), + ), + ], + ), + ], + ); + } + + // Icon selection dialog + void _showIconSelectionDialog() { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Select Icon'), + backgroundColor: Colors.white, + content: Container( + width: 500, // Width of the icon selection dialog + height: 200, // Height of the dialog + padding: const EdgeInsets.all(18), + decoration: BoxDecoration( + color: const Color(0xFFF5F6F7), + borderRadius: BorderRadius.circular(12), + ), + child: GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 7, // Number of icons per row + crossAxisSpacing: 10, // Space between icons horizontally + mainAxisSpacing: 22, // Space between icons vertically + ), + itemCount: _iconList.length, + itemBuilder: (BuildContext context, int index) { + return GestureDetector( + onTap: () { + setState(() { + selectedIcon = + _iconList[index]; // Update the selected icon + }); + Navigator.of(context) + .pop(); // Close the icon selection dialog + }, + child: SvgPicture.asset( + _iconList[index], + width: 50, // Adjust size as needed + height: 50, + ), + ); + }, + ), + ), + ); + }, + ); + } + + // Icon list containing SVG asset paths + final List _iconList = [ + Assets.location, + Assets.villa, + Assets.gym, + Assets.sauna, + Assets.bbq, + Assets.building, + Assets.desk, + Assets.door, + Assets.parking, + Assets.pool, + Assets.stair, + Assets.steamRoom, + Assets.street, + Assets.unit, + ]; +} diff --git a/lib/pages/spaces_management/view/space_widget.dart b/lib/pages/spaces_management/view/space_widget.dart new file mode 100644 index 00000000..0d661292 --- /dev/null +++ b/lib/pages/spaces_management/view/space_widget.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; + +class SpaceWidget extends StatelessWidget { + final String name; + final Offset position; + final VoidCallback onTap; + + const SpaceWidget({ + super.key, + required this.name, + required this.position, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Positioned( + left: position.dx, + top: position.dy, + child: GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 5, + blurRadius: 7, + offset: const Offset(0, 3), + ), + ], + ), + child: Row( + children: [ + const Icon(Icons.location_on, color: Colors.blue), + const SizedBox(width: 8), + Text(name, style: const TextStyle(fontSize: 16)), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart new file mode 100644 index 00000000..e185ee7d --- /dev/null +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -0,0 +1,203 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; +import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class SpaceManagementPage extends StatefulWidget { + @override + SpaceManagementPageState createState() => SpaceManagementPageState(); +} + +class SpaceManagementPageState extends State { + // Store created spaces + List spaces = []; + + @override + Widget build(BuildContext context) { + Size screenSize = MediaQuery.of(context).size; + + return Scaffold( + backgroundColor: ColorsManager.whiteColors, + appBar: AppBar( + title: const Text('Space Management'), + ), + body: Stack( + children: [ + Center( + child: spaces.isEmpty + ? AddSpaceButton( + onTap: () { + _showCreateSpaceDialog(screenSize); + }, + ) + : Stack( + children: spaces + .asMap() + .entries + .map((entry) => _buildSpaceCard(entry.key, screenSize)) + .toList(), + ), + ), + ], + ), + ); + } + + // Function to open the Create Space dialog + void _showCreateSpaceDialog(Size screenSize, {Offset? position}) { + showDialog( + context: context, + builder: (BuildContext context) { + return CreateSpaceDialog( + onCreateSpace: (String name, String icon) { + setState(() { + // Set the first space in the center or use passed position + Offset centerPosition = position ?? + Offset( + screenSize.width / 2 - 75, // Center horizontally + screenSize.height / 2 - 100, // Slightly above the center vertically + ); + + spaces.add(SpaceData(name: name, icon: icon, position: centerPosition)); + }); + }, + ); + }, + ); + } + + // Function to build a draggable space card + Widget _buildSpaceCard(int index, Size screenSize) { + return Positioned( + left: spaces[index].position.dx, + top: spaces[index].position.dy, + child: GestureDetector( + onPanUpdate: (details) { + // Update the position of the space card while dragging + setState(() { + spaces[index].position += details.delta; + }); + }, + child: MouseRegion( + onEnter: (_) { + // Show plus buttons on hover + setState(() { + spaces[index].isHovered = true; + }); + }, + onExit: (_) { + // Hide plus buttons when not hovered + setState(() { + spaces[index].isHovered = false; + }); + }, + child: Stack( + clipBehavior: Clip.none, + children: [ + _buildSpaceContainer(index), + if (spaces[index].isHovered) ...[ + _buildPlusButton(index, 'left', const Offset(-21, 20), screenSize), + _buildPlusButton(index, 'right', const Offset(140, 20), screenSize), + _buildPlusButton(index, 'down', const Offset(63, 55), screenSize), + ], + ], + ), + ), + ), + ); + } + + // Function to build the space container with the styled format + Widget _buildSpaceContainer(int index) { + return Container( + width: 150, + height: 60, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: const Offset(0, 3), + ), + ], + ), + child: Row( + children: [ + Container( + width: 40, + height: 60, + decoration: const BoxDecoration( + color: Color(0xFF023DFE), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(15), + bottomLeft: Radius.circular(15), + ), + ), + child: Center( + child: SvgPicture.asset(spaces[index].icon, width: 24, height: 24, color: Colors.white), + ), + ), + const SizedBox(width: 10), + Text( + spaces[index].name, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ], + ), + ); + } + + // Function to build plus buttons for new space creation + Widget _buildPlusButton(int index, String direction, Offset offset, Size screenSize) { + return Positioned( + left: offset.dx, + top: offset.dy, + child: GestureDetector( + onTap: () { + Offset newPosition; + switch (direction) { + case 'left': + newPosition = spaces[index].position + const Offset(-200, 0); + break; + case 'right': + newPosition = spaces[index].position + const Offset(200, 0); + break; + case 'down': + newPosition = spaces[index].position + const Offset(0, 150); + break; + default: + newPosition = spaces[index].position; + } + + _showCreateSpaceDialog(screenSize, position: newPosition); + }, + child: Container( + width: 30, + height: 30, + decoration: const BoxDecoration( + color: Color(0xFF023DFE), + shape: BoxShape.circle, + ), + child: const Icon(Icons.add, color: Colors.white, size: 20), + ), + ), + ); + } +} + +// Model for storing space information +class SpaceData { + final String name; + final String icon; + Offset position; + bool isHovered; + + SpaceData({required this.name, required this.icon, required this.position, this.isHovered = false}); +} diff --git a/lib/utils/app_routes.dart b/lib/utils/app_routes.dart index a730341b..3714aa69 100644 --- a/lib/utils/app_routes.dart +++ b/lib/utils/app_routes.dart @@ -3,6 +3,7 @@ import 'package:syncrow_web/pages/access_management/view/access_management.dart' import 'package:syncrow_web/pages/auth/view/login_page.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/view/device_managment_page.dart'; import 'package:syncrow_web/pages/home/view/home_page.dart'; +import 'package:syncrow_web/pages/spaces_management/view/spaces_management_page.dart'; import 'package:syncrow_web/pages/visitor_password/view/visitor_password_dialog.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart'; @@ -11,7 +12,7 @@ class AppRoutes { return [ GoRoute( path: RoutesConst.auth, - builder: (context, state) => const LoginPage(), + builder: (context, state) => SpaceManagementPage(), ), GoRoute( path: RoutesConst.home, @@ -29,6 +30,9 @@ class AppRoutes { path: RoutesConst.deviceManagementPage, builder: (context, state) => const DeviceManagementPage(), ), + GoRoute( + path: RoutesConst.spacesManagementPage, + builder: (context, state) => SpaceManagementPage()), ]; } } diff --git a/pubspec.lock b/pubspec.lock index 9a9cd6a8..7973e388 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -268,18 +268,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -316,18 +316,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" nested: dependency: transitive description: @@ -537,10 +537,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" typed_data: dependency: transitive description: @@ -585,10 +585,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.4" web: dependency: transitive description: From 5a3520b0895f85e917f9a8b6cf8bebc61487042b Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 5 Sep 2024 12:21:43 +0400 Subject: [PATCH 006/122] changed text field text color to black --- .../spaces_management/view/dialogs/create_space_dialog.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart index d80de241..4ad0b129 100644 --- a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart @@ -74,6 +74,9 @@ class CreateSpaceDialogState extends State { onChanged: (value) { enteredName = value; // Capture entered name }, + style: TextStyle( + color: ColorsManager.blackColor, + ), decoration: InputDecoration( hintText: 'Please enter the name', filled: true, From f32076eba17869fda2333e4e324fb2695ed37599 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 5 Sep 2024 12:43:38 +0400 Subject: [PATCH 007/122] lines for connecting spaces --- .../view/spaces_management_page.dart | 82 +++++++++++++++++-- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index e185ee7d..cea30ccf 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -12,6 +12,7 @@ class SpaceManagementPage extends StatefulWidget { class SpaceManagementPageState extends State { // Store created spaces List spaces = []; + List connections = []; @override Widget build(BuildContext context) { @@ -24,6 +25,11 @@ class SpaceManagementPageState extends State { ), body: Stack( children: [ + // Draw lines using a CustomPaint widget + CustomPaint( + size: Size.infinite, + painter: LinePainter(connections), + ), Center( child: spaces.isEmpty ? AddSpaceButton( @@ -45,7 +51,7 @@ class SpaceManagementPageState extends State { } // Function to open the Create Space dialog - void _showCreateSpaceDialog(Size screenSize, {Offset? position}) { + void _showCreateSpaceDialog(Size screenSize, {Offset? position, int? parentIndex, bool addConnection = false}) { showDialog( context: context, builder: (BuildContext context) { @@ -59,7 +65,16 @@ class SpaceManagementPageState extends State { screenSize.height / 2 - 100, // Slightly above the center vertically ); - spaces.add(SpaceData(name: name, icon: icon, position: centerPosition)); + SpaceData newSpace = SpaceData(name: name, icon: icon, position: centerPosition); + spaces.add(newSpace); + + // Add connection for down-button + if (addConnection && parentIndex != null) { + connections.add(Connection( + startSpace: spaces[parentIndex], + endSpace: newSpace, + )); + } }); }, ); @@ -137,10 +152,16 @@ class SpaceManagementPageState extends State { bottomLeft: Radius.circular(15), ), ), - child: Center( - child: SvgPicture.asset(spaces[index].icon, width: 24, height: 24, color: Colors.white), + child: Center( + child: SvgPicture.asset( + spaces[index].icon, + width: 24, + height: 24, + color: Colors.white, + ), + ), ), - ), + const SizedBox(width: 10), Text( spaces[index].name, @@ -162,6 +183,7 @@ class SpaceManagementPageState extends State { child: GestureDetector( onTap: () { Offset newPosition; + bool addConnection = false; switch (direction) { case 'left': newPosition = spaces[index].position + const Offset(-200, 0); @@ -171,12 +193,13 @@ class SpaceManagementPageState extends State { break; case 'down': newPosition = spaces[index].position + const Offset(0, 150); + addConnection = true; break; default: newPosition = spaces[index].position; } - - _showCreateSpaceDialog(screenSize, position: newPosition); + // Open the dialog to create a new space and pass down the new position + _showCreateSpaceDialog(screenSize, position: newPosition, parentIndex: index, addConnection: addConnection); }, child: Container( width: 30, @@ -199,5 +222,48 @@ class SpaceData { Offset position; bool isHovered; - SpaceData({required this.name, required this.icon, required this.position, this.isHovered = false}); + SpaceData({ + required this.name, + required this.icon, + required this.position, + this.isHovered = false, + }); } + +// Class for connection lines between spaces +class Connection { + final SpaceData startSpace; + final SpaceData endSpace; + + Connection({required this.startSpace, required this.endSpace}); +} + +// Custom painter to draw lines between connected spaces +class LinePainter extends CustomPainter { + final List connections; + + LinePainter(this.connections); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = Colors.black + ..strokeWidth = 2 + ..style = PaintingStyle.stroke; + + for (var connection in connections) { + final start = connection.startSpace.position + Offset(75, 60); // Bottom center of the starting space + final end = connection.endSpace.position + Offset(75, 0); // Top center of the ending space + + // Draw a straight line connecting the two spaces + canvas.drawLine(start, end, paint); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return true; + } +} + + From 17510c48f26e5540b154498ea47ac6a7e3cb6a49 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 5 Sep 2024 14:29:26 +0400 Subject: [PATCH 008/122] changed to curve line --- .../view/spaces_management_page.dart | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index cea30ccf..929da5b1 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -28,7 +28,7 @@ class SpaceManagementPageState extends State { // Draw lines using a CustomPaint widget CustomPaint( size: Size.infinite, - painter: LinePainter(connections), + painter: CurvedLinePainter(connections), ), Center( child: spaces.isEmpty @@ -114,7 +114,7 @@ class SpaceManagementPageState extends State { if (spaces[index].isHovered) ...[ _buildPlusButton(index, 'left', const Offset(-21, 20), screenSize), _buildPlusButton(index, 'right', const Offset(140, 20), screenSize), - _buildPlusButton(index, 'down', const Offset(63, 55), screenSize), + _buildPlusButton(index, 'down', const Offset(63, 50), screenSize), ], ], ), @@ -239,10 +239,11 @@ class Connection { } // Custom painter to draw lines between connected spaces -class LinePainter extends CustomPainter { + +class CurvedLinePainter extends CustomPainter { final List connections; - LinePainter(this.connections); + CurvedLinePainter(this.connections); @override void paint(Canvas canvas, Size size) { @@ -255,8 +256,20 @@ class LinePainter extends CustomPainter { final start = connection.startSpace.position + Offset(75, 60); // Bottom center of the starting space final end = connection.endSpace.position + Offset(75, 0); // Top center of the ending space - // Draw a straight line connecting the two spaces - canvas.drawLine(start, end, paint); + // Calculate control point for Bézier curve (to create a curve) + final controlPoint = Offset((start.dx + end.dx) / 2, start.dy + 50); + + // Draw the Bézier curve + final path = Path() + ..moveTo(start.dx, start.dy) + ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy); + + canvas.drawPath(path, paint); + + // Draw small connection dots at the start and end points + final dotPaint = Paint()..color = ColorsManager.blackColor; + canvas.drawCircle(start, 5, dotPaint); // Start dot + canvas.drawCircle(end, 5, dotPaint); // End dot } } @@ -264,6 +277,4 @@ class LinePainter extends CustomPainter { bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; } -} - - +} \ No newline at end of file From 55d4349f2efa9221f13a00bbb1106bee3ca83295 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 5 Sep 2024 16:18:58 +0400 Subject: [PATCH 009/122] changed the view --- .../view/spaces_management_page.dart | 166 +++++++++++------- 1 file changed, 105 insertions(+), 61 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 929da5b1..a1a211b9 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -19,39 +19,50 @@ class SpaceManagementPageState extends State { Size screenSize = MediaQuery.of(context).size; return Scaffold( - backgroundColor: ColorsManager.whiteColors, - appBar: AppBar( - title: const Text('Space Management'), - ), - body: Stack( - children: [ - // Draw lines using a CustomPaint widget - CustomPaint( - size: Size.infinite, - painter: CurvedLinePainter(connections), - ), - Center( - child: spaces.isEmpty - ? AddSpaceButton( - onTap: () { - _showCreateSpaceDialog(screenSize); - }, - ) - : Stack( - children: spaces - .asMap() - .entries - .map((entry) => _buildSpaceCard(entry.key, screenSize)) - .toList(), + backgroundColor: ColorsManager.whiteColors, + appBar: AppBar( + title: const Text('Space Management'), + ), + body: InteractiveViewer( + boundaryMargin: const EdgeInsets.all( + double.infinity), // Allow panning to any side + minScale: 0.5, // Minimum zoom scale + maxScale: 2.5, // Maximum zoom scale + panEnabled: true, // Enable panning (move left/right) + scaleEnabled: true, // Enable zooming + child: MouseRegion( + cursor: SystemMouseCursors.grab, + child: Stack( + children: [ + // Draw lines using a CustomPaint widget + CustomPaint( + size: Size.infinite, + painter: CurvedLinePainter(connections), ), - ), - ], - ), - ); + Center( + child: spaces.isEmpty + ? AddSpaceButton( + onTap: () { + _showCreateSpaceDialog(screenSize); + }, + ) + : Stack( + children: spaces + .asMap() + .entries + .map((entry) => + _buildSpaceCard(entry.key, screenSize)) + .toList(), + ), + ), + ], + ), + ))); } // Function to open the Create Space dialog - void _showCreateSpaceDialog(Size screenSize, {Offset? position, int? parentIndex, bool addConnection = false}) { + void _showCreateSpaceDialog(Size screenSize, + {Offset? position, int? parentIndex, String? direction}) { showDialog( context: context, builder: (BuildContext context) { @@ -62,17 +73,20 @@ class SpaceManagementPageState extends State { Offset centerPosition = position ?? Offset( screenSize.width / 2 - 75, // Center horizontally - screenSize.height / 2 - 100, // Slightly above the center vertically + screenSize.height / 2 - + 100, // Slightly above the center vertically ); - SpaceData newSpace = SpaceData(name: name, icon: icon, position: centerPosition); + SpaceData newSpace = + SpaceData(name: name, icon: icon, position: centerPosition); spaces.add(newSpace); // Add connection for down-button - if (addConnection && parentIndex != null) { + if (parentIndex != null && direction != null) { connections.add(Connection( startSpace: spaces[parentIndex], endSpace: newSpace, + direction: direction, )); } }); @@ -112,9 +126,12 @@ class SpaceManagementPageState extends State { children: [ _buildSpaceContainer(index), if (spaces[index].isHovered) ...[ - _buildPlusButton(index, 'left', const Offset(-21, 20), screenSize), - _buildPlusButton(index, 'right', const Offset(140, 20), screenSize), - _buildPlusButton(index, 'down', const Offset(63, 50), screenSize), + _buildPlusButton( + index, 'left', const Offset(-21, 20), screenSize), + _buildPlusButton( + index, 'right', const Offset(140, 20), screenSize), + _buildPlusButton( + index, 'down', const Offset(63, 50), screenSize), ], ], ), @@ -152,16 +169,15 @@ class SpaceManagementPageState extends State { bottomLeft: Radius.circular(15), ), ), - child: Center( - child: SvgPicture.asset( - spaces[index].icon, - width: 24, - height: 24, - color: Colors.white, - ), + child: Center( + child: SvgPicture.asset( + spaces[index].icon, + width: 24, + height: 24, + color: Colors.white, ), ), - + ), const SizedBox(width: 10), Text( spaces[index].name, @@ -176,14 +192,14 @@ class SpaceManagementPageState extends State { } // Function to build plus buttons for new space creation - Widget _buildPlusButton(int index, String direction, Offset offset, Size screenSize) { + Widget _buildPlusButton( + int index, String direction, Offset offset, Size screenSize) { return Positioned( left: offset.dx, top: offset.dy, child: GestureDetector( onTap: () { Offset newPosition; - bool addConnection = false; switch (direction) { case 'left': newPosition = spaces[index].position + const Offset(-200, 0); @@ -193,13 +209,13 @@ class SpaceManagementPageState extends State { break; case 'down': newPosition = spaces[index].position + const Offset(0, 150); - addConnection = true; break; default: newPosition = spaces[index].position; } // Open the dialog to create a new space and pass down the new position - _showCreateSpaceDialog(screenSize, position: newPosition, parentIndex: index, addConnection: addConnection); + _showCreateSpaceDialog(screenSize, + position: newPosition, parentIndex: index, direction: direction); }, child: Container( width: 30, @@ -234,8 +250,12 @@ class SpaceData { class Connection { final SpaceData startSpace; final SpaceData endSpace; + final String direction; - Connection({required this.startSpace, required this.endSpace}); + Connection( + {required this.startSpace, + required this.endSpace, + required this.direction}); } // Custom painter to draw lines between connected spaces @@ -253,23 +273,47 @@ class CurvedLinePainter extends CustomPainter { ..style = PaintingStyle.stroke; for (var connection in connections) { - final start = connection.startSpace.position + Offset(75, 60); // Bottom center of the starting space - final end = connection.endSpace.position + Offset(75, 0); // Top center of the ending space + Offset start = connection.startSpace.position + + Offset(75, 60); // Center bottom of start space + Offset end = connection.endSpace.position + + Offset(75, 0); // Center top of end space - // Calculate control point for Bézier curve (to create a curve) - final controlPoint = Offset((start.dx + end.dx) / 2, start.dy + 50); - - // Draw the Bézier curve - final path = Path() - ..moveTo(start.dx, start.dy) - ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy); - - canvas.drawPath(path, paint); + // Draw straight line for left/right connections and curved line for down connections + if (connection.direction == 'down') { + // Curved line for down connections + final controlPoint = Offset((start.dx + end.dx) / 2, start.dy + 50); + final path = Path() + ..moveTo(start.dx, start.dy) + ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy); + canvas.drawPath(path, paint); + } + if (connection.direction == 'right') { + start = Offset( + connection.startSpace.position.dx + 150, + connection.startSpace.position.dy + + 30); // Right center of the first space + end = Offset( + connection.endSpace.position.dx, + connection.endSpace.position.dy + + 30); // Left center of the new space + canvas.drawLine(start, end, paint); + } else if (connection.direction == 'left') { + // Straight line for left/right connections + start = Offset( + connection.startSpace.position.dx, + connection.startSpace.position.dy + + 30); // Left center of the first space + end = Offset( + connection.endSpace.position.dx + 150, + connection.endSpace.position.dy + + 30); // Right center of the new space + canvas.drawLine(start, end, paint); + } // Draw small connection dots at the start and end points final dotPaint = Paint()..color = ColorsManager.blackColor; canvas.drawCircle(start, 5, dotPaint); // Start dot - canvas.drawCircle(end, 5, dotPaint); // End dot + canvas.drawCircle(end, 5, dotPaint); // End dot } } @@ -277,4 +321,4 @@ class CurvedLinePainter extends CustomPainter { bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; } -} \ No newline at end of file +} From 7e6b0f33c889da26778f2e811eff1832bd172336 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 10 Sep 2024 11:13:00 +0400 Subject: [PATCH 010/122] fixed border of the sidepanel --- .../view/spaces_management_page.dart | 343 +++++++++++++++--- 1 file changed, 285 insertions(+), 58 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index a1a211b9..8b992a0c 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -19,45 +19,278 @@ class SpaceManagementPageState extends State { Size screenSize = MediaQuery.of(context).size; return Scaffold( - backgroundColor: ColorsManager.whiteColors, - appBar: AppBar( - title: const Text('Space Management'), - ), - body: InteractiveViewer( - boundaryMargin: const EdgeInsets.all( - double.infinity), // Allow panning to any side - minScale: 0.5, // Minimum zoom scale - maxScale: 2.5, // Maximum zoom scale - panEnabled: true, // Enable panning (move left/right) - scaleEnabled: true, // Enable zooming - child: MouseRegion( - cursor: SystemMouseCursors.grab, - child: Stack( - children: [ - // Draw lines using a CustomPaint widget - CustomPaint( - size: Size.infinite, - painter: CurvedLinePainter(connections), - ), - Center( - child: spaces.isEmpty - ? AddSpaceButton( - onTap: () { - _showCreateSpaceDialog(screenSize); - }, - ) - : Stack( - children: spaces - .asMap() - .entries - .map((entry) => - _buildSpaceCard(entry.key, screenSize)) - .toList(), + backgroundColor: ColorsManager.whiteColors, + appBar: AppBar( + title: const Text('Space Management'), + ), + body: Stack( + clipBehavior: Clip.none, + + children: [ + Row( + children: [ + // Sidebar for navigation and community list + Container( + width: 250, // Fixed width for sidebar + decoration: BoxDecoration( + color: Colors.white, // No gradient inside + ), + + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // "Communities" title with the add button + Container( + decoration: BoxDecoration( + color: Colors.white, // Background color + + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 10, + spreadRadius: 1, + offset: Offset(0, 2), // Adjust the shadow position ), - ), - ], + ], + ), + padding: const EdgeInsets.all( + 16.0), // Apply padding inside the container + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Communities', + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + ), + ), + // Plus Button (+) + GestureDetector( + onTap: () { + // Handle the button tap action + }, + child: Container( + width: 30, + height: 30, + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 10, + spreadRadius: 1, + ), + ], + ), + child: Center( + child: Icon(Icons.add, + color: Colors.blue, size: 18), + ), + ), + ), + ], + ), + ), + // Search bar + SizedBox(height: 16), + Container( + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 10, + spreadRadius: 1, + offset: Offset(0, 2), // Slight shadow offset + ), + ], + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0), // Padding around the search bar + child: Container( + decoration: BoxDecoration( + color: Colors.grey[ + 200], // Light background for the search bar + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: + 8, // Softer shadow for the search box + ), + ], + ), + child: TextField( + decoration: InputDecoration( + hintText: 'Search', + prefixIcon: + Icon(Icons.search, color: Colors.grey), + border: InputBorder.none, // No visible border + ), + ), + ), + ), + ), + SizedBox(height: 16), + // Example of community list + Expanded( + child: ListView( + children: [ + ListTile( + title: Text( + 'Downtown Dubai', + style: TextStyle( + color: Colors.black87, + fontWeight: FontWeight.w500, + ), + ), + ), + ListTile( + title: Text( + 'Dubai Creek Harbour', + style: TextStyle( + color: Colors.black87, + fontWeight: FontWeight.w500, + ), + ), + ), + ExpansionTile( + title: Text( + 'Dubai Hills Estate', + style: TextStyle( + color: Colors.black87, + fontWeight: FontWeight.w500, + ), + ), + children: [ + ListTile( + title: Text( + 'South Side', + style: TextStyle( + color: Colors + .black54, // Lighter text for nested + ), + ), + ), + ], + ), + ], + ), + ), + ], + ), ), - ))); + + // Right Side: Community Structure Area + Expanded( + child: Container( + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + border: Border( + left: BorderSide( + color: Colors.white!, + width: 1.0), // Light left border to match + ), + ), + // Background color for canvas + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.all(16.0), + width: double.infinity, + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + boxShadow: [ + BoxShadow( + color: Colors.black + .withOpacity(0.2), // Subtle shadow + spreadRadius: 0, // No spread + blurRadius: 8, // Softer shadow edges + offset: Offset(0, 4), // Shadow only on the bottom + ), + ], + ), + child: Text( + 'Community Structure', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + ), + // Use Expanded to ensure InteractiveViewer takes the available space + Expanded( + child: InteractiveViewer( + boundaryMargin: + const EdgeInsets.all(500), // Adjusted to 500 + minScale: 0.5, // Minimum zoom scale + maxScale: 2.5, // Maximum zoom scale + panEnabled: true, // Enable panning + scaleEnabled: true, // Enable zooming + child: Container( + width: 2000, // Large canvas + height: 2000, // Large canvas + color: Colors.transparent, // Transparent background + child: Stack( + clipBehavior: Clip.none, + children: [ + // Draw lines using a CustomPaint widget + CustomPaint( + size: + Size(2000, 2000), // Explicit canvas size + painter: CurvedLinePainter(connections), + ), + Center( + child: spaces.isEmpty + ? AddSpaceButton( + onTap: () { + _showCreateSpaceDialog(screenSize); + }, + ) + : Stack( + children: spaces + .asMap() + .entries + .map((entry) => _buildSpaceCard( + entry.key, screenSize)) + .toList(), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ), + ], + ), + Positioned( + top: 0, + bottom: 0, + left: 250, // Align with the sidebar's width + width: 6, // Width of the gradient border + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Color(0x3F000000).withOpacity(0.2), // Light gray + Colors.transparent, // Transparent fade + ], + ), + ), + ), + ), + ], + ), + ); } // Function to open the Create Space dialog @@ -272,13 +505,23 @@ class CurvedLinePainter extends CustomPainter { ..strokeWidth = 2 ..style = PaintingStyle.stroke; + // Ensure connections exist before painting + if (connections.isEmpty) { + return; // Nothing to paint if there are no connections + } + for (var connection in connections) { + // Ensure positions are valid before drawing lines + if (connection.startSpace.position == null || + connection.endSpace.position == null) { + continue; + } + Offset start = connection.startSpace.position + Offset(75, 60); // Center bottom of start space Offset end = connection.endSpace.position + Offset(75, 0); // Center top of end space - // Draw straight line for left/right connections and curved line for down connections if (connection.direction == 'down') { // Curved line for down connections final controlPoint = Offset((start.dx + end.dx) / 2, start.dy + 50); @@ -286,32 +529,16 @@ class CurvedLinePainter extends CustomPainter { ..moveTo(start.dx, start.dy) ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy); canvas.drawPath(path, paint); - } - if (connection.direction == 'right') { - start = Offset( - connection.startSpace.position.dx + 150, - connection.startSpace.position.dy + - 30); // Right center of the first space - end = Offset( - connection.endSpace.position.dx, - connection.endSpace.position.dy + - 30); // Left center of the new space + } else if (connection.direction == 'right') { + // Straight line for right connections canvas.drawLine(start, end, paint); } else if (connection.direction == 'left') { - // Straight line for left/right connections - start = Offset( - connection.startSpace.position.dx, - connection.startSpace.position.dy + - 30); // Left center of the first space - end = Offset( - connection.endSpace.position.dx + 150, - connection.endSpace.position.dy + - 30); // Right center of the new space + // Straight line for left connections canvas.drawLine(start, end, paint); } // Draw small connection dots at the start and end points - final dotPaint = Paint()..color = ColorsManager.blackColor; + final dotPaint = Paint()..color = Colors.black; canvas.drawCircle(start, 5, dotPaint); // Start dot canvas.drawCircle(end, 5, dotPaint); // End dot } From b362029424e603be77c93ec80a960c16a57c9bec Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 10 Sep 2024 21:22:43 +0400 Subject: [PATCH 011/122] changed ui for community list and view list --- assets/icons/rounded_add_icon.svg | 4 + assets/icons/textfield_search_icon.svg | 4 + lib/common/custom_expansion_tile.dart | 104 ++++++++++++ .../view/spaces_management_page.dart | 159 +++++++++--------- lib/utils/color_manager.dart | 4 + lib/utils/constants/assets.dart | 3 + 6 files changed, 199 insertions(+), 79 deletions(-) create mode 100644 assets/icons/rounded_add_icon.svg create mode 100644 assets/icons/textfield_search_icon.svg create mode 100644 lib/common/custom_expansion_tile.dart diff --git a/assets/icons/rounded_add_icon.svg b/assets/icons/rounded_add_icon.svg new file mode 100644 index 00000000..35068a99 --- /dev/null +++ b/assets/icons/rounded_add_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/icons/textfield_search_icon.svg b/assets/icons/textfield_search_icon.svg new file mode 100644 index 00000000..143af01c --- /dev/null +++ b/assets/icons/textfield_search_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/common/custom_expansion_tile.dart b/lib/common/custom_expansion_tile.dart new file mode 100644 index 00000000..cc140263 --- /dev/null +++ b/lib/common/custom_expansion_tile.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class CustomExpansionTile extends StatefulWidget { + final String title; + final List? children; + final bool initiallyExpanded; + + CustomExpansionTile({ + required this.title, + this.children, + this.initiallyExpanded = false, + }); + + @override + CustomExpansionTileState createState() => CustomExpansionTileState(); +} + +class CustomExpansionTileState extends State { + bool _isExpanded = false; + bool _isChecked = false; + + @override + void initState() { + super.initState(); + _isExpanded = widget.initiallyExpanded; + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + InkWell( + onTap: () { + setState(() { + _isExpanded = !_isExpanded; + }); + }, + child: Row( + children: [ + // Customizing the Checkbox + Checkbox( + value: _isChecked, + onChanged: (bool? value) { + setState(() { + _isChecked = value ?? false; + }); + }, + side: WidgetStateBorderSide.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return const BorderSide(color: ColorsManager.grayBorder); + } + return const BorderSide(color: ColorsManager.grayBorder); + }), + // Customize the color for different checkbox states + fillColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return ColorsManager.grayBorder; // Checked state color + } + return ColorsManager + .checkBoxFillColor; // Unchecked state color + }), + checkColor: ColorsManager.whiteColors, // Checkmark color + ), + // Expand/Collapse Icon with reduced size + if (widget.children != null && widget.children!.isNotEmpty) + Icon( + _isExpanded + ? Icons.keyboard_arrow_down // Upward arrow when expanded + : Icons + .keyboard_arrow_right, // Right arrow when collapsed + color: Colors.grey, + size: 16.0, // Reduced icon size + ), + // Title Text + Expanded( + child: Text( + widget.title, + style: TextStyle( + color: _isExpanded + ? ColorsManager.blackColor + : ColorsManager.lightGrayColor, + fontWeight: FontWeight.w400, + ), + ), + ), + ], + ), + ), + // Display children if available and expanded + if (_isExpanded && + widget.children != null && + widget.children!.isNotEmpty) + Padding( + padding: const EdgeInsets.only(left: 48.0), // Indent the children + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: widget.children!, + ), + ), + ], + ); + } +} diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 8b992a0c..6a7f177e 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -1,8 +1,12 @@ +import 'dart:ui_web'; + import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/common/custom_expansion_tile.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; class SpaceManagementPage extends StatefulWidget { @override @@ -25,17 +29,16 @@ class SpaceManagementPageState extends State { ), body: Stack( clipBehavior: Clip.none, - children: [ Row( children: [ // Sidebar for navigation and community list Container( - width: 250, // Fixed width for sidebar + width: 300, // Fixed width for sidebar decoration: BoxDecoration( color: Colors.white, // No gradient inside ), - + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -73,107 +76,104 @@ class SpaceManagementPageState extends State { child: Container( width: 30, height: 30, - decoration: BoxDecoration( + decoration: const BoxDecoration( color: Colors.white, shape: BoxShape.circle, - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.1), - blurRadius: 10, - spreadRadius: 1, - ), - ], ), child: Center( - child: Icon(Icons.add, - color: Colors.blue, size: 18), + child: SvgPicture.asset( + Assets + .roundedAddIcon, // Path to your SVG asset + width: 24, // Set the width + height: 24, // Set the height + ), ), ), ), ], ), ), + + // Search bar // Search bar - SizedBox(height: 16), Container( decoration: BoxDecoration( + color: ColorsManager.whiteColors, boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.1), - blurRadius: 10, - spreadRadius: 1, - offset: Offset(0, 2), // Slight shadow offset + color: + Colors.black.withOpacity(0.2), // Subtle shadow + spreadRadius: 0, // No spread + blurRadius: 8, // Softer shadow edges + offset: Offset(0, 4), // Shadow only on the bottom ), ], ), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0), // Padding around the search bar - child: Container( - decoration: BoxDecoration( - color: Colors.grey[ - 200], // Light background for the search bar - borderRadius: BorderRadius.circular(10), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: - 8, // Softer shadow for the search box - ), - ], + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 20.0), + width: double + .infinity, // Make the container take the full width of its parent + decoration: BoxDecoration( + borderRadius: BorderRadius.circular( + 12), // Smooth rounded corners + ), + child: TextField( + style: const TextStyle( + color: Colors.black, // Set the text color to black ), - child: TextField( - decoration: InputDecoration( - hintText: 'Search', - prefixIcon: - Icon(Icons.search, color: Colors.grey), - border: InputBorder.none, // No visible border + decoration: InputDecoration( + filled: true, + fillColor: ColorsManager + .textFieldGreyColor, // Background color for the text field itself + hintText: 'Search', + hintStyle: TextStyle( + color: Colors.grey[400], // Gray hint text color ), + suffixIcon: Padding( + padding: const EdgeInsets.only( + right: + 16), // Add padding to avoid icon sticking to the edge + child: SvgPicture.asset( + Assets + .textFieldSearch, // Path to your SVG asset + width: 24, // Set the width + height: 24, // Set the height + ), + ), // Search icon + border: OutlineInputBorder( + borderSide: BorderSide.none, // Remove border + borderRadius: BorderRadius.circular( + 12), // Rounded corners for text field + ), + contentPadding: const EdgeInsets.symmetric( + vertical: + 14, // Vertical padding for proper text alignment + horizontal: + 16, // Add space between the text and the left edge + ), // Proper padding for alignment ), ), ), ), - SizedBox(height: 16), + + const SizedBox(height: 16), // Example of community list Expanded( child: ListView( children: [ - ListTile( - title: Text( - 'Downtown Dubai', - style: TextStyle( - color: Colors.black87, - fontWeight: FontWeight.w500, - ), - ), + CustomExpansionTile( + title: "Downtown Dubai", ), - ListTile( - title: Text( - 'Dubai Creek Harbour', - style: TextStyle( - color: Colors.black87, - fontWeight: FontWeight.w500, - ), - ), + CustomExpansionTile( + title: 'Dubai Creek Harbour', ), - ExpansionTile( - title: Text( - 'Dubai Hills Estate', - style: TextStyle( - color: Colors.black87, - fontWeight: FontWeight.w500, - ), - ), + CustomExpansionTile( + title: 'Dubai Hills Estate', children: [ - ListTile( - title: Text( - 'South Side', - style: TextStyle( - color: Colors - .black54, // Lighter text for nested - ), - ), - ), + CustomExpansionTile( + title: 'South Side', + initiallyExpanded: false), ], ), ], @@ -186,11 +186,11 @@ class SpaceManagementPageState extends State { // Right Side: Community Structure Area Expanded( child: Container( - decoration: BoxDecoration( + decoration: const BoxDecoration( color: ColorsManager.whiteColors, border: Border( left: BorderSide( - color: Colors.white!, + color: Colors.white, width: 1.0), // Light left border to match ), ), @@ -199,7 +199,8 @@ class SpaceManagementPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - padding: const EdgeInsets.all(16.0), + padding: + const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), width: double.infinity, decoration: BoxDecoration( color: ColorsManager.whiteColors, @@ -273,15 +274,15 @@ class SpaceManagementPageState extends State { Positioned( top: 0, bottom: 0, - left: 250, // Align with the sidebar's width - width: 6, // Width of the gradient border + left: 300, // Align with the sidebar's width + width: 8, // Width of the gradient border child: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [ - Color(0x3F000000).withOpacity(0.2), // Light gray + Color(0x3F000000).withOpacity(0.1), // Light gray Colors.transparent, // Transparent fade ], ), diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index b62889d0..8f42e606 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -18,6 +18,7 @@ abstract class ColorsManager { static const Color greyColor = Color(0xFFd5d5d5); static const Color lightGreyColor = Color(0xFF999999); static const Color backgroundColor = Color(0xFFececec); + static const Color textFieldGreyColor = Color(0xFFF4F4F4); static const Color dozeColor = Color(0xFFFEC258); static const Color relaxColor = Color(0xFFFBD288); static const Color readingColor = Color(0xFFF7D69C); @@ -31,6 +32,8 @@ abstract class ColorsManager { static const Color grayColor = Color(0xFF999999); static const Color red = Color(0xFFFF0000); static const Color graysColor = Color(0xffEBEBEB); + static const Color lightGrayColor = Color(0xB2999999); + static const Color grayBorder = Color(0xFFCFCFCF); static const Color textGray = Color(0xffD5D5D5); static const Color btnColor = Color(0xFF00008B); static const Color blueColor = Color(0xFF0036E6); @@ -42,5 +45,6 @@ abstract class ColorsManager { static const Color blue4 = Color(0xFF001E7E); static const Color textGreen = Color(0xFF008905); static const Color yaGreen = Color(0xFFFFBF44); + static const Color checkBoxFillColor = Color(0xFFF3F3F3); } //0036E6 \ No newline at end of file diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 34b685d3..7c01ddb4 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -159,4 +159,7 @@ class Assets { static const String unit = 'assets/icons/unit_icon.svg'; static const String villa = 'assets/icons/villa_icon.svg'; static const String iconEdit = 'assets/icons/icon_edit_icon.svg'; + static const String textFieldSearch = + 'assets/icons/textfield_search_icon.svg'; + static const String roundedAddIcon = 'assets/icons/rounded_add_icon.svg'; } From b40998558e1999d997d3eb2e293daad9118586f2 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 10 Sep 2024 22:57:34 +0400 Subject: [PATCH 012/122] reduce duplicated code --- lib/common/custom_expansion_tile.dart | 34 ++-- lib/common/search_bar.dart | 70 ++++++++ lib/main.dart | 1 + .../view/community_tile.dart | 29 ++++ .../view/sidebar_widget.dart | 107 ++++++++++++ .../view/spaces_management_page.dart | 157 +----------------- lib/utils/style.dart | 12 ++ 7 files changed, 248 insertions(+), 162 deletions(-) create mode 100644 lib/common/search_bar.dart create mode 100644 lib/pages/spaces_management/view/community_tile.dart create mode 100644 lib/pages/spaces_management/view/sidebar_widget.dart diff --git a/lib/common/custom_expansion_tile.dart b/lib/common/custom_expansion_tile.dart index cc140263..5876ae31 100644 --- a/lib/common/custom_expansion_tile.dart +++ b/lib/common/custom_expansion_tile.dart @@ -5,11 +5,15 @@ class CustomExpansionTile extends StatefulWidget { final String title; final List? children; final bool initiallyExpanded; + final bool? isExpanded; // New parameter to control expansion + final ValueChanged? onExpansionChanged; // Callback for expansion change CustomExpansionTile({ required this.title, this.children, this.initiallyExpanded = false, + this.isExpanded, // Allow external control over expansion + this.onExpansionChanged, // Notify when expansion changes }); @override @@ -26,6 +30,16 @@ class CustomExpansionTileState extends State { _isExpanded = widget.initiallyExpanded; } + @override + void didUpdateWidget(CustomExpansionTile oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.isExpanded != null && widget.isExpanded != _isExpanded) { + setState(() { + _isExpanded = widget.isExpanded!; + }); + } + } + @override Widget build(BuildContext context) { return Column( @@ -34,6 +48,7 @@ class CustomExpansionTileState extends State { onTap: () { setState(() { _isExpanded = !_isExpanded; + widget.onExpansionChanged?.call(_isExpanded); }); }, child: Row( @@ -47,28 +62,22 @@ class CustomExpansionTileState extends State { }); }, side: WidgetStateBorderSide.resolveWith((states) { - if (states.contains(WidgetState.selected)) { - return const BorderSide(color: ColorsManager.grayBorder); - } return const BorderSide(color: ColorsManager.grayBorder); }), - // Customize the color for different checkbox states fillColor: WidgetStateProperty.resolveWith((states) { if (states.contains(WidgetState.selected)) { - return ColorsManager.grayBorder; // Checked state color + return ColorsManager.grayBorder; + } else { + return ColorsManager.checkBoxFillColor; } - return ColorsManager - .checkBoxFillColor; // Unchecked state color }), - checkColor: ColorsManager.whiteColors, // Checkmark color + checkColor: ColorsManager.whiteColors, ), - // Expand/Collapse Icon with reduced size if (widget.children != null && widget.children!.isNotEmpty) Icon( _isExpanded - ? Icons.keyboard_arrow_down // Upward arrow when expanded - : Icons - .keyboard_arrow_right, // Right arrow when collapsed + ? Icons.keyboard_arrow_down + : Icons.keyboard_arrow_right, color: Colors.grey, size: 16.0, // Reduced icon size ), @@ -87,7 +96,6 @@ class CustomExpansionTileState extends State { ], ), ), - // Display children if available and expanded if (_isExpanded && widget.children != null && widget.children!.isNotEmpty) diff --git a/lib/common/search_bar.dart b/lib/common/search_bar.dart new file mode 100644 index 00000000..073957b2 --- /dev/null +++ b/lib/common/search_bar.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class CustomSearchBar extends StatelessWidget { + final TextEditingController? controller; + final String hintText; + + const CustomSearchBar({ + Key? key, + this.controller, + this.hintText = 'Search', + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + spreadRadius: 0, + blurRadius: 8, + offset: const Offset(0, 4), + ), + ], + ), + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 20.0), + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + ), + child: TextField( + controller: controller, + style: const TextStyle( + color: Colors.black, + ), + decoration: InputDecoration( + filled: true, + fillColor: ColorsManager.textFieldGreyColor, + hintText: hintText, + hintStyle: TextStyle( + color: Colors.grey[400], + ), + suffixIcon: Padding( + padding: const EdgeInsets.only(right: 16), + child: SvgPicture.asset( + Assets.textFieldSearch, + width: 24, + height: 24, + ), + ), + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(12), + ), + contentPadding: const EdgeInsets.symmetric( + vertical: 14, + horizontal: 16, + ), + ), + ), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 90c643cc..1bd86775 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -59,6 +59,7 @@ class MyApp extends StatelessWidget { bodyLarge: TextStyle(fontSize: 16, color: Colors.white), headlineSmall: TextStyle(color: Colors.black87, fontSize: 18), headlineMedium: TextStyle(color: Colors.black87, fontSize: 20), + titleMedium: TextStyle(color: Colors.black87, fontSize: 22, fontWeight: FontWeight.bold ), headlineLarge: TextStyle( color: Colors.white, fontSize: 24, diff --git a/lib/pages/spaces_management/view/community_tile.dart b/lib/pages/spaces_management/view/community_tile.dart new file mode 100644 index 00000000..5530ea13 --- /dev/null +++ b/lib/pages/spaces_management/view/community_tile.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/common/custom_expansion_tile.dart'; + +class CommunityTile extends StatelessWidget { + final String title; + final String expandedTile; + final Function(String, bool) onExpansionChanged; + final List? children; + + const CommunityTile({ + Key? key, + required this.title, + required this.expandedTile, + required this.onExpansionChanged, + this.children, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return CustomExpansionTile( + title: title, + isExpanded: expandedTile == title, + onExpansionChanged: (bool expanded) { + onExpansionChanged(title, expanded); + }, + children: children, + ); + } +} diff --git a/lib/pages/spaces_management/view/sidebar_widget.dart b/lib/pages/spaces_management/view/sidebar_widget.dart new file mode 100644 index 00000000..8572c63c --- /dev/null +++ b/lib/pages/spaces_management/view/sidebar_widget.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/common/search_bar.dart'; +import 'package:syncrow_web/pages/spaces_management/view/community_tile.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/style.dart'; + +class SidebarWidget extends StatefulWidget { + final Function(String)? onCommunitySelected; + + SidebarWidget({this.onCommunitySelected}); + + @override + _SidebarWidgetState createState() => _SidebarWidgetState(); +} + +class _SidebarWidgetState extends State { + String? _expandedTile; + + // A helper method to handle the expansion logic for CustomExpansionTile + void _handleExpansionChange(String title, bool expanded) { + setState(() { + _expandedTile = expanded ? title : null; + }); + widget.onCommunitySelected?.call(title); + } + + @override + Widget build(BuildContext context) { + return Container( + width: 300, + decoration: subSectionContainerDecoration, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Communities title with the add button + Container( + decoration: subSectionContainerDecoration, + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Communities', + style: Theme.of(context).textTheme.titleMedium, + ), + GestureDetector( + onTap: () { + // Handle add button action + }, + child: Container( + width: 30, + height: 30, + decoration: const BoxDecoration( + color: ColorsManager.whiteColors, + shape: BoxShape.circle, + ), + child: Center( + child: SvgPicture.asset( + Assets.roundedAddIcon, + width: 24, + height: 24, + ), + ), + ), + ), + ], + ), + ), + // Search bar + const CustomSearchBar(), + const SizedBox(height: 16), + // Community list with one item expanded at a time + Expanded( + child: ListView( + children: [ + CommunityTile( + title: "Downtown Dubai", + expandedTile: _expandedTile ?? '', + onExpansionChanged: _handleExpansionChange, + ), + CommunityTile( + title: 'Dubai Creek Harbour', + expandedTile: _expandedTile ?? '', + onExpansionChanged: _handleExpansionChange, + ), + CommunityTile( + title: 'Dubai Hills Estate', + expandedTile: _expandedTile ?? '', + onExpansionChanged: _handleExpansionChange, + children: [ + CommunityTile( + title: 'South Side', + expandedTile: _expandedTile ?? '', + onExpansionChanged: _handleExpansionChange, + ), + ], + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 6a7f177e..e5f6eedf 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -5,6 +5,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/common/custom_expansion_tile.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; +import 'package:syncrow_web/pages/spaces_management/view/sidebar_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -18,6 +19,11 @@ class SpaceManagementPageState extends State { List spaces = []; List connections = []; + void _handleCommunitySelection(String community) { + // Handle community selection here + print("Selected Community: $community"); + } + @override Widget build(BuildContext context) { Size screenSize = MediaQuery.of(context).size; @@ -33,156 +39,9 @@ class SpaceManagementPageState extends State { Row( children: [ // Sidebar for navigation and community list - Container( - width: 300, // Fixed width for sidebar - decoration: BoxDecoration( - color: Colors.white, // No gradient inside - ), - - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // "Communities" title with the add button - Container( - decoration: BoxDecoration( - color: Colors.white, // Background color - - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.1), - blurRadius: 10, - spreadRadius: 1, - offset: Offset(0, 2), // Adjust the shadow position - ), - ], - ), - padding: const EdgeInsets.all( - 16.0), // Apply padding inside the container - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Communities', - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - ), - ), - // Plus Button (+) - GestureDetector( - onTap: () { - // Handle the button tap action - }, - child: Container( - width: 30, - height: 30, - decoration: const BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - ), - child: Center( - child: SvgPicture.asset( - Assets - .roundedAddIcon, // Path to your SVG asset - width: 24, // Set the width - height: 24, // Set the height - ), - ), - ), - ), - ], - ), - ), - - // Search bar - // Search bar - Container( - decoration: BoxDecoration( - color: ColorsManager.whiteColors, - boxShadow: [ - BoxShadow( - color: - Colors.black.withOpacity(0.2), // Subtle shadow - spreadRadius: 0, // No spread - blurRadius: 8, // Softer shadow edges - offset: Offset(0, 4), // Shadow only on the bottom - ), - ], - ), - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Container( - padding: const EdgeInsets.symmetric(vertical: 20.0), - width: double - .infinity, // Make the container take the full width of its parent - decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - 12), // Smooth rounded corners - ), - child: TextField( - style: const TextStyle( - color: Colors.black, // Set the text color to black - ), - decoration: InputDecoration( - filled: true, - fillColor: ColorsManager - .textFieldGreyColor, // Background color for the text field itself - hintText: 'Search', - hintStyle: TextStyle( - color: Colors.grey[400], // Gray hint text color - ), - suffixIcon: Padding( - padding: const EdgeInsets.only( - right: - 16), // Add padding to avoid icon sticking to the edge - child: SvgPicture.asset( - Assets - .textFieldSearch, // Path to your SVG asset - width: 24, // Set the width - height: 24, // Set the height - ), - ), // Search icon - border: OutlineInputBorder( - borderSide: BorderSide.none, // Remove border - borderRadius: BorderRadius.circular( - 12), // Rounded corners for text field - ), - contentPadding: const EdgeInsets.symmetric( - vertical: - 14, // Vertical padding for proper text alignment - horizontal: - 16, // Add space between the text and the left edge - ), // Proper padding for alignment - ), - ), - ), - ), - - const SizedBox(height: 16), - // Example of community list - Expanded( - child: ListView( - children: [ - CustomExpansionTile( - title: "Downtown Dubai", - ), - CustomExpansionTile( - title: 'Dubai Creek Harbour', - ), - CustomExpansionTile( - title: 'Dubai Hills Estate', - children: [ - CustomExpansionTile( - title: 'South Side', - initiallyExpanded: false), - ], - ), - ], - ), - ), - ], - ), + SidebarWidget( + onCommunitySelected: _handleCommunitySelection, ), - // Right Side: Community Structure Area Expanded( child: Container( diff --git a/lib/utils/style.dart b/lib/utils/style.dart index 3f31f9d7..bdc4eb3f 100644 --- a/lib/utils/style.dart +++ b/lib/utils/style.dart @@ -41,3 +41,15 @@ BoxDecoration containerDecoration = BoxDecoration( ], color: ColorsManager.boxColor, borderRadius: const BorderRadius.all(Radius.circular(10))); + +BoxDecoration subSectionContainerDecoration = BoxDecoration( + color: ColorsManager.whiteColors, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 10, + spreadRadius: 1, + offset: const Offset(0, 2), + ), + ], +); From fe3525d2559d899be9b64b4ded003df3298a2079 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 26 Sep 2024 14:49:19 +0400 Subject: [PATCH 013/122] sidebar widget --- lib/common/search_bar.dart | 7 +- .../model/community_model.dart | 6 ++ .../view/sidebar_widget.dart | 82 +++++++++++++------ .../view/spaces_management_page.dart | 2 +- 4 files changed, 67 insertions(+), 30 deletions(-) create mode 100644 lib/pages/spaces_management/model/community_model.dart diff --git a/lib/common/search_bar.dart b/lib/common/search_bar.dart index 073957b2..21ac8055 100644 --- a/lib/common/search_bar.dart +++ b/lib/common/search_bar.dart @@ -6,12 +6,14 @@ import 'package:syncrow_web/utils/constants/assets.dart'; class CustomSearchBar extends StatelessWidget { final TextEditingController? controller; final String hintText; + final Function(String)? onSearchChanged; // Callback for search input changes const CustomSearchBar({ - Key? key, + super.key, this.controller, this.hintText = 'Search', - }) : super(key: key); + this.onSearchChanged, + }); @override Widget build(BuildContext context) { @@ -39,6 +41,7 @@ class CustomSearchBar extends StatelessWidget { style: const TextStyle( color: Colors.black, ), + onChanged: onSearchChanged, // Call the callback on text change decoration: InputDecoration( filled: true, fillColor: ColorsManager.textFieldGreyColor, diff --git a/lib/pages/spaces_management/model/community_model.dart b/lib/pages/spaces_management/model/community_model.dart new file mode 100644 index 00000000..5c4b1ce6 --- /dev/null +++ b/lib/pages/spaces_management/model/community_model.dart @@ -0,0 +1,6 @@ +class Community { + final String name; + final List? children; // Sub-communities + + Community({required this.name, this.children}); +} diff --git a/lib/pages/spaces_management/view/sidebar_widget.dart b/lib/pages/spaces_management/view/sidebar_widget.dart index 8572c63c..8d775672 100644 --- a/lib/pages/spaces_management/view/sidebar_widget.dart +++ b/lib/pages/spaces_management/view/sidebar_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/common/search_bar.dart'; +import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/community_tile.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -17,6 +18,19 @@ class SidebarWidget extends StatefulWidget { class _SidebarWidgetState extends State { String? _expandedTile; + String _searchQuery = ''; // Track search query + + // List of all communities + final List _communities = [ + Community(name: 'Downtown Dubai'), + Community(name: 'Dubai Creek Harbour'), + Community( + name: 'Dubai Hills Estate', + children: [ + Community(name: 'South Side'), // Nested community + ], + ), + ]; // A helper method to handle the expansion logic for CustomExpansionTile void _handleExpansionChange(String title, bool expanded) { @@ -26,8 +40,20 @@ class _SidebarWidgetState extends State { widget.onCommunitySelected?.call(title); } + List _filterCommunities() { + if (_searchQuery.isEmpty) { + return _communities; + } + return _communities + .where((community) => + community.name.toLowerCase().contains(_searchQuery.toLowerCase())) + .toList(); + } + @override Widget build(BuildContext context) { + List filteredCommunities = _filterCommunities(); + return Container( width: 300, decoration: subSectionContainerDecoration, @@ -69,39 +95,41 @@ class _SidebarWidgetState extends State { ), ), // Search bar - const CustomSearchBar(), + CustomSearchBar( + onSearchChanged: (query) { + setState(() { + _searchQuery = query; // Update search query on text change + }); + }, + ), const SizedBox(height: 16), // Community list with one item expanded at a time Expanded( - child: ListView( - children: [ - CommunityTile( - title: "Downtown Dubai", - expandedTile: _expandedTile ?? '', - onExpansionChanged: _handleExpansionChange, - ), - CommunityTile( - title: 'Dubai Creek Harbour', - expandedTile: _expandedTile ?? '', - onExpansionChanged: _handleExpansionChange, - ), - CommunityTile( - title: 'Dubai Hills Estate', - expandedTile: _expandedTile ?? '', - onExpansionChanged: _handleExpansionChange, - children: [ - CommunityTile( - title: 'South Side', - expandedTile: _expandedTile ?? '', - onExpansionChanged: _handleExpansionChange, - ), - ], - ), - ], + child: ListView.builder( + itemCount: filteredCommunities.length, + itemBuilder: (context, index) { + Community community = filteredCommunities[index]; + return _buildCommunityTile(community); + }, ), ), ], ), ); } + + Widget _buildCommunityTile(Community community) { + bool hasChildren = + community.children != null && community.children!.isNotEmpty; + return CommunityTile( + title: community.name, + expandedTile: _expandedTile ?? '', + onExpansionChanged: _handleExpansionChange, + children: hasChildren + ? community.children! + .map((child) => _buildCommunityTile(child)) + .toList() + : null, // Recursively render sub-communities + ); + } } diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index e5f6eedf..50fc9c3a 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -9,7 +9,7 @@ import 'package:syncrow_web/pages/spaces_management/view/sidebar_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -class SpaceManagementPage extends StatefulWidget { +class SpaceManagementPage extends StatefulWidget with HelperResponsiveLayout { @override SpaceManagementPageState createState() => SpaceManagementPageState(); } From c3000f8a7fb2cd6e67331e316095fc8bc293bead Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 26 Sep 2024 14:51:07 +0400 Subject: [PATCH 014/122] fixed bug --- lib/pages/spaces_management/view/spaces_management_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 50fc9c3a..3f646d05 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -9,7 +9,7 @@ import 'package:syncrow_web/pages/spaces_management/view/sidebar_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -class SpaceManagementPage extends StatefulWidget with HelperResponsiveLayout { +class SpaceManagementPage extends StatefulWidget{ @override SpaceManagementPageState createState() => SpaceManagementPageState(); } From 23f92d4901a35b0cfa52641c0c18bf36d813add3 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 26 Sep 2024 14:54:11 +0400 Subject: [PATCH 015/122] added route from home screen --- lib/pages/home/bloc/home_bloc.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/pages/home/bloc/home_bloc.dart b/lib/pages/home/bloc/home_bloc.dart index 42a63592..7993c1ce 100644 --- a/lib/pages/home/bloc/home_bloc.dart +++ b/lib/pages/home/bloc/home_bloc.dart @@ -41,7 +41,8 @@ class HomeBloc extends Bloc { Future fetchUserInfo() async { try { - var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); + var uuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey); user = await HomeApi().fetchUserInfo(uuid); emit(HomeUserInfoLoaded(user!)); // Emit state after fetching user info } catch (e) { @@ -63,7 +64,9 @@ class HomeBloc extends Bloc { title: 'Space Management', icon: Assets.spaseManagementIcon, active: true, - onPress: (context) {}, + onPress: (context) { + context.go(RoutesConst.spacesManagementPage); + }, color: ColorsManager.primaryColor, ), HomeItemModel( From ef233c414ba9807f9a62a8ac519a06ae1f8ffb54 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 2 Oct 2024 10:11:55 +0400 Subject: [PATCH 016/122] added api for space management --- lib/common/search_bar.dart | 7 +- lib/services/space_mana_api.dart | 230 +++++++++++++++++++++++++++++ lib/utils/app_routes.dart | 4 +- lib/utils/constants/api_const.dart | 30 +++- 4 files changed, 267 insertions(+), 4 deletions(-) create mode 100644 lib/services/space_mana_api.dart diff --git a/lib/common/search_bar.dart b/lib/common/search_bar.dart index 21ac8055..728fad33 100644 --- a/lib/common/search_bar.dart +++ b/lib/common/search_bar.dart @@ -47,7 +47,12 @@ class CustomSearchBar extends StatelessWidget { fillColor: ColorsManager.textFieldGreyColor, hintText: hintText, hintStyle: TextStyle( - color: Colors.grey[400], + color: Color(0xB2999999), + fontSize: 12, + fontFamily: 'Aftika', + fontWeight: FontWeight.w400, + height: 0, + letterSpacing: -0.24, ), suffixIcon: Padding( padding: const EdgeInsets.only(right: 16), diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart new file mode 100644 index 00000000..0a96ed2b --- /dev/null +++ b/lib/services/space_mana_api.dart @@ -0,0 +1,230 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_response_model.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/constants/api_const.dart'; + +class CommunitySpaceManagementApi { + // Community Management APIs + Future> fetchCommunities() async { + try { + final response = await HTTPService().get( + path: ApiEndpoints.getCommunityList, + showServerMessage: true, + expectedResponseModel: (json) { + List jsonData = json; + List communityList = jsonData.map((jsonItem) { + return CommunityModel.fromJson(jsonItem); + }).toList(); + return communityList; + }, + ); + return response; + } catch (e) { + debugPrint('Error fetching communities: $e'); + return []; + } + } + + Future getCommunityById(String communityId) async { + try { + final response = await HTTPService().get( + path: ApiEndpoints.getCommunityById + .replaceAll('{communityId}', communityId), + showServerMessage: true, + expectedResponseModel: (json) { + return CommunityModel.fromJson(json['data']); + }, + ); + return response; + } catch (e) { + debugPrint('Error fetching community by ID: $e'); + return null; + } + } + + Future createCommunity( + String name, String description, String regionId) async { + try { + final response = await HTTPService().post( + path: ApiEndpoints.createCommunity, + body: { + 'name': name, + 'description': description, + 'regionId': regionId, + }, + showServerMessage: true, + expectedResponseModel: (json) { + return CommunityModel.fromJson(json['data']); + }, + ); + return response; + } catch (e) { + debugPrint('Error creating community: $e'); + return null; + } + } + + Future updateCommunity( + String communityId, CommunityModel community) async { + try { + final response = await HTTPService().put( + path: ApiEndpoints.updateCommunity + .replaceAll('{communityId}', communityId), + body: community.toMap(), + showServerMessage: true, + expectedResponseModel: (json) { + return json['success'] ?? false; + }, + ); + return response; + } catch (e) { + debugPrint('Error updating community: $e'); + return false; + } + } + + Future deleteCommunity(String communityId) async { + try { + final response = await HTTPService().delete( + path: ApiEndpoints.deleteCommunity + .replaceAll('{communityId}', communityId), + showServerMessage: true, + expectedResponseModel: (json) { + return json['success'] ?? false; + }, + ); + return response; + } catch (e) { + debugPrint('Error deleting community: $e'); + return false; + } + } + + Future fetchSpaces(String communityId) async { + try { + final response = await HTTPService().get( + path: ApiEndpoints.listSpaces.replaceAll('{communityId}', communityId), + showServerMessage: true, + expectedResponseModel: (json) { + return SpacesResponse.fromJson(json); + }, + ); + return response; + } catch (e) { + debugPrint('Error fetching spaces: $e'); + return SpacesResponse( + data: [], + message: 'Error fetching spaces', + page: 1, + size: 10, + totalItem: 0, + totalPage: 0, + hasNext: false, + hasPrevious: false, + ); + } + } + + Future getSpace(String communityId, String spaceId) async { + try { + final response = await HTTPService().get( + path: ApiEndpoints.getSpace + .replaceAll('{communityId}', communityId) + .replaceAll('{spaceId}', spaceId), + showServerMessage: true, + expectedResponseModel: (json) { + return SpaceModel.fromJson(json); + }, + ); + return response; + } catch (e) { + debugPrint('Error fetching space: $e'); + return SpaceModel(); // Assuming an empty SpaceModel constructor + } + } + + Future createSpace(String communityId, String name, + {String? parentId, bool isPrivate = false}) async { + try { + final body = { + 'name': name, + 'isPrivate': isPrivate, + }; + if (parentId != null) { + body['parentId'] = parentId; + } + + final response = await HTTPService().post( + path: ApiEndpoints.createSpace.replaceAll('{communityId}', communityId), + body: body, + showServerMessage: true, + expectedResponseModel: (json) { + return SpaceModel.fromJson(json['data']); + }, + ); + return response; + } catch (e) { + debugPrint('Error creating space: $e'); + return null; + } + } + + Future updateSpace( + String communityId, String spaceId, SpaceModel space) async { + try { + final response = await HTTPService().put( + path: ApiEndpoints.updateSpace + .replaceAll('{communityId}', communityId) + .replaceAll('{spaceId}', spaceId), + body: space.toMap(), + showServerMessage: true, + expectedResponseModel: (json) { + return json['success'] ?? false; + }, + ); + return response; + } catch (e) { + debugPrint('Error updating space: $e'); + return false; + } + } + + Future deleteSpace(String communityId, String spaceId) async { + try { + final response = await HTTPService().delete( + path: ApiEndpoints.deleteSpace + .replaceAll('{communityId}', communityId) + .replaceAll('{spaceId}', spaceId), + showServerMessage: true, + expectedResponseModel: (json) { + return json['success'] ?? false; + }, + ); + return response; + } catch (e) { + debugPrint('Error deleting space: $e'); + return false; + } + } + + Future> getSpaceHierarchy(String communityId) async { + try { + final response = await HTTPService().get( + path: ApiEndpoints.getSpaceHierarchy + .replaceAll('{communityId}', communityId), + showServerMessage: true, + expectedResponseModel: (json) { + return (json['data'] as List) + .map((spaceJson) => SpaceModel.fromJson(spaceJson)) + .toList(); + }, + ); + return response; + } catch (e) { + debugPrint('Error fetching space hierarchy: $e'); + return []; + } + } +} diff --git a/lib/utils/app_routes.dart b/lib/utils/app_routes.dart index 3714aa69..b14da393 100644 --- a/lib/utils/app_routes.dart +++ b/lib/utils/app_routes.dart @@ -12,7 +12,7 @@ class AppRoutes { return [ GoRoute( path: RoutesConst.auth, - builder: (context, state) => SpaceManagementPage(), + builder: (context, state) => const HomePage(), ), GoRoute( path: RoutesConst.home, @@ -32,7 +32,7 @@ class AppRoutes { ), GoRoute( path: RoutesConst.spacesManagementPage, - builder: (context, state) => SpaceManagementPage()), + builder: (context, state) => SpaceManagementPage()), ]; } } diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 2d85a7f5..487aeaa3 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -1,6 +1,7 @@ abstract class ApiEndpoints { static const String baseUrl = 'https://syncrow-dev.azurewebsites.net'; - // static const String baseUrl = 'http://100.107.182.63:4001'; //Localhost + static const String baseLocalUrl = 'http://localhost:4001'; //Localhost + //https://syncrow-staging.azurewebsites.net ////////////////////////////////////// Authentication /////////////////////////////// static const String signUp = '$baseUrl/authentication/user/signup'; @@ -38,4 +39,31 @@ abstract class ApiEndpoints { static const String getDeviceLogs = '$baseUrl/device/report-logs/{uuid}?code={code}'; + +// Space Module + static const String createSpace = + '$baseLocalUrl/communities/{communityId}/spaces'; + static const String listSpaces = + '$baseLocalUrl/communities/{communityId}/spaces'; + static const String deleteSpace = + '$baseLocalUrl/communities/{communityId}/spaces/{spaceId}'; + static const String updateSpace = + '$baseLocalUrl/communities/{communityId}/spaces/{spaceId}'; + static const String getSpace = + '$baseLocalUrl/communities/{communityId}/spaces/{spaceId}'; + static const String getSpaceHierarchy = + '$baseLocalUrl/communities/{communityId}/spaces/hierarchy'; + +// Community Module + static const String createCommunity = '$baseLocalUrl/communities'; + static const String getCommunityList = '$baseLocalUrl/communities'; + static const String getCommunityById = + '$baseLocalUrl/communities/{communityId}'; + static const String updateCommunity = + '$baseLocalUrl/communities/{communityId}'; + static const String deleteCommunity = + '$baseLocalUrl/communities/{communityId}'; + static const String getUserCommunities = + '$baseLocalUrl/communities/user/{userUuid}'; + static const String createUserCommunity = '$baseLocalUrl/communities/user'; } From 86728177911b4843755c81e5c2c6e663d320e94b Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 2 Oct 2024 10:27:54 +0400 Subject: [PATCH 017/122] updated models --- .../bloc/space_management_bloc.dart | 2 + .../spaces_management/bloc/test_bloc.dart | 60 ++++++++ .../model/community_model.dart | 44 +++++- .../spaces_management/model/space_model.dart | 70 ++++++++- .../model/space_response_model.dart | 67 +++++++++ .../view/dialogs/create_community_dialog.dart | 136 ++++++++++++++++++ 6 files changed, 371 insertions(+), 8 deletions(-) create mode 100644 lib/pages/spaces_management/bloc/test_bloc.dart create mode 100644 lib/pages/spaces_management/model/space_response_model.dart create mode 100644 lib/pages/spaces_management/view/dialogs/create_community_dialog.dart diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index bb4afe19..678ba96d 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -58,3 +58,5 @@ class SpacesManagementBloc extends Bloc { }); } } + + diff --git a/lib/pages/spaces_management/bloc/test_bloc.dart b/lib/pages/spaces_management/bloc/test_bloc.dart new file mode 100644 index 00000000..0b4dbe9d --- /dev/null +++ b/lib/pages/spaces_management/bloc/test_bloc.dart @@ -0,0 +1,60 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +// Define User States +abstract class UserState extends Equatable { + @override + List get props => []; +} + +class UserInitial extends UserState {} + +class UserLoaded extends UserState { + final List users; + final String selectedUser; + + UserLoaded({required this.users, required this.selectedUser}); + + @override + List get props => [users, selectedUser]; +} + +abstract class UserEvent extends Equatable { + @override + List get props => []; +} + +class LoadUsers extends UserEvent { + @override + List get props => []; +} + +class SelectUser extends UserEvent { + final String selectedUser; + + SelectUser(this.selectedUser); + + @override + List get props => [selectedUser]; +} + +class UserManagementBloc extends Bloc { + UserManagementBloc() : super(UserInitial()) { + on((event, emit) { + // Dummy list of users + final List users = []; + + // Emit the UserLoaded state with the dummy users + emit(UserLoaded(users: users, selectedUser: users[0])); + }); + + on((event, emit) { + // Handle user selection in the UserLoaded state + if (state is UserLoaded) { + final loadedState = state as UserLoaded; + emit(UserLoaded( + users: loadedState.users, selectedUser: event.selectedUser)); + } + }); + } +} diff --git a/lib/pages/spaces_management/model/community_model.dart b/lib/pages/spaces_management/model/community_model.dart index 5c4b1ce6..a37dcbd5 100644 --- a/lib/pages/spaces_management/model/community_model.dart +++ b/lib/pages/spaces_management/model/community_model.dart @@ -1,6 +1,42 @@ -class Community { - final String name; - final List? children; // Sub-communities +import 'package:syncrow_web/pages/auth/model/region_model.dart'; - Community({required this.name, this.children}); +class CommunityModel { + final String uuid; + final DateTime createdAt; + final DateTime updatedAt; + final String name; + final String description; + final RegionModel? region; + + CommunityModel({ + required this.uuid, + required this.createdAt, + required this.updatedAt, + required this.name, + required this.description, + this.region, + }); + + factory CommunityModel.fromJson(Map json) { + return CommunityModel( + uuid: json['uuid'], + createdAt: DateTime.parse(json['createdAt']), + updatedAt: DateTime.parse(json['updatedAt']), + name: json['name'], + description: json['description'], + region: + json['region'] != null ? RegionModel.fromJson(json['region']) : null, + ); + } + + Map toMap() { + return { + 'uuid': uuid, + 'createdAt': createdAt.toIso8601String(), + 'updatedAt': updatedAt.toIso8601String(), + 'name': name, + 'description': description, + 'region': region?.toJson(), + }; + } } diff --git a/lib/pages/spaces_management/model/space_model.dart b/lib/pages/spaces_management/model/space_model.dart index ee488441..82a94d16 100644 --- a/lib/pages/spaces_management/model/space_model.dart +++ b/lib/pages/spaces_management/model/space_model.dart @@ -1,6 +1,68 @@ -class SpaceModel { - final String communityName; - final List subSpaces; +import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; - SpaceModel({required this.communityName, required this.subSpaces}); +class SpaceModel { + final String uuid; + final DateTime createdAt; + final DateTime updatedAt; + final String? spaceTuyaUuid; + final String name; + final bool isPrivate; + final String? invitationCode; + final bool isParent; + final SpaceModel? parent; + final CommunityModel? community; + final List children; // List of child spaces + + SpaceModel({ + required this.uuid, + required this.createdAt, + required this.updatedAt, + this.spaceTuyaUuid, + required this.name, + required this.isPrivate, + this.invitationCode, + required this.isParent, + this.parent, + this.community, + required this.children, + }); + + factory SpaceModel.fromJson(Map json) { + return SpaceModel( + uuid: json['uuid'], + createdAt: DateTime.parse(json['createdAt']), + updatedAt: DateTime.parse(json['updatedAt']), + spaceTuyaUuid: json['spaceTuyaUuid'], + name: json['name'], + isPrivate: json['isPrivate'], + invitationCode: json['invitationCode'], + isParent: json['isParent'], + parent: + json['parent'] != null ? SpaceModel.fromJson(json['parent']) : null, + community: json['community'] != null + ? CommunityModel.fromJson(json['community']) + : null, + children: json['children'] != null + ? (json['children'] as List) + .map((child) => SpaceModel.fromJson(child)) + .toList() + : [], + ); + } + + Map toMap() { + return { + 'uuid': uuid, + 'createdAt': createdAt.toIso8601String(), + 'updatedAt': updatedAt.toIso8601String(), + 'spaceTuyaUuid': spaceTuyaUuid, + 'name': name, + 'isPrivate': isPrivate, + 'invitationCode': invitationCode, + 'isParent': isParent, + 'parent': parent?.toMap(), + 'community': community?.toMap(), + 'children': children.map((child) => child.toMap()).toList(), + }; + } } diff --git a/lib/pages/spaces_management/model/space_response_model.dart b/lib/pages/spaces_management/model/space_response_model.dart new file mode 100644 index 00000000..ab6a8f79 --- /dev/null +++ b/lib/pages/spaces_management/model/space_response_model.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; + +import 'space_model.dart'; + +class SpacesResponse { + final List data; + final String message; + final int page; + final int size; + final int totalItem; + final int totalPage; + final bool hasNext; + final bool hasPrevious; + + SpacesResponse({ + required this.data, + required this.message, + required this.page, + required this.size, + required this.totalItem, + required this.totalPage, + required this.hasNext, + required this.hasPrevious, + }); + + factory SpacesResponse.fromJson(Map json) { + return SpacesResponse( + data: (json['data'] as List) + .map((jsonItem) => SpaceModel.fromJson(jsonItem)) + .toList(), + message: json['message'], + page: json['page'], + size: json['size'], + totalItem: json['totalItem'], + totalPage: json['totalPage'], + hasNext: json['hasNext'], + hasPrevious: json['hasPrevious'], + ); + } +} + +class CommunitySpaceManagementApi { + Future fetchSpaces(String communityId) async { + try { + final response = await HTTPService().get( + path: ApiEndpoints.listSpaces.replaceAll('{communityId}', communityId), + showServerMessage: true, + expectedResponseModel: (json) { + return SpacesResponse.fromJson(json); + }, + ); + return response; + } catch (e) { + debugPrint('Error fetching spaces: $e'); + return SpacesResponse( + data: [], + message: 'Error fetching spaces', + page: 1, + size: 10, + totalItem: 0, + totalPage: 0, + hasNext: false, + hasPrevious: false, + ); + } + } +} diff --git a/lib/pages/spaces_management/view/dialogs/create_community_dialog.dart b/lib/pages/spaces_management/view/dialogs/create_community_dialog.dart new file mode 100644 index 00000000..2eb8107e --- /dev/null +++ b/lib/pages/spaces_management/view/dialogs/create_community_dialog.dart @@ -0,0 +1,136 @@ +import 'package:flutter/material.dart'; + +class CreateCommunityDialog extends StatefulWidget { + final Function(String) onCreateCommunity; + + const CreateCommunityDialog({super.key, required this.onCreateCommunity}); + + @override + CreateCommunityDialogState createState() => CreateCommunityDialogState(); +} + +class CreateCommunityDialogState extends State { + String enteredName = ''; // Store the entered community name + + @override + Widget build(BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + backgroundColor: Colors.transparent, // Transparent for shadow effect + child: Stack( + children: [ + // Background container with shadow and rounded corners + Container( + width: 490, + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.25), + blurRadius: 20, + spreadRadius: 5, + offset: const Offset(0, 5), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Community Name', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 16), + // Input field for the community name + TextField( + onChanged: (value) { + enteredName = value; // Capture entered name + }, + style: const TextStyle( + color: Colors.black, + ), + decoration: InputDecoration( + hintText: 'Please enter the community name', + filled: true, + fillColor: const Color(0xFFF5F6F7), + hintStyle: const TextStyle( + fontSize: 14, + color: Color(0xFF999999), + fontWeight: FontWeight.w400, + ), + border: OutlineInputBorder( + borderSide: + const BorderSide(color: Color(0xFFF5F6F7), width: 1), + borderRadius: BorderRadius.circular(10), + ), + enabledBorder: OutlineInputBorder( + borderSide: + const BorderSide(color: Color(0xFFF5F6F7), width: 1), + borderRadius: BorderRadius.circular(10), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: + const BorderSide(color: Color(0xFFF5F6F7), width: 1), + ), + ), + ), + const SizedBox(height: 24), + // Action buttons + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: TextButton( + onPressed: () => Navigator.of(context).pop(), + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + backgroundColor: const Color(0xFFF5F6F7), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: const Text( + 'Cancel', + style: TextStyle(color: Colors.black), + ), + ), + ), + const SizedBox(width: 16), + Expanded( + child: ElevatedButton( + onPressed: () { + if (enteredName.isNotEmpty) { + widget.onCreateCommunity(enteredName); + Navigator.of(context).pop(); + } + }, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + backgroundColor: const Color(0xFF023DFE), + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: const Text('OK'), + ), + ), + ], + ), + ], + ), + ), + ], + ), + ); + } +} From 5d397691b6a860e86f4e5ecbdf752c11c75e267c Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 2 Oct 2024 11:13:25 +0400 Subject: [PATCH 018/122] fixed get communities --- lib/services/space_mana_api.dart | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index 0a96ed2b..bdb369d7 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -11,13 +11,19 @@ class CommunitySpaceManagementApi { try { final response = await HTTPService().get( path: ApiEndpoints.getCommunityList, - showServerMessage: true, expectedResponseModel: (json) { - List jsonData = json; - List communityList = jsonData.map((jsonItem) { - return CommunityModel.fromJson(jsonItem); - }).toList(); - return communityList; + // Access the 'data' key from the response + List jsonData = json['data']; + + // Check if jsonData is actually a List + if (jsonData is List) { + List communityList = jsonData.map((jsonItem) { + return CommunityModel.fromJson(jsonItem); + }).toList(); + return communityList; + } else { + throw Exception('Expected a list but got something else.'); + } }, ); return response; @@ -32,7 +38,6 @@ class CommunitySpaceManagementApi { final response = await HTTPService().get( path: ApiEndpoints.getCommunityById .replaceAll('{communityId}', communityId), - showServerMessage: true, expectedResponseModel: (json) { return CommunityModel.fromJson(json['data']); }, @@ -54,7 +59,6 @@ class CommunitySpaceManagementApi { 'description': description, 'regionId': regionId, }, - showServerMessage: true, expectedResponseModel: (json) { return CommunityModel.fromJson(json['data']); }, @@ -73,7 +77,6 @@ class CommunitySpaceManagementApi { path: ApiEndpoints.updateCommunity .replaceAll('{communityId}', communityId), body: community.toMap(), - showServerMessage: true, expectedResponseModel: (json) { return json['success'] ?? false; }, @@ -90,7 +93,6 @@ class CommunitySpaceManagementApi { final response = await HTTPService().delete( path: ApiEndpoints.deleteCommunity .replaceAll('{communityId}', communityId), - showServerMessage: true, expectedResponseModel: (json) { return json['success'] ?? false; }, @@ -106,7 +108,6 @@ class CommunitySpaceManagementApi { try { final response = await HTTPService().get( path: ApiEndpoints.listSpaces.replaceAll('{communityId}', communityId), - showServerMessage: true, expectedResponseModel: (json) { return SpacesResponse.fromJson(json); }, @@ -127,13 +128,12 @@ class CommunitySpaceManagementApi { } } - Future getSpace(String communityId, String spaceId) async { + Future getSpace(String communityId, String spaceId) async { try { final response = await HTTPService().get( path: ApiEndpoints.getSpace .replaceAll('{communityId}', communityId) .replaceAll('{spaceId}', spaceId), - showServerMessage: true, expectedResponseModel: (json) { return SpaceModel.fromJson(json); }, @@ -141,7 +141,7 @@ class CommunitySpaceManagementApi { return response; } catch (e) { debugPrint('Error fetching space: $e'); - return SpaceModel(); // Assuming an empty SpaceModel constructor + return null; // Assuming an empty SpaceModel constructor } } @@ -159,7 +159,6 @@ class CommunitySpaceManagementApi { final response = await HTTPService().post( path: ApiEndpoints.createSpace.replaceAll('{communityId}', communityId), body: body, - showServerMessage: true, expectedResponseModel: (json) { return SpaceModel.fromJson(json['data']); }, @@ -179,7 +178,6 @@ class CommunitySpaceManagementApi { .replaceAll('{communityId}', communityId) .replaceAll('{spaceId}', spaceId), body: space.toMap(), - showServerMessage: true, expectedResponseModel: (json) { return json['success'] ?? false; }, @@ -197,7 +195,6 @@ class CommunitySpaceManagementApi { path: ApiEndpoints.deleteSpace .replaceAll('{communityId}', communityId) .replaceAll('{spaceId}', spaceId), - showServerMessage: true, expectedResponseModel: (json) { return json['success'] ?? false; }, @@ -214,7 +211,6 @@ class CommunitySpaceManagementApi { final response = await HTTPService().get( path: ApiEndpoints.getSpaceHierarchy .replaceAll('{communityId}', communityId), - showServerMessage: true, expectedResponseModel: (json) { return (json['data'] as List) .map((spaceJson) => SpaceModel.fromJson(spaceJson)) From 308a7adba7c81acd7976b9cdc26b2fe905428528 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 2 Oct 2024 12:03:34 +0400 Subject: [PATCH 019/122] community title will list from the api --- .../model/space_response_model.dart | 27 ---- .../view/community_tile.dart | 8 +- .../view/sidebar_widget.dart | 141 ++++++++++++------ .../view/spaces_management_page.dart | 41 ++++- 4 files changed, 140 insertions(+), 77 deletions(-) diff --git a/lib/pages/spaces_management/model/space_response_model.dart b/lib/pages/spaces_management/model/space_response_model.dart index ab6a8f79..11511481 100644 --- a/lib/pages/spaces_management/model/space_response_model.dart +++ b/lib/pages/spaces_management/model/space_response_model.dart @@ -38,30 +38,3 @@ class SpacesResponse { ); } } - -class CommunitySpaceManagementApi { - Future fetchSpaces(String communityId) async { - try { - final response = await HTTPService().get( - path: ApiEndpoints.listSpaces.replaceAll('{communityId}', communityId), - showServerMessage: true, - expectedResponseModel: (json) { - return SpacesResponse.fromJson(json); - }, - ); - return response; - } catch (e) { - debugPrint('Error fetching spaces: $e'); - return SpacesResponse( - data: [], - message: 'Error fetching spaces', - page: 1, - size: 10, - totalItem: 0, - totalPage: 0, - hasNext: false, - hasPrevious: false, - ); - } - } -} diff --git a/lib/pages/spaces_management/view/community_tile.dart b/lib/pages/spaces_management/view/community_tile.dart index 5530ea13..7511d4f3 100644 --- a/lib/pages/spaces_management/view/community_tile.dart +++ b/lib/pages/spaces_management/view/community_tile.dart @@ -3,14 +3,14 @@ import 'package:syncrow_web/common/custom_expansion_tile.dart'; class CommunityTile extends StatelessWidget { final String title; - final String expandedTile; + final bool isExpanded; final Function(String, bool) onExpansionChanged; final List? children; const CommunityTile({ Key? key, required this.title, - required this.expandedTile, + required this.isExpanded, required this.onExpansionChanged, this.children, }) : super(key: key); @@ -19,11 +19,11 @@ class CommunityTile extends StatelessWidget { Widget build(BuildContext context) { return CustomExpansionTile( title: title, - isExpanded: expandedTile == title, + initiallyExpanded: isExpanded, onExpansionChanged: (bool expanded) { onExpansionChanged(title, expanded); }, - children: children, + children: children ?? [], ); } } diff --git a/lib/pages/spaces_management/view/sidebar_widget.dart b/lib/pages/spaces_management/view/sidebar_widget.dart index 8d775672..7bdcdb7a 100644 --- a/lib/pages/spaces_management/view/sidebar_widget.dart +++ b/lib/pages/spaces_management/view/sidebar_widget.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/common/custom_expansion_tile.dart'; import 'package:syncrow_web/common/search_bar.dart'; -import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/community_tile.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -9,50 +10,77 @@ import 'package:syncrow_web/utils/style.dart'; class SidebarWidget extends StatefulWidget { final Function(String)? onCommunitySelected; + final Map> communitySpaces; - SidebarWidget({this.onCommunitySelected}); + SidebarWidget({this.onCommunitySelected, required this.communitySpaces}); @override _SidebarWidgetState createState() => _SidebarWidgetState(); } class _SidebarWidgetState extends State { - String? _expandedTile; String _searchQuery = ''; // Track search query + Map _expandedTiles = {}; // Track expanded state for each UUID - // List of all communities - final List _communities = [ - Community(name: 'Downtown Dubai'), - Community(name: 'Dubai Creek Harbour'), - Community( - name: 'Dubai Hills Estate', - children: [ - Community(name: 'South Side'), // Nested community - ], - ), - ]; - - // A helper method to handle the expansion logic for CustomExpansionTile - void _handleExpansionChange(String title, bool expanded) { - setState(() { - _expandedTile = expanded ? title : null; - }); - widget.onCommunitySelected?.call(title); + @override + void initState() { + super.initState(); + _logCommunitySpaces(); // Log the structure on initialization } - List _filterCommunities() { - if (_searchQuery.isEmpty) { - return _communities; + // Function to log the communitySpaces data structure for debugging + void _logCommunitySpaces() { + debugPrint('Logging communitySpaces structure:'); + widget.communitySpaces.forEach((communityName, spaces) { + debugPrint('Community: $communityName'); + _logSpaces(spaces, 1); + }); + } + + // Helper function to log the spaces and their children + void _logSpaces(List spaces, int level) { + for (SpaceModel space in spaces) { + debugPrint('${' ' * level}Space: ${space.name}, UUID: ${space.uuid}, Has children: ${space.children.isNotEmpty}'); + if (space.children.isNotEmpty) { + _logSpaces(space.children, level + 1); + } } - return _communities - .where((community) => - community.name.toLowerCase().contains(_searchQuery.toLowerCase())) - .toList(); + } + + // Function to filter communities based on the search query + Map> _filterCommunities() { + if (_searchQuery.isEmpty) { + return widget.communitySpaces; + } + + // Filter communities and their spaces based on the search query + final filteredCommunitySpaces = >{}; + widget.communitySpaces.forEach((communityName, spaces) { + if (communityName.toLowerCase().contains(_searchQuery.toLowerCase()) || + spaces.any((space) => + _containsQuery(space, _searchQuery.toLowerCase()))) { + filteredCommunitySpaces[communityName] = spaces; + } + }); + return filteredCommunitySpaces; + } + + // Helper function to determine if any space or its children match the search query + bool _containsQuery(SpaceModel space, String query) { + if (space.name.toLowerCase().contains(query)) { + return true; + } + for (var child in space.children) { + if (_containsQuery(child, query)) { + return true; + } + } + return false; } @override Widget build(BuildContext context) { - List filteredCommunities = _filterCommunities(); + final filteredCommunities = _filterCommunities(); return Container( width: 300, @@ -105,12 +133,10 @@ class _SidebarWidgetState extends State { const SizedBox(height: 16), // Community list with one item expanded at a time Expanded( - child: ListView.builder( - itemCount: filteredCommunities.length, - itemBuilder: (context, index) { - Community community = filteredCommunities[index]; - return _buildCommunityTile(community); - }, + child: ListView( + children: filteredCommunities.keys.map((communityName) { + return _buildCommunityTile(communityName, filteredCommunities[communityName]!); + }).toList(), ), ), ], @@ -118,18 +144,43 @@ class _SidebarWidgetState extends State { ); } - Widget _buildCommunityTile(Community community) { - bool hasChildren = - community.children != null && community.children!.isNotEmpty; + Widget _buildCommunityTile(String communityName, List spaces) { + bool hasChildren = spaces.isNotEmpty; + + debugPrint('Building CommunityTile for $communityName, hasChildren: $hasChildren'); return CommunityTile( - title: community.name, - expandedTile: _expandedTile ?? '', - onExpansionChanged: _handleExpansionChange, + title: communityName, + isExpanded: _expandedTiles[communityName] ?? false, + onExpansionChanged: (String title, bool expanded) { + debugPrint('CommunityTile onExpansionChanged called for $title, expanded: $expanded'); + _handleExpansionChange(title, expanded); + }, children: hasChildren - ? community.children! - .map((child) => _buildCommunityTile(child)) - .toList() - : null, // Recursively render sub-communities + ? spaces.map((space) => _buildSpaceTile(space)).toList() + : null, // Render spaces within the community ); } + + Widget _buildSpaceTile(SpaceModel space) { + debugPrint('Building SpaceTile for ${space.name}, hasChildren: ${space.children.isNotEmpty}'); + return CustomExpansionTile( + title: space.name, + isExpanded: _expandedTiles[space.uuid] ?? false, + onExpansionChanged: (bool expanded) { + debugPrint('SpaceTile onExpansionChanged called for ${space.name}, expanded: $expanded'); + _handleExpansionChange(space.uuid, expanded); + }, + children: space.children.isNotEmpty + ? space.children.map((childSpace) => _buildSpaceTile(childSpace)).toList() + : [], // Recursively render child spaces if available + ); + } + + void _handleExpansionChange(String uuid, bool expanded) { + setState(() { + _expandedTiles[uuid] = expanded; + debugPrint('_expandedTiles updated: $_expandedTiles'); + }); + widget.onCommunitySelected?.call(uuid); + } } diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 3f646d05..420eab36 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -4,12 +4,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/common/custom_expansion_tile.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; +import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/view/sidebar_widget.dart'; +import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; -class SpaceManagementPage extends StatefulWidget{ +class SpaceManagementPage extends StatefulWidget { @override SpaceManagementPageState createState() => SpaceManagementPageState(); } @@ -19,6 +22,41 @@ class SpaceManagementPageState extends State { List spaces = []; List connections = []; + // API instance + final CommunitySpaceManagementApi _api = CommunitySpaceManagementApi(); + + // Data structure to store community and associated spaces + Map> communitySpaces = {}; + + @override + void initState() { + super.initState(); + _loadCommunityAndSpacesData(); + } + + // Function to load all communities and their respective spaces + void _loadCommunityAndSpacesData() async { + try { + // Fetch all communities + List communities = await _api.fetchCommunities(); + + for (CommunityModel community in communities) { + // Fetch spaces hierarchy for each community + List spaces = await _api.getSpaceHierarchy(community.uuid); + + // Store the result in the communitySpaces map + communitySpaces[community.name] = spaces; + } + + // Update the state to reflect loaded data + setState(() { + // Optionally, you can initialize something here based on the loaded data + }); + } catch (e) { + debugPrint('Error loading communities and spaces: $e'); + } + } + void _handleCommunitySelection(String community) { // Handle community selection here print("Selected Community: $community"); @@ -40,6 +78,7 @@ class SpaceManagementPageState extends State { children: [ // Sidebar for navigation and community list SidebarWidget( + communitySpaces: communitySpaces, // Pass communitySpaces here onCommunitySelected: _handleCommunitySelection, ), // Right Side: Community Structure Area From 559989cdec88ce6be1d375470159117b6aa42904 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 2 Oct 2024 12:12:14 +0400 Subject: [PATCH 020/122] Capitalized first letter --- lib/common/custom_expansion_tile.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/common/custom_expansion_tile.dart b/lib/common/custom_expansion_tile.dart index 5876ae31..bf39991e 100644 --- a/lib/common/custom_expansion_tile.dart +++ b/lib/common/custom_expansion_tile.dart @@ -40,6 +40,11 @@ class CustomExpansionTileState extends State { } } + String _capitalizeFirstLetter(String text) { + if (text.isEmpty) return text; + return text[0].toUpperCase() + text.substring(1); + } + @override Widget build(BuildContext context) { return Column( @@ -84,7 +89,7 @@ class CustomExpansionTileState extends State { // Title Text Expanded( child: Text( - widget.title, + _capitalizeFirstLetter(widget.title), style: TextStyle( color: _isExpanded ? ColorsManager.blackColor From 0d2d343bf8592c5f82b379453231d3019565d3c8 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 2 Oct 2024 12:19:49 +0400 Subject: [PATCH 021/122] added the tap gesture for creating community --- .../view/sidebar_widget.dart | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/pages/spaces_management/view/sidebar_widget.dart b/lib/pages/spaces_management/view/sidebar_widget.dart index 7bdcdb7a..2056c148 100644 --- a/lib/pages/spaces_management/view/sidebar_widget.dart +++ b/lib/pages/spaces_management/view/sidebar_widget.dart @@ -4,6 +4,7 @@ import 'package:syncrow_web/common/custom_expansion_tile.dart'; import 'package:syncrow_web/common/search_bar.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/community_tile.dart'; +import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_community_dialog.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/style.dart'; @@ -40,13 +41,29 @@ class _SidebarWidgetState extends State { // Helper function to log the spaces and their children void _logSpaces(List spaces, int level) { for (SpaceModel space in spaces) { - debugPrint('${' ' * level}Space: ${space.name}, UUID: ${space.uuid}, Has children: ${space.children.isNotEmpty}'); + debugPrint( + '${' ' * level}Space: ${space.name}, UUID: ${space.uuid}, Has children: ${space.children.isNotEmpty}'); if (space.children.isNotEmpty) { _logSpaces(space.children, level + 1); } } } + void _showCreateCommunityDialog() { + showDialog( + context: context, + builder: (context) => CreateCommunityDialog( + onCreateCommunity: (String communityName) { + setState(() { + debugPrint("hello"); + + // You can update the communitySpaces map here + }); + }, + ), + ); + } + // Function to filter communities based on the search query Map> _filterCommunities() { if (_searchQuery.isEmpty) { @@ -57,8 +74,8 @@ class _SidebarWidgetState extends State { final filteredCommunitySpaces = >{}; widget.communitySpaces.forEach((communityName, spaces) { if (communityName.toLowerCase().contains(_searchQuery.toLowerCase()) || - spaces.any((space) => - _containsQuery(space, _searchQuery.toLowerCase()))) { + spaces.any( + (space) => _containsQuery(space, _searchQuery.toLowerCase()))) { filteredCommunitySpaces[communityName] = spaces; } }); @@ -101,7 +118,7 @@ class _SidebarWidgetState extends State { ), GestureDetector( onTap: () { - // Handle add button action + _showCreateCommunityDialog(); }, child: Container( width: 30, @@ -135,7 +152,8 @@ class _SidebarWidgetState extends State { Expanded( child: ListView( children: filteredCommunities.keys.map((communityName) { - return _buildCommunityTile(communityName, filteredCommunities[communityName]!); + return _buildCommunityTile( + communityName, filteredCommunities[communityName]!); }).toList(), ), ), @@ -147,12 +165,14 @@ class _SidebarWidgetState extends State { Widget _buildCommunityTile(String communityName, List spaces) { bool hasChildren = spaces.isNotEmpty; - debugPrint('Building CommunityTile for $communityName, hasChildren: $hasChildren'); + debugPrint( + 'Building CommunityTile for $communityName, hasChildren: $hasChildren'); return CommunityTile( title: communityName, isExpanded: _expandedTiles[communityName] ?? false, onExpansionChanged: (String title, bool expanded) { - debugPrint('CommunityTile onExpansionChanged called for $title, expanded: $expanded'); + debugPrint( + 'CommunityTile onExpansionChanged called for $title, expanded: $expanded'); _handleExpansionChange(title, expanded); }, children: hasChildren @@ -162,16 +182,20 @@ class _SidebarWidgetState extends State { } Widget _buildSpaceTile(SpaceModel space) { - debugPrint('Building SpaceTile for ${space.name}, hasChildren: ${space.children.isNotEmpty}'); + debugPrint( + 'Building SpaceTile for ${space.name}, hasChildren: ${space.children.isNotEmpty}'); return CustomExpansionTile( title: space.name, isExpanded: _expandedTiles[space.uuid] ?? false, onExpansionChanged: (bool expanded) { - debugPrint('SpaceTile onExpansionChanged called for ${space.name}, expanded: $expanded'); + debugPrint( + 'SpaceTile onExpansionChanged called for ${space.name}, expanded: $expanded'); _handleExpansionChange(space.uuid, expanded); }, children: space.children.isNotEmpty - ? space.children.map((childSpace) => _buildSpaceTile(childSpace)).toList() + ? space.children + .map((childSpace) => _buildSpaceTile(childSpace)) + .toList() : [], // Recursively render child spaces if available ); } From 4db5f4ddc14af9d0b4e694f322191f9332603ed7 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 2 Oct 2024 12:35:17 +0400 Subject: [PATCH 022/122] added community --- .../view/sidebar_widget.dart | 27 +++++++++++++++---- .../view/spaces_management_page.dart | 3 +-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/pages/spaces_management/view/sidebar_widget.dart b/lib/pages/spaces_management/view/sidebar_widget.dart index 2056c148..975f53c8 100644 --- a/lib/pages/spaces_management/view/sidebar_widget.dart +++ b/lib/pages/spaces_management/view/sidebar_widget.dart @@ -5,6 +5,7 @@ import 'package:syncrow_web/common/search_bar.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/community_tile.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_community_dialog.dart'; +import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/style.dart'; @@ -22,6 +23,7 @@ class SidebarWidget extends StatefulWidget { class _SidebarWidgetState extends State { String _searchQuery = ''; // Track search query Map _expandedTiles = {}; // Track expanded state for each UUID + final CommunitySpaceManagementApi _api = CommunitySpaceManagementApi(); @override void initState() { @@ -53,12 +55,27 @@ class _SidebarWidgetState extends State { showDialog( context: context, builder: (context) => CreateCommunityDialog( - onCreateCommunity: (String communityName) { - setState(() { - debugPrint("hello"); + onCreateCommunity: (String communityName) async { + try { + // Assuming regionId and description are provided statically or via dialog in future. + String regionId = "42dc377a-1a39-4df9-b85a-b3817af88525"; + String description = ""; - // You can update the communitySpaces map here - }); + final newCommunity = await _api.createCommunity( + communityName, + description, + regionId, + ); + + if (newCommunity != null) { + setState(() { + // Add the newly created community to the communitySpaces map + widget.communitySpaces[newCommunity.name] = []; + }); + } + } catch (e) { + debugPrint('Error creating community: $e'); + } }, ), ); diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 420eab36..31d24768 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -2,7 +2,6 @@ import 'dart:ui_web'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_web/common/custom_expansion_tile.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; @@ -10,7 +9,6 @@ import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_di import 'package:syncrow_web/pages/spaces_management/view/sidebar_widget.dart'; import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/constants/assets.dart'; class SpaceManagementPage extends StatefulWidget { @override @@ -361,6 +359,7 @@ class SpaceManagementPageState extends State { ), ); } + } // Model for storing space information From 3b5de591313dd0d7bfed2cbeb763f398c8638bcd Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 8 Oct 2024 10:18:47 +0400 Subject: [PATCH 023/122] Revert "added community" This reverts commit 4db5f4ddc14af9d0b4e694f322191f9332603ed7. --- .../view/sidebar_widget.dart | 27 ++++--------------- .../view/spaces_management_page.dart | 3 ++- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/lib/pages/spaces_management/view/sidebar_widget.dart b/lib/pages/spaces_management/view/sidebar_widget.dart index 975f53c8..2056c148 100644 --- a/lib/pages/spaces_management/view/sidebar_widget.dart +++ b/lib/pages/spaces_management/view/sidebar_widget.dart @@ -5,7 +5,6 @@ import 'package:syncrow_web/common/search_bar.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/community_tile.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_community_dialog.dart'; -import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/style.dart'; @@ -23,7 +22,6 @@ class SidebarWidget extends StatefulWidget { class _SidebarWidgetState extends State { String _searchQuery = ''; // Track search query Map _expandedTiles = {}; // Track expanded state for each UUID - final CommunitySpaceManagementApi _api = CommunitySpaceManagementApi(); @override void initState() { @@ -55,27 +53,12 @@ class _SidebarWidgetState extends State { showDialog( context: context, builder: (context) => CreateCommunityDialog( - onCreateCommunity: (String communityName) async { - try { - // Assuming regionId and description are provided statically or via dialog in future. - String regionId = "42dc377a-1a39-4df9-b85a-b3817af88525"; - String description = ""; + onCreateCommunity: (String communityName) { + setState(() { + debugPrint("hello"); - final newCommunity = await _api.createCommunity( - communityName, - description, - regionId, - ); - - if (newCommunity != null) { - setState(() { - // Add the newly created community to the communitySpaces map - widget.communitySpaces[newCommunity.name] = []; - }); - } - } catch (e) { - debugPrint('Error creating community: $e'); - } + // You can update the communitySpaces map here + }); }, ), ); diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 31d24768..420eab36 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -2,6 +2,7 @@ import 'dart:ui_web'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/common/custom_expansion_tile.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; @@ -9,6 +10,7 @@ import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_di import 'package:syncrow_web/pages/spaces_management/view/sidebar_widget.dart'; import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; class SpaceManagementPage extends StatefulWidget { @override @@ -359,7 +361,6 @@ class SpaceManagementPageState extends State { ), ); } - } // Model for storing space information From fc7ccb8ad8133c8eec202f309e23dea0e007da84 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 8 Oct 2024 10:33:35 +0400 Subject: [PATCH 024/122] fixed api routes --- lib/utils/app_routes.dart | 2 +- lib/utils/constants/api_const.dart | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/utils/app_routes.dart b/lib/utils/app_routes.dart index b14da393..20a89e21 100644 --- a/lib/utils/app_routes.dart +++ b/lib/utils/app_routes.dart @@ -12,7 +12,7 @@ class AppRoutes { return [ GoRoute( path: RoutesConst.auth, - builder: (context, state) => const HomePage(), + builder: (context, state) => const LoginPage(), ), GoRoute( path: RoutesConst.home, diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 487aeaa3..a1bdd843 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -1,6 +1,6 @@ abstract class ApiEndpoints { - static const String baseUrl = 'https://syncrow-dev.azurewebsites.net'; - static const String baseLocalUrl = 'http://localhost:4001'; //Localhost + static const String baseUrl = 'http://localhost:4001'; + //static const String baseUrl = 'http://localhost:4001'; //Localhost //https://syncrow-staging.azurewebsites.net ////////////////////////////////////// Authentication /////////////////////////////// @@ -42,28 +42,28 @@ abstract class ApiEndpoints { // Space Module static const String createSpace = - '$baseLocalUrl/communities/{communityId}/spaces'; + '$baseUrl/communities/{communityId}/spaces'; static const String listSpaces = - '$baseLocalUrl/communities/{communityId}/spaces'; + '$baseUrl/communities/{communityId}/spaces'; static const String deleteSpace = - '$baseLocalUrl/communities/{communityId}/spaces/{spaceId}'; + '$baseUrl/communities/{communityId}/spaces/{spaceId}'; static const String updateSpace = - '$baseLocalUrl/communities/{communityId}/spaces/{spaceId}'; + '$baseUrl/communities/{communityId}/spaces/{spaceId}'; static const String getSpace = - '$baseLocalUrl/communities/{communityId}/spaces/{spaceId}'; + '$baseUrl/communities/{communityId}/spaces/{spaceId}'; static const String getSpaceHierarchy = - '$baseLocalUrl/communities/{communityId}/spaces/hierarchy'; + '$baseUrl/communities/{communityId}/spaces/hierarchy'; // Community Module - static const String createCommunity = '$baseLocalUrl/communities'; - static const String getCommunityList = '$baseLocalUrl/communities'; + static const String createCommunity = '$baseUrl/communities'; + static const String getCommunityList = '$baseUrl/communities'; static const String getCommunityById = - '$baseLocalUrl/communities/{communityId}'; + '$baseUrl/communities/{communityId}'; static const String updateCommunity = - '$baseLocalUrl/communities/{communityId}'; + '$baseUrl/communities/{communityId}'; static const String deleteCommunity = - '$baseLocalUrl/communities/{communityId}'; + '$baseUrl/communities/{communityId}'; static const String getUserCommunities = - '$baseLocalUrl/communities/user/{userUuid}'; - static const String createUserCommunity = '$baseLocalUrl/communities/user'; + '$baseUrl/communities/user/{userUuid}'; + static const String createUserCommunity = '$baseUrl/communities/user'; } From cdd22f8d761251ec79defd3f97fac30546f84c4b Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 8 Oct 2024 10:41:40 +0400 Subject: [PATCH 025/122] added colors --- .../view/spaces_management_page.dart | 28 ++++++++----------- lib/utils/color_manager.dart | 5 ++++ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 420eab36..841e3302 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -1,8 +1,5 @@ -import 'dart:ui_web'; - import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_web/common/custom_expansion_tile.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; @@ -10,7 +7,6 @@ import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_di import 'package:syncrow_web/pages/spaces_management/view/sidebar_widget.dart'; import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/constants/assets.dart'; class SpaceManagementPage extends StatefulWidget { @override @@ -88,7 +84,7 @@ class SpaceManagementPageState extends State { color: ColorsManager.whiteColors, border: Border( left: BorderSide( - color: Colors.white, + color: ColorsManager.whiteColors, width: 1.0), // Light left border to match ), ), @@ -104,8 +100,8 @@ class SpaceManagementPageState extends State { color: ColorsManager.whiteColors, boxShadow: [ BoxShadow( - color: Colors.black - .withOpacity(0.2), // Subtle shadow + color: ColorsManager + .shadowBlackColor, // Subtle shadow spreadRadius: 0, // No spread blurRadius: 8, // Softer shadow edges offset: Offset(0, 4), // Shadow only on the bottom @@ -132,7 +128,7 @@ class SpaceManagementPageState extends State { child: Container( width: 2000, // Large canvas height: 2000, // Large canvas - color: Colors.transparent, // Transparent background + color: ColorsManager.transparentColor, // Transparent background child: Stack( clipBehavior: Clip.none, children: [ @@ -180,8 +176,9 @@ class SpaceManagementPageState extends State { begin: Alignment.centerLeft, end: Alignment.centerRight, colors: [ - Color(0x3F000000).withOpacity(0.1), // Light gray - Colors.transparent, // Transparent fade + ColorsManager.semiTransparentBlackColor + .withOpacity(0.1), // Light gray + ColorsManager.transparentColor, // Transparent fade ], ), ), @@ -278,7 +275,7 @@ class SpaceManagementPageState extends State { width: 150, height: 60, decoration: BoxDecoration( - color: Colors.white, + color: ColorsManager.whiteColors, borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( @@ -295,7 +292,7 @@ class SpaceManagementPageState extends State { width: 40, height: 60, decoration: const BoxDecoration( - color: Color(0xFF023DFE), + color: ColorsManager.secondaryColor, borderRadius: BorderRadius.only( topLeft: Radius.circular(15), bottomLeft: Radius.circular(15), @@ -306,7 +303,6 @@ class SpaceManagementPageState extends State { spaces[index].icon, width: 24, height: 24, - color: Colors.white, ), ), ), @@ -353,7 +349,7 @@ class SpaceManagementPageState extends State { width: 30, height: 30, decoration: const BoxDecoration( - color: Color(0xFF023DFE), + color: ColorsManager.secondaryColor, shape: BoxShape.circle, ), child: const Icon(Icons.add, color: Colors.white, size: 20), @@ -400,7 +396,7 @@ class CurvedLinePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint() - ..color = Colors.black + ..color = ColorsManager.blackColor ..strokeWidth = 2 ..style = PaintingStyle.stroke; @@ -437,7 +433,7 @@ class CurvedLinePainter extends CustomPainter { } // Draw small connection dots at the start and end points - final dotPaint = Paint()..color = Colors.black; + final dotPaint = Paint()..color = ColorsManager.blackColor; canvas.drawCircle(start, 5, dotPaint); // Start dot canvas.drawCircle(end, 5, dotPaint); // End dot } diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index 8f42e606..6e54ba0e 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -10,6 +10,8 @@ abstract class ColorsManager { static const Color whiteColors = Colors.white; static const Color secondaryColor = Color(0xFF023DFE); static const Color onSecondaryColor = Color(0xFF023DFE); + static Color shadowBlackColor = Colors.black.withOpacity(0.2); + static Color dialogBlueTitle = Color(0xFF023DFE).withOpacity(0.6); @@ -46,5 +48,8 @@ abstract class ColorsManager { static const Color textGreen = Color(0xFF008905); static const Color yaGreen = Color(0xFFFFBF44); static const Color checkBoxFillColor = Color(0xFFF3F3F3); + static const Color semiTransparentBlackColor = Color(0x3F000000); + static const Color transparentColor = Color(0x00000000) + } //0036E6 \ No newline at end of file From 081d6fd65f444c8fd21908a7929f6f120c7f603d Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 8 Oct 2024 11:39:10 +0400 Subject: [PATCH 026/122] added position elements to space --- .../model/community_model.dart | 11 + .../spaces_management/model/space_model.dart | 17 +- .../view/curved_line_painter.dart | 62 +++++ .../view/plus_button_widget.dart | 55 ++++ .../view/space_card_widget.dart | 79 ++++++ .../view/space_container_widget.dart | 67 +++++ .../view/spaces_management_page.dart | 247 ++++-------------- lib/utils/color_manager.dart | 2 +- 8 files changed, 342 insertions(+), 198 deletions(-) create mode 100644 lib/pages/spaces_management/view/curved_line_painter.dart create mode 100644 lib/pages/spaces_management/view/plus_button_widget.dart create mode 100644 lib/pages/spaces_management/view/space_card_widget.dart create mode 100644 lib/pages/spaces_management/view/space_container_widget.dart diff --git a/lib/pages/spaces_management/model/community_model.dart b/lib/pages/spaces_management/model/community_model.dart index a37dcbd5..182aee73 100644 --- a/lib/pages/spaces_management/model/community_model.dart +++ b/lib/pages/spaces_management/model/community_model.dart @@ -1,4 +1,5 @@ import 'package:syncrow_web/pages/auth/model/region_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; class CommunityModel { final String uuid; @@ -7,6 +8,7 @@ class CommunityModel { final String name; final String description; final RegionModel? region; + List spaces; CommunityModel({ required this.uuid, @@ -14,6 +16,7 @@ class CommunityModel { required this.updatedAt, required this.name, required this.description, + required this.spaces, this.region, }); @@ -26,6 +29,11 @@ class CommunityModel { description: json['description'], region: json['region'] != null ? RegionModel.fromJson(json['region']) : null, + spaces: json['spaces'] != null + ? (json['spaces'] as List) + .map((space) => SpaceModel.fromJson(space)) + .toList() + : [], ); } @@ -37,6 +45,9 @@ class CommunityModel { 'name': name, 'description': description, 'region': region?.toJson(), + 'spaces': spaces + .map((space) => space.toMap()) + .toList(), // Convert spaces to Map }; } } diff --git a/lib/pages/spaces_management/model/space_model.dart b/lib/pages/spaces_management/model/space_model.dart index 82a94d16..4ba54545 100644 --- a/lib/pages/spaces_management/model/space_model.dart +++ b/lib/pages/spaces_management/model/space_model.dart @@ -1,3 +1,4 @@ +import 'dart:ui'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; class SpaceModel { @@ -11,7 +12,10 @@ class SpaceModel { final bool isParent; final SpaceModel? parent; final CommunityModel? community; - final List children; // List of child spaces + final List children; + final String icon; + Offset position; + bool isHovered; SpaceModel({ required this.uuid, @@ -25,6 +29,9 @@ class SpaceModel { this.parent, this.community, required this.children, + required this.icon, + required this.position, + this.isHovered = false, }); factory SpaceModel.fromJson(Map json) { @@ -47,6 +54,11 @@ class SpaceModel { .map((child) => SpaceModel.fromJson(child)) .toList() : [], + icon: json['icon'], // New field from JSON + position: json['position'] != null + ? Offset(json['position']['dx'], json['position']['dy']) + : Offset(0, 0), // Default position if not provided in JSON + isHovered: json['isHovered'] ?? false, // Default isHovered if not in JSON ); } @@ -63,6 +75,9 @@ class SpaceModel { 'parent': parent?.toMap(), 'community': community?.toMap(), 'children': children.map((child) => child.toMap()).toList(), + 'icon': icon, + 'position': {'dx': position.dx, 'dy': position.dy}, + 'isHovered': isHovered, }; } } diff --git a/lib/pages/spaces_management/view/curved_line_painter.dart b/lib/pages/spaces_management/view/curved_line_painter.dart new file mode 100644 index 00000000..2d6c523a --- /dev/null +++ b/lib/pages/spaces_management/view/curved_line_painter.dart @@ -0,0 +1,62 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/spaces_management/view/spaces_management_page.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class CurvedLinePainter extends CustomPainter { + final List connections; + + CurvedLinePainter(this.connections); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = ColorsManager.blackColor + ..strokeWidth = 2 + ..style = PaintingStyle.stroke; + + // Ensure connections exist before painting + if (connections.isEmpty) { + return; // Nothing to paint if there are no connections + } + + for (var connection in connections) { + // Ensure positions are valid before drawing lines + if (connection.startSpace.position == null || + connection.endSpace.position == null) { + continue; + } + + Offset start = connection.startSpace.position + + Offset(75, 60); // Center bottom of start space + Offset end = connection.endSpace.position + + Offset(75, 0); // Center top of end space + + if (connection.direction == 'down') { + // Curved line for down connections + final controlPoint = Offset((start.dx + end.dx) / 2, start.dy + 50); + final path = Path() + ..moveTo(start.dx, start.dy) + ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy); + canvas.drawPath(path, paint); + } else if (connection.direction == 'right') { + // Straight line for right connections + canvas.drawLine(start, end, paint); + } else if (connection.direction == 'left') { + // Straight line for left connections + canvas.drawLine(start, end, paint); + } + + // Draw small connection dots at the start and end points + final dotPaint = Paint()..color = ColorsManager.blackColor; + canvas.drawCircle(start, 5, dotPaint); // Start dot + canvas.drawCircle(end, 5, dotPaint); // End dot + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return true; + } +} diff --git a/lib/pages/spaces_management/view/plus_button_widget.dart b/lib/pages/spaces_management/view/plus_button_widget.dart new file mode 100644 index 00000000..ee2faad6 --- /dev/null +++ b/lib/pages/spaces_management/view/plus_button_widget.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class PlusButtonWidget extends StatelessWidget { + final int index; + final String direction; + final Offset offset; + final Size screenSize; + final Function(int index, Offset newPosition, String direction) onButtonTap; + + const PlusButtonWidget({ + Key? key, + required this.index, + required this.direction, + required this.offset, + required this.screenSize, + required this.onButtonTap, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Positioned( + left: offset.dx, + top: offset.dy, + child: GestureDetector( + onTap: () { + Offset newPosition; + switch (direction) { + case 'left': + newPosition = Offset(-200, 0); + break; + case 'right': + newPosition = Offset(200, 0); + break; + case 'down': + newPosition = Offset(0, 150); + break; + default: + newPosition = Offset.zero; + } + onButtonTap(index, newPosition, direction); + }, + child: Container( + width: 30, + height: 30, + decoration: const BoxDecoration( + color: ColorsManager.secondaryColor, + shape: BoxShape.circle, + ), + child: const Icon(Icons.add, color: Colors.white, size: 20), + ), + ), + ); + } +} diff --git a/lib/pages/spaces_management/view/space_card_widget.dart b/lib/pages/spaces_management/view/space_card_widget.dart new file mode 100644 index 00000000..ff9909c1 --- /dev/null +++ b/lib/pages/spaces_management/view/space_card_widget.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; + +import 'plus_button_widget.dart'; // Make sure to import your PlusButtonWidget + +class SpaceCardWidget extends StatelessWidget { + final int index; + final Size screenSize; + final Offset position; + final bool isHovered; + final Function(int index, Offset delta) onPanUpdate; + final Function(int index, bool isHovered) onHoverChanged; + final Function(int index, Offset newPosition, String direction) onButtonTap; + final Widget Function(int index) buildSpaceContainer; + + const SpaceCardWidget({ + Key? key, + required this.index, + required this.screenSize, + required this.position, + required this.isHovered, + required this.onPanUpdate, + required this.onHoverChanged, + required this.onButtonTap, + required this.buildSpaceContainer, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Positioned( + left: position.dx, + top: position.dy, + child: GestureDetector( + onPanUpdate: (details) { + // Call the provided callback to update the position + onPanUpdate(index, details.delta); + }, + child: MouseRegion( + onEnter: (_) { + // Call the provided callback to handle hover state + onHoverChanged(index, true); + }, + onExit: (_) { + // Call the provided callback to handle hover state + onHoverChanged(index, false); + }, + child: Stack( + clipBehavior: Clip.none, + children: [ + buildSpaceContainer(index), // Build the space container + if (isHovered) ...[ + PlusButtonWidget( + index: index, + direction: 'left', + offset: const Offset(-21, 20), + screenSize: screenSize, + onButtonTap: onButtonTap, + ), + PlusButtonWidget( + index: index, + direction: 'right', + offset: const Offset(140, 20), + screenSize: screenSize, + onButtonTap: onButtonTap, + ), + PlusButtonWidget( + index: index, + direction: 'down', + offset: const Offset(63, 50), + screenSize: screenSize, + onButtonTap: onButtonTap, + ), + ], + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/spaces_management/view/space_container_widget.dart b/lib/pages/spaces_management/view/space_container_widget.dart new file mode 100644 index 00000000..d7a6ee14 --- /dev/null +++ b/lib/pages/spaces_management/view/space_container_widget.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + + +class SpaceContainerWidget extends StatelessWidget { + final int index; + final String icon; + final String name; + + const SpaceContainerWidget({ + Key? key, + required this.index, + required this.icon, + required this.name, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: 150, + height: 60, + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: const Offset(0, 3), // shadow position + ), + ], + ), + child: Row( + children: [ + Container( + width: 40, + height: 60, + decoration: const BoxDecoration( + color: ColorsManager.secondaryColor, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(15), + bottomLeft: Radius.circular(15), + ), + ), + child: Center( + child: SvgPicture.asset( + icon, + width: 24, + height: 24, + ), + ), + ), + const SizedBox(width: 10), + Text( + name, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 841e3302..026e9d50 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; +import 'package:syncrow_web/pages/spaces_management/view/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/view/sidebar_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/view/space_card_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/view/space_container_widget.dart'; import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -41,7 +43,7 @@ class SpaceManagementPageState extends State { List spaces = await _api.getSpaceHierarchy(community.uuid); // Store the result in the communitySpaces map - communitySpaces[community.name] = spaces; + community.spaces = spaces; } // Update the state to reflect loaded data @@ -128,7 +130,8 @@ class SpaceManagementPageState extends State { child: Container( width: 2000, // Large canvas height: 2000, // Large canvas - color: ColorsManager.transparentColor, // Transparent background + color: ColorsManager + .transparentColor, // Transparent background child: Stack( clipBehavior: Clip.none, children: [ @@ -149,8 +152,51 @@ class SpaceManagementPageState extends State { children: spaces .asMap() .entries - .map((entry) => _buildSpaceCard( - entry.key, screenSize)) + .map((entry) => SpaceCardWidget( + index: entry.key, + screenSize: screenSize, + position: spaces[entry.key] + .position, + isHovered: spaces[entry.key] + .isHovered, + onPanUpdate: (int index, + Offset delta) { + setState(() { + spaces[index] + .position += delta; + }); + }, + onHoverChanged: (int index, + bool isHovered) { + setState(() { + spaces[index] + .isHovered = + isHovered; + }); + }, + onButtonTap: (int index, + Offset newPosition, + String direction) { + _showCreateSpaceDialog( + screenSize, + position: spaces[index] + .position + + newPosition, + parentIndex: index, + direction: direction, + ); + }, + buildSpaceContainer: + (int index) { + return SpaceContainerWidget( + index: index, + icon: + spaces[index].icon, + name: + spaces[index].name, + ); + }, + )) .toList(), ), ), @@ -224,139 +270,6 @@ class SpaceManagementPageState extends State { }, ); } - - // Function to build a draggable space card - Widget _buildSpaceCard(int index, Size screenSize) { - return Positioned( - left: spaces[index].position.dx, - top: spaces[index].position.dy, - child: GestureDetector( - onPanUpdate: (details) { - // Update the position of the space card while dragging - setState(() { - spaces[index].position += details.delta; - }); - }, - child: MouseRegion( - onEnter: (_) { - // Show plus buttons on hover - setState(() { - spaces[index].isHovered = true; - }); - }, - onExit: (_) { - // Hide plus buttons when not hovered - setState(() { - spaces[index].isHovered = false; - }); - }, - child: Stack( - clipBehavior: Clip.none, - children: [ - _buildSpaceContainer(index), - if (spaces[index].isHovered) ...[ - _buildPlusButton( - index, 'left', const Offset(-21, 20), screenSize), - _buildPlusButton( - index, 'right', const Offset(140, 20), screenSize), - _buildPlusButton( - index, 'down', const Offset(63, 50), screenSize), - ], - ], - ), - ), - ), - ); - } - - // Function to build the space container with the styled format - Widget _buildSpaceContainer(int index) { - return Container( - width: 150, - height: 60, - decoration: BoxDecoration( - color: ColorsManager.whiteColors, - borderRadius: BorderRadius.circular(15), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: const Offset(0, 3), - ), - ], - ), - child: Row( - children: [ - Container( - width: 40, - height: 60, - decoration: const BoxDecoration( - color: ColorsManager.secondaryColor, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(15), - bottomLeft: Radius.circular(15), - ), - ), - child: Center( - child: SvgPicture.asset( - spaces[index].icon, - width: 24, - height: 24, - ), - ), - ), - const SizedBox(width: 10), - Text( - spaces[index].name, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - ], - ), - ); - } - - // Function to build plus buttons for new space creation - Widget _buildPlusButton( - int index, String direction, Offset offset, Size screenSize) { - return Positioned( - left: offset.dx, - top: offset.dy, - child: GestureDetector( - onTap: () { - Offset newPosition; - switch (direction) { - case 'left': - newPosition = spaces[index].position + const Offset(-200, 0); - break; - case 'right': - newPosition = spaces[index].position + const Offset(200, 0); - break; - case 'down': - newPosition = spaces[index].position + const Offset(0, 150); - break; - default: - newPosition = spaces[index].position; - } - // Open the dialog to create a new space and pass down the new position - _showCreateSpaceDialog(screenSize, - position: newPosition, parentIndex: index, direction: direction); - }, - child: Container( - width: 30, - height: 30, - decoration: const BoxDecoration( - color: ColorsManager.secondaryColor, - shape: BoxShape.circle, - ), - child: const Icon(Icons.add, color: Colors.white, size: 20), - ), - ), - ); - } } // Model for storing space information @@ -386,61 +299,3 @@ class Connection { required this.direction}); } -// Custom painter to draw lines between connected spaces - -class CurvedLinePainter extends CustomPainter { - final List connections; - - CurvedLinePainter(this.connections); - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = ColorsManager.blackColor - ..strokeWidth = 2 - ..style = PaintingStyle.stroke; - - // Ensure connections exist before painting - if (connections.isEmpty) { - return; // Nothing to paint if there are no connections - } - - for (var connection in connections) { - // Ensure positions are valid before drawing lines - if (connection.startSpace.position == null || - connection.endSpace.position == null) { - continue; - } - - Offset start = connection.startSpace.position + - Offset(75, 60); // Center bottom of start space - Offset end = connection.endSpace.position + - Offset(75, 0); // Center top of end space - - if (connection.direction == 'down') { - // Curved line for down connections - final controlPoint = Offset((start.dx + end.dx) / 2, start.dy + 50); - final path = Path() - ..moveTo(start.dx, start.dy) - ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy); - canvas.drawPath(path, paint); - } else if (connection.direction == 'right') { - // Straight line for right connections - canvas.drawLine(start, end, paint); - } else if (connection.direction == 'left') { - // Straight line for left connections - canvas.drawLine(start, end, paint); - } - - // Draw small connection dots at the start and end points - final dotPaint = Paint()..color = ColorsManager.blackColor; - canvas.drawCircle(start, 5, dotPaint); // Start dot - canvas.drawCircle(end, 5, dotPaint); // End dot - } - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return true; - } -} diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index 6e54ba0e..e6dc4690 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -49,7 +49,7 @@ abstract class ColorsManager { static const Color yaGreen = Color(0xFFFFBF44); static const Color checkBoxFillColor = Color(0xFFF3F3F3); static const Color semiTransparentBlackColor = Color(0x3F000000); - static const Color transparentColor = Color(0x00000000) + static const Color transparentColor = Color(0x00000000); } //0036E6 \ No newline at end of file From 062daa6c7767c1649a83620f550885849795a027 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 8 Oct 2024 13:44:55 +0400 Subject: [PATCH 027/122] bloc and separation of widgets --- .../bloc/space_management_bloc.dart | 99 +++++++++---------- .../bloc/space_management_event.dart | 46 +++++++++ .../bloc/space_management_state.dart | 33 +++++++ .../spaces_management/bloc/test_bloc.dart | 60 ----------- .../{view => widgets}/community_tile.dart | 0 .../{view => widgets}/plus_button_widget.dart | 0 .../{view => widgets}/sidebar_widget.dart | 16 +-- .../{view => widgets}/space_card_widget.dart | 0 .../space_container_widget.dart | 0 .../{view => widgets}/space_widget.dart | 0 10 files changed, 135 insertions(+), 119 deletions(-) create mode 100644 lib/pages/spaces_management/bloc/space_management_event.dart create mode 100644 lib/pages/spaces_management/bloc/space_management_state.dart delete mode 100644 lib/pages/spaces_management/bloc/test_bloc.dart rename lib/pages/spaces_management/{view => widgets}/community_tile.dart (100%) rename lib/pages/spaces_management/{view => widgets}/plus_button_widget.dart (100%) rename lib/pages/spaces_management/{view => widgets}/sidebar_widget.dart (93%) rename lib/pages/spaces_management/{view => widgets}/space_card_widget.dart (100%) rename lib/pages/spaces_management/{view => widgets}/space_container_widget.dart (100%) rename lib/pages/spaces_management/{view => widgets}/space_widget.dart (100%) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 678ba96d..dc2829c5 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -1,62 +1,57 @@ -import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - +import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; +import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; +import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; +import 'package:syncrow_web/services/space_mana_api.dart'; -// Events -abstract class SpaceEvent extends Equatable { - @override - List get props => []; -} +class SpaceManagementBloc + extends Bloc { + final CommunitySpaceManagementApi _api; -class LoadSpaces extends SpaceEvent {} + SpaceManagementBloc(this._api) : super(SpaceManagementInitial()) { + on(_onLoadCommunityAndSpaces); + on(_onCreateSpace); + on(_onUpdateSpacePosition); + } -class SelectSpace extends SpaceEvent { - final String selectedSpace; - - SelectSpace(this.selectedSpace); + void _onLoadCommunityAndSpaces( + LoadCommunityAndSpacesEvent event, + Emitter emit, + ) async { + emit(SpaceManagementLoading()); + try { + // Fetch all communities + List communities = await _api.fetchCommunities(); - @override - List get props => [selectedSpace]; -} + Map> communitySpaces = {}; -// States -abstract class SpaceState extends Equatable { - @override - List get props => []; -} - -class SpaceInitial extends SpaceState {} - -class SpaceLoaded extends SpaceState { - final List spaces; - final String selectedSpace; - - SpaceLoaded({required this.spaces, required this.selectedSpace}); - - @override - List get props => [spaces, selectedSpace]; -} - -// Bloc -class SpacesManagementBloc extends Bloc { - SpacesManagementBloc() : super(SpaceInitial()) { - on((event, emit) { - final List spaces = [ - SpaceModel(communityName: 'Downtown Dubai', subSpaces: ['Sub Space 1', 'Sub Space 2']), - SpaceModel(communityName: 'Dubai Creek Harbour', subSpaces: ['Sub Space 1', 'Sub Space 2']), - SpaceModel(communityName: 'Dubai Hills Estate', subSpaces: ['Sub Space 1', 'Sub Space 2']), - ]; - emit(SpaceLoaded(spaces: spaces, selectedSpace: spaces[0].communityName)); - }); - - on((event, emit) { - if (state is SpaceLoaded) { - final loadedState = state as SpaceLoaded; - emit(SpaceLoaded(spaces: loadedState.spaces, selectedSpace: event.selectedSpace)); + for (CommunityModel community in communities) { + // Fetch spaces hierarchy for each community + List spaces = await _api.getSpaceHierarchy(community.uuid); + community.spaces = spaces; + communitySpaces[community.uuid] = spaces; } - }); + + emit(SpaceManagementLoaded(communitySpaces: communitySpaces)); + } catch (e) { + emit(SpaceManagementError('Error loading communities and spaces: $e')); + } + } + + void _onCreateSpace( + CreateSpaceEvent event, + Emitter emit, + ) { + // Handle space creation logic + // You can emit a new state here based on your needs + emit(SpaceCreationSuccess()); + } + + void _onUpdateSpacePosition( + UpdateSpacePositionEvent event, + Emitter emit, + ) { + // Handle space position update logic } } - - diff --git a/lib/pages/spaces_management/bloc/space_management_event.dart b/lib/pages/spaces_management/bloc/space_management_event.dart new file mode 100644 index 00000000..048cc1ce --- /dev/null +++ b/lib/pages/spaces_management/bloc/space_management_event.dart @@ -0,0 +1,46 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; // Import for Offset + +abstract class SpaceManagementEvent extends Equatable { + const SpaceManagementEvent(); + + @override + List get props => []; +} + +class LoadCommunityAndSpacesEvent extends SpaceManagementEvent {} + +class CreateSpaceEvent extends SpaceManagementEvent { + final String name; + final String icon; + final Offset position; + final int? parentIndex; + final String? direction; + + CreateSpaceEvent({ + required this.name, + required this.icon, + required this.position, + this.parentIndex, + this.direction, + }); + + @override + List get props => [ + name, + icon, + position, + parentIndex ?? -1, // Use a fallback value if nullable + direction ?? '', // Use a fallback value if nullable + ]; +} + +class UpdateSpacePositionEvent extends SpaceManagementEvent { + final int index; + final Offset newPosition; + + UpdateSpacePositionEvent(this.index, this.newPosition); + + @override + List get props => [index, newPosition]; +} diff --git a/lib/pages/spaces_management/bloc/space_management_state.dart b/lib/pages/spaces_management/bloc/space_management_state.dart new file mode 100644 index 00000000..e1088cfc --- /dev/null +++ b/lib/pages/spaces_management/bloc/space_management_state.dart @@ -0,0 +1,33 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; + +abstract class SpaceManagementState extends Equatable { + const SpaceManagementState(); + + @override + List get props => []; +} + +class SpaceManagementInitial extends SpaceManagementState {} + +class SpaceManagementLoading extends SpaceManagementState {} + +class SpaceManagementLoaded extends SpaceManagementState { + final Map> communitySpaces; + + const SpaceManagementLoaded({required this.communitySpaces}); + + @override + List get props => [communitySpaces]; +} + +class SpaceCreationSuccess extends SpaceManagementState {} + +class SpaceManagementError extends SpaceManagementState { + final String errorMessage; + + const SpaceManagementError(this.errorMessage); + + @override + List get props => [errorMessage]; +} diff --git a/lib/pages/spaces_management/bloc/test_bloc.dart b/lib/pages/spaces_management/bloc/test_bloc.dart deleted file mode 100644 index 0b4dbe9d..00000000 --- a/lib/pages/spaces_management/bloc/test_bloc.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -// Define User States -abstract class UserState extends Equatable { - @override - List get props => []; -} - -class UserInitial extends UserState {} - -class UserLoaded extends UserState { - final List users; - final String selectedUser; - - UserLoaded({required this.users, required this.selectedUser}); - - @override - List get props => [users, selectedUser]; -} - -abstract class UserEvent extends Equatable { - @override - List get props => []; -} - -class LoadUsers extends UserEvent { - @override - List get props => []; -} - -class SelectUser extends UserEvent { - final String selectedUser; - - SelectUser(this.selectedUser); - - @override - List get props => [selectedUser]; -} - -class UserManagementBloc extends Bloc { - UserManagementBloc() : super(UserInitial()) { - on((event, emit) { - // Dummy list of users - final List users = []; - - // Emit the UserLoaded state with the dummy users - emit(UserLoaded(users: users, selectedUser: users[0])); - }); - - on((event, emit) { - // Handle user selection in the UserLoaded state - if (state is UserLoaded) { - final loadedState = state as UserLoaded; - emit(UserLoaded( - users: loadedState.users, selectedUser: event.selectedUser)); - } - }); - } -} diff --git a/lib/pages/spaces_management/view/community_tile.dart b/lib/pages/spaces_management/widgets/community_tile.dart similarity index 100% rename from lib/pages/spaces_management/view/community_tile.dart rename to lib/pages/spaces_management/widgets/community_tile.dart diff --git a/lib/pages/spaces_management/view/plus_button_widget.dart b/lib/pages/spaces_management/widgets/plus_button_widget.dart similarity index 100% rename from lib/pages/spaces_management/view/plus_button_widget.dart rename to lib/pages/spaces_management/widgets/plus_button_widget.dart diff --git a/lib/pages/spaces_management/view/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart similarity index 93% rename from lib/pages/spaces_management/view/sidebar_widget.dart rename to lib/pages/spaces_management/widgets/sidebar_widget.dart index 2056c148..68d811ff 100644 --- a/lib/pages/spaces_management/view/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -3,7 +3,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/common/custom_expansion_tile.dart'; import 'package:syncrow_web/common/search_bar.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; -import 'package:syncrow_web/pages/spaces_management/view/community_tile.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/community_tile.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_community_dialog.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -103,6 +103,8 @@ class _SidebarWidgetState extends State { width: 300, decoration: subSectionContainerDecoration, child: Column( + mainAxisSize: + MainAxisSize.min, // Ensures the Column only takes necessary height crossAxisAlignment: CrossAxisAlignment.start, children: [ // Communities title with the add button @@ -117,9 +119,7 @@ class _SidebarWidgetState extends State { style: Theme.of(context).textTheme.titleMedium, ), GestureDetector( - onTap: () { - _showCreateCommunityDialog(); - }, + onTap: _showCreateCommunityDialog, child: Container( width: 30, height: 30, @@ -143,13 +143,15 @@ class _SidebarWidgetState extends State { CustomSearchBar( onSearchChanged: (query) { setState(() { - _searchQuery = query; // Update search query on text change + _searchQuery = query; }); }, ), const SizedBox(height: 16), - // Community list with one item expanded at a time - Expanded( + // Community list + Flexible( + fit: FlexFit + .loose, // Ensure the ListView can flex but doesn't expand indefinitely child: ListView( children: filteredCommunities.keys.map((communityName) { return _buildCommunityTile( diff --git a/lib/pages/spaces_management/view/space_card_widget.dart b/lib/pages/spaces_management/widgets/space_card_widget.dart similarity index 100% rename from lib/pages/spaces_management/view/space_card_widget.dart rename to lib/pages/spaces_management/widgets/space_card_widget.dart diff --git a/lib/pages/spaces_management/view/space_container_widget.dart b/lib/pages/spaces_management/widgets/space_container_widget.dart similarity index 100% rename from lib/pages/spaces_management/view/space_container_widget.dart rename to lib/pages/spaces_management/widgets/space_container_widget.dart diff --git a/lib/pages/spaces_management/view/space_widget.dart b/lib/pages/spaces_management/widgets/space_widget.dart similarity index 100% rename from lib/pages/spaces_management/view/space_widget.dart rename to lib/pages/spaces_management/widgets/space_widget.dart From 76229788b336d070b2ee9806da9ec28411fa9858 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 8 Oct 2024 13:48:00 +0400 Subject: [PATCH 028/122] space widget --- .../view/spaces_management_page.dart | 376 +++++++++--------- 1 file changed, 188 insertions(+), 188 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 026e9d50..b69b440c 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -1,14 +1,19 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; +import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; +import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; -import 'package:syncrow_web/pages/spaces_management/view/sidebar_widget.dart'; -import 'package:syncrow_web/pages/spaces_management/view/space_card_widget.dart'; -import 'package:syncrow_web/pages/spaces_management/view/space_container_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/sidebar_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/space_card_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/space_container_widget.dart'; import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/web_layout/web_scaffold.dart'; class SpaceManagementPage extends StatefulWidget { @override @@ -29,213 +34,207 @@ class SpaceManagementPageState extends State { @override void initState() { super.initState(); - _loadCommunityAndSpacesData(); } - // Function to load all communities and their respective spaces - void _loadCommunityAndSpacesData() async { - try { - // Fetch all communities - List communities = await _api.fetchCommunities(); - - for (CommunityModel community in communities) { - // Fetch spaces hierarchy for each community - List spaces = await _api.getSpaceHierarchy(community.uuid); - - // Store the result in the communitySpaces map - community.spaces = spaces; - } - - // Update the state to reflect loaded data - setState(() { - // Optionally, you can initialize something here based on the loaded data - }); - } catch (e) { - debugPrint('Error loading communities and spaces: $e'); - } - } - - void _handleCommunitySelection(String community) { - // Handle community selection here - print("Selected Community: $community"); - } @override Widget build(BuildContext context) { Size screenSize = MediaQuery.of(context).size; - return Scaffold( - backgroundColor: ColorsManager.whiteColors, - appBar: AppBar( - title: const Text('Space Management'), + return BlocProvider( + create: (context) => SpaceManagementBloc(CommunitySpaceManagementApi()) + ..add(LoadCommunityAndSpacesEvent()), + child: WebScaffold( + appBarTitle: Text( + 'Space Management', + style: Theme.of(context).textTheme.headlineLarge, + ), + enableMenuSideba: false, + scaffoldBody: BlocBuilder( + builder: (context, state) { + if (state is SpaceManagementLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is SpaceManagementLoaded) { + return _buildLoadedState( + context, screenSize, state.communitySpaces); + } else if (state is SpaceManagementError) { + return Center(child: Text('Error: ${state.errorMessage}')); + } + return Container(); + }, + ), ), - body: Stack( - clipBehavior: Clip.none, - children: [ - Row( - children: [ - // Sidebar for navigation and community list - SidebarWidget( - communitySpaces: communitySpaces, // Pass communitySpaces here - onCommunitySelected: _handleCommunitySelection, - ), - // Right Side: Community Structure Area - Expanded( - child: Container( - decoration: const BoxDecoration( - color: ColorsManager.whiteColors, - border: Border( - left: BorderSide( - color: ColorsManager.whiteColors, - width: 1.0), // Light left border to match - ), + ); + } + + Widget _buildLoadedState(BuildContext context, Size screenSize, + Map> communitySpaces) { + return Stack( + clipBehavior: Clip.none, + children: [ + Row( + children: [ + SidebarWidget( + communitySpaces: communitySpaces, + onCommunitySelected: (community) { + context.read().add( + LoadCommunityAndSpacesEvent(), // Re-fetch or perform community-specific actions + ); + }, + ), + Expanded( + child: _buildCommunityStructureArea(context, screenSize), + ), + ], + ), + _buildGradientBorder(), + ], + ); + } + + Widget _buildCommunityStructureArea(BuildContext context, Size screenSize) { + return Expanded( + child: Container( + decoration: const BoxDecoration( + color: ColorsManager.whiteColors, + border: Border( + left: BorderSide( + color: ColorsManager.whiteColors, + width: 1.0), // Light left border to match + ), + ), + // Background color for canvas + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), + width: double.infinity, + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + boxShadow: [ + BoxShadow( + color: ColorsManager.shadowBlackColor, // Subtle shadow + spreadRadius: 0, // No spread + blurRadius: 8, // Softer shadow edges + offset: Offset(0, 4), // Shadow only on the bottom ), - // Background color for canvas - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + ], + ), + child: Text( + 'Community Structure', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + ), + // Use Expanded to ensure InteractiveViewer takes the available space + Flexible( + child: InteractiveViewer( + boundaryMargin: const EdgeInsets.all(500), // Adjusted to 500 + minScale: 0.5, // Minimum zoom scale + maxScale: 2.5, // Maximum zoom scale + panEnabled: true, // Enable panning + scaleEnabled: true, // Enable zooming + child: Container( + width: 2000, // Large canvas + height: 2000, // Large canvas + color: + ColorsManager.transparentColor, // Transparent background + child: Stack( + clipBehavior: Clip.none, children: [ - Container( - padding: - const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), - width: double.infinity, - decoration: BoxDecoration( - color: ColorsManager.whiteColors, - boxShadow: [ - BoxShadow( - color: ColorsManager - .shadowBlackColor, // Subtle shadow - spreadRadius: 0, // No spread - blurRadius: 8, // Softer shadow edges - offset: Offset(0, 4), // Shadow only on the bottom - ), - ], - ), - child: Text( - 'Community Structure', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), + // Draw lines using a CustomPaint widget + CustomPaint( + size: Size(2000, 2000), // Explicit canvas size + painter: CurvedLinePainter(connections), ), - // Use Expanded to ensure InteractiveViewer takes the available space - Expanded( - child: InteractiveViewer( - boundaryMargin: - const EdgeInsets.all(500), // Adjusted to 500 - minScale: 0.5, // Minimum zoom scale - maxScale: 2.5, // Maximum zoom scale - panEnabled: true, // Enable panning - scaleEnabled: true, // Enable zooming - child: Container( - width: 2000, // Large canvas - height: 2000, // Large canvas - color: ColorsManager - .transparentColor, // Transparent background - child: Stack( - clipBehavior: Clip.none, - children: [ - // Draw lines using a CustomPaint widget - CustomPaint( - size: - Size(2000, 2000), // Explicit canvas size - painter: CurvedLinePainter(connections), - ), - Center( - child: spaces.isEmpty - ? AddSpaceButton( - onTap: () { - _showCreateSpaceDialog(screenSize); + Center( + child: spaces.isEmpty + ? AddSpaceButton( + onTap: () { + _showCreateSpaceDialog(screenSize); + }, + ) + : Stack( + children: spaces + .asMap() + .entries + .map((entry) => SpaceCardWidget( + index: entry.key, + screenSize: screenSize, + position: spaces[entry.key].position, + isHovered: + spaces[entry.key].isHovered, + onPanUpdate: + (int index, Offset delta) { + setState(() { + spaces[index].position += delta; + }); }, - ) - : Stack( - children: spaces - .asMap() - .entries - .map((entry) => SpaceCardWidget( - index: entry.key, - screenSize: screenSize, - position: spaces[entry.key] - .position, - isHovered: spaces[entry.key] - .isHovered, - onPanUpdate: (int index, - Offset delta) { - setState(() { - spaces[index] - .position += delta; - }); - }, - onHoverChanged: (int index, - bool isHovered) { - setState(() { - spaces[index] - .isHovered = - isHovered; - }); - }, - onButtonTap: (int index, - Offset newPosition, - String direction) { - _showCreateSpaceDialog( - screenSize, - position: spaces[index] - .position + - newPosition, - parentIndex: index, - direction: direction, - ); - }, - buildSpaceContainer: - (int index) { - return SpaceContainerWidget( - index: index, - icon: - spaces[index].icon, - name: - spaces[index].name, - ); - }, - )) - .toList(), - ), - ), - ], - ), - ), - ), + onHoverChanged: + (int index, bool isHovered) { + setState(() { + spaces[index].isHovered = + isHovered; + }); + }, + onButtonTap: (int index, + Offset newPosition, + String direction) { + _showCreateSpaceDialog( + screenSize, + position: spaces[index].position + + newPosition, + parentIndex: index, + direction: direction, + ); + }, + buildSpaceContainer: (int index) { + return SpaceContainerWidget( + index: index, + icon: spaces[index].icon, + name: spaces[index].name, + ); + }, + )) + .toList(), + ), ), ], ), ), ), + ), + ], + ), + ), + + ); + } + + Widget _buildGradientBorder() { + return Positioned( + top: 0, + bottom: 0, + left: 300, + width: 8, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + ColorsManager.semiTransparentBlackColor.withOpacity(0.1), + ColorsManager.transparentColor, ], ), - Positioned( - top: 0, - bottom: 0, - left: 300, // Align with the sidebar's width - width: 8, // Width of the gradient border - child: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.centerLeft, - end: Alignment.centerRight, - colors: [ - ColorsManager.semiTransparentBlackColor - .withOpacity(0.1), // Light gray - ColorsManager.transparentColor, // Transparent fade - ], - ), - ), - ), - ), - ], + ), ), ); } - // Function to open the Create Space dialog void _showCreateSpaceDialog(Size screenSize, {Offset? position, int? parentIndex, String? direction}) { showDialog( @@ -272,6 +271,8 @@ class SpaceManagementPageState extends State { } } +// Function to open the Create Space dialog + // Model for storing space information class SpaceData { final String name; @@ -298,4 +299,3 @@ class Connection { required this.endSpace, required this.direction}); } - From 72dfedf2b6c8d860dde7a1f133e81f5ea870ab97 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 8 Oct 2024 13:54:24 +0400 Subject: [PATCH 029/122] removed logs --- .../view/spaces_management_page.dart | 3 --- .../widgets/sidebar_widget.dart | 21 ------------------- 2 files changed, 24 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index b69b440c..e41af548 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -4,7 +4,6 @@ import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; -import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; @@ -36,7 +35,6 @@ class SpaceManagementPageState extends State { super.initState(); } - @override Widget build(BuildContext context) { Size screenSize = MediaQuery.of(context).size; @@ -210,7 +208,6 @@ class SpaceManagementPageState extends State { ], ), ), - ); } diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 68d811ff..a91d9977 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -26,27 +26,6 @@ class _SidebarWidgetState extends State { @override void initState() { super.initState(); - _logCommunitySpaces(); // Log the structure on initialization - } - - // Function to log the communitySpaces data structure for debugging - void _logCommunitySpaces() { - debugPrint('Logging communitySpaces structure:'); - widget.communitySpaces.forEach((communityName, spaces) { - debugPrint('Community: $communityName'); - _logSpaces(spaces, 1); - }); - } - - // Helper function to log the spaces and their children - void _logSpaces(List spaces, int level) { - for (SpaceModel space in spaces) { - debugPrint( - '${' ' * level}Space: ${space.name}, UUID: ${space.uuid}, Has children: ${space.children.isNotEmpty}'); - if (space.children.isNotEmpty) { - _logSpaces(space.children, level + 1); - } - } } void _showCreateCommunityDialog() { From 1156af9b8eda6943e26d44d142d6b698e699dd93 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 8 Oct 2024 20:03:31 +0400 Subject: [PATCH 030/122] fixed expand error --- .../bloc/space_management_bloc.dart | 29 ++++++++--- .../bloc/space_management_state.dart | 8 +-- .../spaces_management/model/space_model.dart | 8 +-- .../view/spaces_management_page.dart | 14 +++-- .../widgets/sidebar_widget.dart | 52 ++++++++----------- lib/services/space_mana_api.dart | 1 + 6 files changed, 60 insertions(+), 52 deletions(-) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index dc2829c5..f8093208 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; @@ -24,16 +25,28 @@ class SpaceManagementBloc // Fetch all communities List communities = await _api.fetchCommunities(); - Map> communitySpaces = {}; + // Use Future.wait to handle async calls within map + List updatedCommunities = await Future.wait( + communities.map((community) async { + List spaces = + await _api.getSpaceHierarchy(community.uuid); - for (CommunityModel community in communities) { - // Fetch spaces hierarchy for each community - List spaces = await _api.getSpaceHierarchy(community.uuid); - community.spaces = spaces; - communitySpaces[community.uuid] = spaces; - } + debugPrint( + 'Fetched spaces for community ${community.name}: ${spaces.length}'); - emit(SpaceManagementLoaded(communitySpaces: communitySpaces)); + return CommunityModel( + uuid: community.uuid, + createdAt: community.createdAt, + updatedAt: community.updatedAt, + name: community.name, + description: community.description, + spaces: spaces, // New spaces list + region: community.region, + ); + }).toList(), + ); + + emit(SpaceManagementLoaded(communities: updatedCommunities)); } catch (e) { emit(SpaceManagementError('Error loading communities and spaces: $e')); } diff --git a/lib/pages/spaces_management/bloc/space_management_state.dart b/lib/pages/spaces_management/bloc/space_management_state.dart index e1088cfc..3f8c4af8 100644 --- a/lib/pages/spaces_management/bloc/space_management_state.dart +++ b/lib/pages/spaces_management/bloc/space_management_state.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; abstract class SpaceManagementState extends Equatable { @@ -13,12 +14,13 @@ class SpaceManagementInitial extends SpaceManagementState {} class SpaceManagementLoading extends SpaceManagementState {} class SpaceManagementLoaded extends SpaceManagementState { - final Map> communitySpaces; + final List communities; - const SpaceManagementLoaded({required this.communitySpaces}); + + const SpaceManagementLoaded({ required this.communities}); @override - List get props => [communitySpaces]; + List get props => [communities]; } class SpaceCreationSuccess extends SpaceManagementState {} diff --git a/lib/pages/spaces_management/model/space_model.dart b/lib/pages/spaces_management/model/space_model.dart index 4ba54545..273c09cd 100644 --- a/lib/pages/spaces_management/model/space_model.dart +++ b/lib/pages/spaces_management/model/space_model.dart @@ -13,7 +13,7 @@ class SpaceModel { final SpaceModel? parent; final CommunityModel? community; final List children; - final String icon; + final String? icon; Offset position; bool isHovered; @@ -54,11 +54,11 @@ class SpaceModel { .map((child) => SpaceModel.fromJson(child)) .toList() : [], - icon: json['icon'], // New field from JSON + icon: json['icon'], position: json['position'] != null ? Offset(json['position']['dx'], json['position']['dy']) - : Offset(0, 0), // Default position if not provided in JSON - isHovered: json['isHovered'] ?? false, // Default isHovered if not in JSON + : const Offset(0, 0), + isHovered: false, ); } diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index e41af548..86bfe880 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; +import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; @@ -53,8 +54,7 @@ class SpaceManagementPageState extends State { if (state is SpaceManagementLoading) { return const Center(child: CircularProgressIndicator()); } else if (state is SpaceManagementLoaded) { - return _buildLoadedState( - context, screenSize, state.communitySpaces); + return _buildLoadedState(context, screenSize, state.communities); } else if (state is SpaceManagementError) { return Center(child: Text('Error: ${state.errorMessage}')); } @@ -65,24 +65,22 @@ class SpaceManagementPageState extends State { ); } - Widget _buildLoadedState(BuildContext context, Size screenSize, - Map> communitySpaces) { + Widget _buildLoadedState( + BuildContext context, Size screenSize, List communities) { return Stack( clipBehavior: Clip.none, children: [ Row( children: [ SidebarWidget( - communitySpaces: communitySpaces, + communities: communities, onCommunitySelected: (community) { context.read().add( LoadCommunityAndSpacesEvent(), // Re-fetch or perform community-specific actions ); }, ), - Expanded( - child: _buildCommunityStructureArea(context, screenSize), - ), + _buildCommunityStructureArea(context, screenSize), ], ), _buildGradientBorder(), diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index a91d9977..b5596315 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/common/custom_expansion_tile.dart'; import 'package:syncrow_web/common/search_bar.dart'; +import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/community_tile.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_community_dialog.dart'; @@ -11,9 +12,9 @@ import 'package:syncrow_web/utils/style.dart'; class SidebarWidget extends StatefulWidget { final Function(String)? onCommunitySelected; - final Map> communitySpaces; + final List communities; - SidebarWidget({this.onCommunitySelected, required this.communitySpaces}); + SidebarWidget({this.onCommunitySelected, required this.communities}); @override _SidebarWidgetState createState() => _SidebarWidgetState(); @@ -34,9 +35,7 @@ class _SidebarWidgetState extends State { builder: (context) => CreateCommunityDialog( onCreateCommunity: (String communityName) { setState(() { - debugPrint("hello"); - - // You can update the communitySpaces map here + // You can update the community list here when a new community is added }); }, ), @@ -44,21 +43,19 @@ class _SidebarWidgetState extends State { } // Function to filter communities based on the search query - Map> _filterCommunities() { + List _filterCommunities() { if (_searchQuery.isEmpty) { - return widget.communitySpaces; + return widget.communities; } // Filter communities and their spaces based on the search query - final filteredCommunitySpaces = >{}; - widget.communitySpaces.forEach((communityName, spaces) { - if (communityName.toLowerCase().contains(_searchQuery.toLowerCase()) || - spaces.any( - (space) => _containsQuery(space, _searchQuery.toLowerCase()))) { - filteredCommunitySpaces[communityName] = spaces; - } - }); - return filteredCommunitySpaces; + return widget.communities.where((community) { + final containsQueryInCommunity = + community.name.toLowerCase().contains(_searchQuery.toLowerCase()); + final containsQueryInSpaces = community.spaces + .any((space) => _containsQuery(space, _searchQuery.toLowerCase())); + return containsQueryInCommunity || containsQueryInSpaces; + }).toList(); } // Helper function to determine if any space or its children match the search query @@ -128,13 +125,10 @@ class _SidebarWidgetState extends State { ), const SizedBox(height: 16), // Community list - Flexible( - fit: FlexFit - .loose, // Ensure the ListView can flex but doesn't expand indefinitely + Expanded( child: ListView( - children: filteredCommunities.keys.map((communityName) { - return _buildCommunityTile( - communityName, filteredCommunities[communityName]!); + children: filteredCommunities.map((community) { + return _buildCommunityTile(community); }).toList(), ), ), @@ -143,21 +137,21 @@ class _SidebarWidgetState extends State { ); } - Widget _buildCommunityTile(String communityName, List spaces) { - bool hasChildren = spaces.isNotEmpty; + Widget _buildCommunityTile(CommunityModel community) { + bool hasChildren = community.spaces.isNotEmpty; debugPrint( - 'Building CommunityTile for $communityName, hasChildren: $hasChildren'); + 'Building CommunityTile for ${community.name}, hasChildren: $hasChildren'); return CommunityTile( - title: communityName, - isExpanded: _expandedTiles[communityName] ?? false, + title: community.name, + isExpanded: _expandedTiles[community.uuid] ?? false, onExpansionChanged: (String title, bool expanded) { debugPrint( 'CommunityTile onExpansionChanged called for $title, expanded: $expanded'); - _handleExpansionChange(title, expanded); + _handleExpansionChange(community.uuid, expanded); }, children: hasChildren - ? spaces.map((space) => _buildSpaceTile(space)).toList() + ? community.spaces.map((space) => _buildSpaceTile(space)).toList() : null, // Render spaces within the community ); } diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index bdb369d7..4a60c8c3 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -223,4 +223,5 @@ class CommunitySpaceManagementApi { return []; } } + } From 33712b7690890cc87c2d95e62b13936084bbabc4 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 8 Oct 2024 20:37:04 +0400 Subject: [PATCH 031/122] fixed re-rendering of community widget --- lib/common/custom_expansion_tile.dart | 27 ++++++----- .../widgets/community_tile.dart | 32 +++++++++---- .../widgets/sidebar_widget.dart | 16 +++---- .../widgets/space_tile_widget.dart | 45 +++++++++++++++++++ 4 files changed, 90 insertions(+), 30 deletions(-) create mode 100644 lib/pages/spaces_management/widgets/space_tile_widget.dart diff --git a/lib/common/custom_expansion_tile.dart b/lib/common/custom_expansion_tile.dart index bf39991e..995b5d04 100644 --- a/lib/common/custom_expansion_tile.dart +++ b/lib/common/custom_expansion_tile.dart @@ -5,8 +5,8 @@ class CustomExpansionTile extends StatefulWidget { final String title; final List? children; final bool initiallyExpanded; - final bool? isExpanded; // New parameter to control expansion - final ValueChanged? onExpansionChanged; // Callback for expansion change + final bool? isExpanded; // External control over expansion + final ValueChanged? onExpansionChanged; // Notify when expansion changes CustomExpansionTile({ required this.title, @@ -21,8 +21,8 @@ class CustomExpansionTile extends StatefulWidget { } class CustomExpansionTileState extends State { - bool _isExpanded = false; - bool _isChecked = false; + bool _isExpanded = false; // Local expansion state + bool _isChecked = false; // Local checkbox state @override void initState() { @@ -33,6 +33,7 @@ class CustomExpansionTileState extends State { @override void didUpdateWidget(CustomExpansionTile oldWidget) { super.didUpdateWidget(oldWidget); + // Sync local state with external control of expansion state if (widget.isExpanded != null && widget.isExpanded != _isExpanded) { setState(() { _isExpanded = widget.isExpanded!; @@ -40,6 +41,7 @@ class CustomExpansionTileState extends State { } } + // Utility function to capitalize the first letter of the title String _capitalizeFirstLetter(String text) { if (text.isEmpty) return text; return text[0].toUpperCase() + text.substring(1); @@ -49,6 +51,7 @@ class CustomExpansionTileState extends State { Widget build(BuildContext context) { return Column( children: [ + // The main clickable row for the expansion tile InkWell( onTap: () { setState(() { @@ -58,7 +61,7 @@ class CustomExpansionTileState extends State { }, child: Row( children: [ - // Customizing the Checkbox + // Checkbox with independent state management Checkbox( value: _isChecked, onChanged: (bool? value) { @@ -66,11 +69,11 @@ class CustomExpansionTileState extends State { _isChecked = value ?? false; }); }, - side: WidgetStateBorderSide.resolveWith((states) { + side: MaterialStateBorderSide.resolveWith((states) { return const BorderSide(color: ColorsManager.grayBorder); }), - fillColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.selected)) { + fillColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.selected)) { return ColorsManager.grayBorder; } else { return ColorsManager.checkBoxFillColor; @@ -78,15 +81,16 @@ class CustomExpansionTileState extends State { }), checkColor: ColorsManager.whiteColors, ), + // Show the expand/collapse icon if (widget.children != null && widget.children!.isNotEmpty) Icon( _isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right, color: Colors.grey, - size: 16.0, // Reduced icon size + size: 16.0, // Adjusted size for better alignment ), - // Title Text + // The title text with dynamic styling Expanded( child: Text( _capitalizeFirstLetter(widget.title), @@ -101,11 +105,12 @@ class CustomExpansionTileState extends State { ], ), ), + // The expanded section (children) that shows when the tile is expanded if (_isExpanded && widget.children != null && widget.children!.isNotEmpty) Padding( - padding: const EdgeInsets.only(left: 48.0), // Indent the children + padding: const EdgeInsets.only(left: 48.0), // Indented children child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: widget.children!, diff --git a/lib/pages/spaces_management/widgets/community_tile.dart b/lib/pages/spaces_management/widgets/community_tile.dart index 7511d4f3..2943a6e5 100644 --- a/lib/pages/spaces_management/widgets/community_tile.dart +++ b/lib/pages/spaces_management/widgets/community_tile.dart @@ -1,29 +1,45 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/common/custom_expansion_tile.dart'; -class CommunityTile extends StatelessWidget { +class CommunityTile extends StatefulWidget { final String title; - final bool isExpanded; - final Function(String, bool) onExpansionChanged; final List? children; + final bool initiallyExpanded; + final Function(String, bool) onExpansionChanged; const CommunityTile({ Key? key, required this.title, - required this.isExpanded, + required this.initiallyExpanded, required this.onExpansionChanged, this.children, }) : super(key: key); + @override + _CommunityTileState createState() => _CommunityTileState(); +} + +class _CommunityTileState extends State { + late bool _isExpanded; + + @override + void initState() { + super.initState(); + _isExpanded = widget.initiallyExpanded; + } + @override Widget build(BuildContext context) { return CustomExpansionTile( - title: title, - initiallyExpanded: isExpanded, + title: widget.title, + initiallyExpanded: _isExpanded, onExpansionChanged: (bool expanded) { - onExpansionChanged(title, expanded); + setState(() { + _isExpanded = expanded; + }); + widget.onExpansionChanged(widget.title, expanded); }, - children: children ?? [], + children: widget.children ?? [], ); } } diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index b5596315..f12f89b3 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:syncrow_web/common/custom_expansion_tile.dart'; import 'package:syncrow_web/common/search_bar.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/community_tile.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_community_dialog.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/space_tile_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/style.dart'; @@ -144,7 +144,7 @@ class _SidebarWidgetState extends State { 'Building CommunityTile for ${community.name}, hasChildren: $hasChildren'); return CommunityTile( title: community.name, - isExpanded: _expandedTiles[community.uuid] ?? false, + initiallyExpanded: false, onExpansionChanged: (String title, bool expanded) { debugPrint( 'CommunityTile onExpansionChanged called for $title, expanded: $expanded'); @@ -159,9 +159,9 @@ class _SidebarWidgetState extends State { Widget _buildSpaceTile(SpaceModel space) { debugPrint( 'Building SpaceTile for ${space.name}, hasChildren: ${space.children.isNotEmpty}'); - return CustomExpansionTile( + return SpaceTile( title: space.name, - isExpanded: _expandedTiles[space.uuid] ?? false, + initiallyExpanded: false, onExpansionChanged: (bool expanded) { debugPrint( 'SpaceTile onExpansionChanged called for ${space.name}, expanded: $expanded'); @@ -175,11 +175,5 @@ class _SidebarWidgetState extends State { ); } - void _handleExpansionChange(String uuid, bool expanded) { - setState(() { - _expandedTiles[uuid] = expanded; - debugPrint('_expandedTiles updated: $_expandedTiles'); - }); - widget.onCommunitySelected?.call(uuid); - } + void _handleExpansionChange(String uuid, bool expanded) {} } diff --git a/lib/pages/spaces_management/widgets/space_tile_widget.dart b/lib/pages/spaces_management/widgets/space_tile_widget.dart new file mode 100644 index 00000000..8c48d568 --- /dev/null +++ b/lib/pages/spaces_management/widgets/space_tile_widget.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/common/custom_expansion_tile.dart'; + +class SpaceTile extends StatefulWidget { + final String title; + final bool initiallyExpanded; + final ValueChanged onExpansionChanged; + final List? children; + + const SpaceTile({ + Key? key, + required this.title, + required this.initiallyExpanded, + required this.onExpansionChanged, + this.children, + }) : super(key: key); + + @override + _SpaceTileState createState() => _SpaceTileState(); +} + +class _SpaceTileState extends State { + late bool _isExpanded; + + @override + void initState() { + super.initState(); + _isExpanded = widget.initiallyExpanded; + } + + @override + Widget build(BuildContext context) { + return CustomExpansionTile( + title: widget.title, + initiallyExpanded: _isExpanded, + onExpansionChanged: (bool expanded) { + setState(() { + _isExpanded = expanded; + }); + widget.onExpansionChanged(expanded); + }, + children: widget.children ?? [], + ); + } +} From 3a94ebedda5530d69a4e53900e4f414f7e3dad38 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 8 Oct 2024 23:51:01 +0400 Subject: [PATCH 032/122] filtering --- lib/common/custom_expansion_tile.dart | 4 +- .../widgets/sidebar_widget.dart | 43 ++++++++++++++++--- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/lib/common/custom_expansion_tile.dart b/lib/common/custom_expansion_tile.dart index 995b5d04..f637f118 100644 --- a/lib/common/custom_expansion_tile.dart +++ b/lib/common/custom_expansion_tile.dart @@ -95,9 +95,7 @@ class CustomExpansionTileState extends State { child: Text( _capitalizeFirstLetter(widget.title), style: TextStyle( - color: _isExpanded - ? ColorsManager.blackColor - : ColorsManager.lightGrayColor, + color:ColorsManager.lightGrayColor, fontWeight: FontWeight.w400, ), ), diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index f12f89b3..9314b10c 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -22,7 +22,8 @@ class SidebarWidget extends StatefulWidget { class _SidebarWidgetState extends State { String _searchQuery = ''; // Track search query - Map _expandedTiles = {}; // Track expanded state for each UUID + String? _selectedCommunityUuid; + String? _selectedSpaceUuid; @override void initState() { @@ -45,29 +46,55 @@ class _SidebarWidgetState extends State { // Function to filter communities based on the search query List _filterCommunities() { if (_searchQuery.isEmpty) { + // Reset the selected community and space UUIDs if there's no query + _selectedCommunityUuid = null; + _selectedSpaceUuid = null; return widget.communities; } - // Filter communities and their spaces based on the search query + // Filter communities and expand only those that match the query return widget.communities.where((community) { final containsQueryInCommunity = community.name.toLowerCase().contains(_searchQuery.toLowerCase()); final containsQueryInSpaces = community.spaces .any((space) => _containsQuery(space, _searchQuery.toLowerCase())); + + if (containsQueryInCommunity || containsQueryInSpaces) { + _selectedCommunityUuid = + community.uuid; // Set the community to expanded + } + return containsQueryInCommunity || containsQueryInSpaces; }).toList(); } // Helper function to determine if any space or its children match the search query bool _containsQuery(SpaceModel space, String query) { - if (space.name.toLowerCase().contains(query)) { + final matchesSpace = space.name.toLowerCase().contains(query); + final matchesChildren = space.children.any((child) => + _containsQuery(child, query)); // Recursive check for children + + // If the space or any of its children match the query, expand this space + if (matchesSpace || matchesChildren) { + _selectedSpaceUuid = space.uuid; + } + + return matchesSpace || matchesChildren; + } + + bool _isSpaceOrChildSelected(SpaceModel space) { + // Return true if the current space or any of its child spaces is selected + if (_selectedSpaceUuid == space.uuid) { return true; } + + // Recursively check if any child spaces match the query for (var child in space.children) { - if (_containsQuery(child, query)) { + if (_isSpaceOrChildSelected(child)) { return true; } } + return false; } @@ -139,12 +166,13 @@ class _SidebarWidgetState extends State { Widget _buildCommunityTile(CommunityModel community) { bool hasChildren = community.spaces.isNotEmpty; + bool isSelectedCommunity = _selectedCommunityUuid == community.uuid; debugPrint( 'Building CommunityTile for ${community.name}, hasChildren: $hasChildren'); return CommunityTile( title: community.name, - initiallyExpanded: false, + initiallyExpanded: isSelectedCommunity, onExpansionChanged: (String title, bool expanded) { debugPrint( 'CommunityTile onExpansionChanged called for $title, expanded: $expanded'); @@ -157,11 +185,14 @@ class _SidebarWidgetState extends State { } Widget _buildSpaceTile(SpaceModel space) { + bool isSelectedSpace = + _isSpaceOrChildSelected(space); // Check if space should be expanded + debugPrint( 'Building SpaceTile for ${space.name}, hasChildren: ${space.children.isNotEmpty}'); return SpaceTile( title: space.name, - initiallyExpanded: false, + initiallyExpanded: isSelectedSpace, onExpansionChanged: (bool expanded) { debugPrint( 'SpaceTile onExpansionChanged called for ${space.name}, expanded: $expanded'); From 9cb58771e0b835a5b6d0687a2922b4a9778b709f Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 9 Oct 2024 10:15:29 +0400 Subject: [PATCH 033/122] added community list view --- .../view/community_list_view.dart | 117 ++++++++++++++++++ .../view/spaces_management_page.dart | 11 +- 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 lib/pages/spaces_management/view/community_list_view.dart diff --git a/lib/pages/spaces_management/view/community_list_view.dart b/lib/pages/spaces_management/view/community_list_view.dart new file mode 100644 index 00000000..0812b8f9 --- /dev/null +++ b/lib/pages/spaces_management/view/community_list_view.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; + +class CommunityListViewWidget extends StatelessWidget { + final List communities; + + const CommunityListViewWidget({ + Key? key, + required this.communities, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + + // Increase the item count by 1 to include the blank community + return Expanded( + child: Container( + color: Colors.white, + child: GridView.builder( + padding: const EdgeInsets.all(16.0), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, // Three containers per row + crossAxisSpacing: size.height * 0.041, + mainAxisSpacing: size.width * 0.015, + childAspectRatio: 361 / 239, // Aspect ratio based on container size + ), + itemCount: communities.length + 1, // One additional item for the blank community + itemBuilder: (context, index) { + // If the index is 0, display the blank community, otherwise display the normal ones + if (index == 0) { + return _buildBlankCommunityCard(size); + } else { + return _buildCommunityCard(communities[index - 1], size); + } + }, + ), + ), + ); + } + + // Build the blank community container + Widget _buildBlankCommunityCard(Size size) { + return Container( + width: size.width * .18, + height: size.height * .22, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Inner blank container with white background and border + Container( + width: size.width * .18, + height: size.height * .16, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(5), + border: Border.all( + color: Color(0xFFE5E5E5), + width: 5, + ), + ), + ), + SizedBox(height: size.height * 0.02), // Add spacing between container and text + // Text saying "Blank" for the blank community + Text( + 'Blank', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 18, + fontFamily: 'Aftika', + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ); + } + + // Build a single community container based on the format provided + Widget _buildCommunityCard(CommunityModel community, Size size) { + return Container( + width: size.width * .18, + height: size.height * .22, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Inner container with white background and border + Container( + width: size.width * .18, + height: size.height * .16, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(5), + border: Border.all( + color: Color(0xFFE5E5E5), + width: 5, + ), + ), + ), + SizedBox(height: size.height * 0.02), // Add spacing between container and text + // Community name text + Text( + community.name ?? 'Blank', // Display community name + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 18, + fontFamily: 'Aftika', + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 86bfe880..ea58c600 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -6,6 +6,7 @@ import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event. import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; +import 'package:syncrow_web/pages/spaces_management/view/community_list_view.dart'; import 'package:syncrow_web/pages/spaces_management/view/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/sidebar_widget.dart'; @@ -25,6 +26,9 @@ class SpaceManagementPageState extends State { List spaces = []; List connections = []; + // Track whether to show the community list view or community structure + bool showCommunityStructure = false; + // API instance final CommunitySpaceManagementApi _api = CommunitySpaceManagementApi(); @@ -80,7 +84,12 @@ class SpaceManagementPageState extends State { ); }, ), - _buildCommunityStructureArea(context, screenSize), + SizedBox(width: 45), + showCommunityStructure + ? _buildCommunityStructureArea(context, screenSize) + : CommunityListViewWidget( + communities: communities, + ), ], ), _buildGradientBorder(), From 1046690f9bcc79483d5a118cdb2e1e88a1c8e160 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 9 Oct 2024 10:18:41 +0400 Subject: [PATCH 034/122] isolating widgets --- .../view/spaces_management_page.dart | 26 ++----------- .../gradient_canvas_border_widget.dart | 39 +++++++++++++++++++ 2 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 lib/pages/spaces_management/widgets/gradient_canvas_border_widget.dart diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index ea58c600..4427234b 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/community_list_view.dart'; import 'package:syncrow_web/pages/spaces_management/view/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/gradient_canvas_border_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/sidebar_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/space_card_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/space_container_widget.dart'; @@ -84,7 +85,7 @@ class SpaceManagementPageState extends State { ); }, ), - SizedBox(width: 45), + const SizedBox(width: 45), showCommunityStructure ? _buildCommunityStructureArea(context, screenSize) : CommunityListViewWidget( @@ -92,7 +93,7 @@ class SpaceManagementPageState extends State { ), ], ), - _buildGradientBorder(), + const GradientBorderWidget() ], ); } @@ -218,27 +219,6 @@ class SpaceManagementPageState extends State { ); } - Widget _buildGradientBorder() { - return Positioned( - top: 0, - bottom: 0, - left: 300, - width: 8, - child: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.centerLeft, - end: Alignment.centerRight, - colors: [ - ColorsManager.semiTransparentBlackColor.withOpacity(0.1), - ColorsManager.transparentColor, - ], - ), - ), - ), - ); - } - void _showCreateSpaceDialog(Size screenSize, {Offset? position, int? parentIndex, String? direction}) { showDialog( diff --git a/lib/pages/spaces_management/widgets/gradient_canvas_border_widget.dart b/lib/pages/spaces_management/widgets/gradient_canvas_border_widget.dart new file mode 100644 index 00000000..638434dc --- /dev/null +++ b/lib/pages/spaces_management/widgets/gradient_canvas_border_widget.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class GradientBorderWidget extends StatelessWidget { + final double top; + final double bottom; + final double left; + final double width; + + const GradientCanvasBorderWidget({ + Key? key, + this.top = 0, + this.bottom = 0, + this.left = 300, + this.width = 8, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Positioned( + top: top, + bottom: bottom, + left: left, + width: width, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + ColorsManager.semiTransparentBlackColor.withOpacity(0.1), + ColorsManager.transparentColor, + ], + ), + ), + ), + ); + } +} From ec4bb9bc04f1d4464e58be1c630e78fdebdc03be Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 9 Oct 2024 10:42:35 +0400 Subject: [PATCH 035/122] added on select for community --- .../view/community_list_view.dart | 19 +++++++++++++++---- .../view/spaces_management_page.dart | 16 +++++++++++----- .../gradient_canvas_border_widget.dart | 2 +- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/lib/pages/spaces_management/view/community_list_view.dart b/lib/pages/spaces_management/view/community_list_view.dart index 0812b8f9..eafd4066 100644 --- a/lib/pages/spaces_management/view/community_list_view.dart +++ b/lib/pages/spaces_management/view/community_list_view.dart @@ -3,10 +3,12 @@ import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; class CommunityListViewWidget extends StatelessWidget { final List communities; + final Function(CommunityModel) onCommunitySelected; const CommunityListViewWidget({ Key? key, required this.communities, + required this.onCommunitySelected, }) : super(key: key); @override @@ -25,13 +27,18 @@ class CommunityListViewWidget extends StatelessWidget { mainAxisSpacing: size.width * 0.015, childAspectRatio: 361 / 239, // Aspect ratio based on container size ), - itemCount: communities.length + 1, // One additional item for the blank community + itemCount: communities.length + + 1, // One additional item for the blank community itemBuilder: (context, index) { // If the index is 0, display the blank community, otherwise display the normal ones if (index == 0) { return _buildBlankCommunityCard(size); } else { - return _buildCommunityCard(communities[index - 1], size); + return GestureDetector( + onTap: () => onCommunitySelected( + communities[index - 1]), // Trigger callback when tapped + child: _buildCommunityCard(communities[index - 1], size), + ); } }, ), @@ -60,7 +67,9 @@ class CommunityListViewWidget extends StatelessWidget { ), ), ), - SizedBox(height: size.height * 0.02), // Add spacing between container and text + SizedBox( + height: + size.height * 0.02), // Add spacing between container and text // Text saying "Blank" for the blank community Text( 'Blank', @@ -98,7 +107,9 @@ class CommunityListViewWidget extends StatelessWidget { ), ), ), - SizedBox(height: size.height * 0.02), // Add spacing between container and text + SizedBox( + height: + size.height * 0.02), // Add spacing between container and text // Community name text Text( community.name ?? 'Blank', // Display community name diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 4427234b..e774455c 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -30,6 +30,9 @@ class SpaceManagementPageState extends State { // Track whether to show the community list view or community structure bool showCommunityStructure = false; + // Selected community + CommunityModel? selectedCommunity; + // API instance final CommunitySpaceManagementApi _api = CommunitySpaceManagementApi(); @@ -80,20 +83,23 @@ class SpaceManagementPageState extends State { SidebarWidget( communities: communities, onCommunitySelected: (community) { - context.read().add( - LoadCommunityAndSpacesEvent(), // Re-fetch or perform community-specific actions - ); + }, ), - const SizedBox(width: 45), showCommunityStructure ? _buildCommunityStructureArea(context, screenSize) : CommunityListViewWidget( communities: communities, + onCommunitySelected: (community) { + setState(() { + selectedCommunity = community; + showCommunityStructure = true; + }); + }, ), ], ), - const GradientBorderWidget() + const GradientCanvasBorderWidget() ], ); } diff --git a/lib/pages/spaces_management/widgets/gradient_canvas_border_widget.dart b/lib/pages/spaces_management/widgets/gradient_canvas_border_widget.dart index 638434dc..2e156f06 100644 --- a/lib/pages/spaces_management/widgets/gradient_canvas_border_widget.dart +++ b/lib/pages/spaces_management/widgets/gradient_canvas_border_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -class GradientBorderWidget extends StatelessWidget { +class GradientCanvasBorderWidget extends StatelessWidget { final double top; final double bottom; final double left; From 7122ad5ac12cde4a379426c9d7fc26df1c434511 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 9 Oct 2024 10:52:11 +0400 Subject: [PATCH 036/122] Display selected community name --- .../view/spaces_management_page.dart | 30 +++++++++++++------ 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index e774455c..0a771cfe 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -82,9 +82,7 @@ class SpaceManagementPageState extends State { children: [ SidebarWidget( communities: communities, - onCommunitySelected: (community) { - - }, + onCommunitySelected: (community) {}, ), showCommunityStructure ? _buildCommunityStructureArea(context, screenSize) @@ -133,12 +131,26 @@ class SpaceManagementPageState extends State { ), ], ), - child: Text( - 'Community Structure', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Community Structure', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + if (selectedCommunity != null) ...[ + Text( + selectedCommunity!.name, // Show community name + style: TextStyle( + fontSize: 16, + color: ColorsManager.blackColor, // Slightly muted color + ), + ), + ] + ], ), ), // Use Expanded to ensure InteractiveViewer takes the available space From 4e7aba398302d9d724862255def21d721cdf092e Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 9 Oct 2024 10:58:37 +0400 Subject: [PATCH 037/122] blank community should also take to community structure --- .../spaces_management/view/community_list_view.dart | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/pages/spaces_management/view/community_list_view.dart b/lib/pages/spaces_management/view/community_list_view.dart index eafd4066..13546ab0 100644 --- a/lib/pages/spaces_management/view/community_list_view.dart +++ b/lib/pages/spaces_management/view/community_list_view.dart @@ -3,7 +3,7 @@ import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; class CommunityListViewWidget extends StatelessWidget { final List communities; - final Function(CommunityModel) onCommunitySelected; + final Function(CommunityModel?) onCommunitySelected; const CommunityListViewWidget({ Key? key, @@ -32,7 +32,11 @@ class CommunityListViewWidget extends StatelessWidget { itemBuilder: (context, index) { // If the index is 0, display the blank community, otherwise display the normal ones if (index == 0) { - return _buildBlankCommunityCard(size); + return GestureDetector( + onTap: () => + onCommunitySelected(null), // Pass null for blank community + child: _buildBlankCommunityCard(size), + ); } else { return GestureDetector( onTap: () => onCommunitySelected( @@ -71,7 +75,7 @@ class CommunityListViewWidget extends StatelessWidget { height: size.height * 0.02), // Add spacing between container and text // Text saying "Blank" for the blank community - Text( + const Text( 'Blank', textAlign: TextAlign.center, style: TextStyle( From f285189e42c4c46d4cd1404664aabaf801f8beb3 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 9 Oct 2024 11:04:56 +0400 Subject: [PATCH 038/122] added bloc for create community --- .../bloc/space_management_bloc.dart | 24 +++++++++++++++++++ .../bloc/space_management_event.dart | 16 +++++++++++++ .../bloc/space_management_state.dart | 4 +--- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index f8093208..f3ad73dc 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -67,4 +67,28 @@ class SpaceManagementBloc ) { // Handle space position update logic } + + void _onCreateCommunity( + CreateCommunityEvent event, + Emitter emit, + ) async { + try { + CommunityModel? community = await _api.createCommunity( + event.name, + event.description, + event.regionId, + ); + + if (community != null) { + List updatedCommunities = List.from((state as SpaceManagementLoaded).communities) + ..add(community); // Add the newly created community to the list + + emit(SpaceManagementLoaded(communities: updatedCommunities)); + } else { + emit(const SpaceManagementError('Error creating community')); + } + } catch (e) { + emit(SpaceManagementError('Error creating community: $e')); + } + } } diff --git a/lib/pages/spaces_management/bloc/space_management_event.dart b/lib/pages/spaces_management/bloc/space_management_event.dart index 048cc1ce..295c3ef9 100644 --- a/lib/pages/spaces_management/bloc/space_management_event.dart +++ b/lib/pages/spaces_management/bloc/space_management_event.dart @@ -44,3 +44,19 @@ class UpdateSpacePositionEvent extends SpaceManagementEvent { @override List get props => [index, newPosition]; } + + +class CreateCommunityEvent extends SpaceManagementEvent { + final String name; + final String description; + final String regionId; + + CreateCommunityEvent({ + required this.name, + required this.description, + required this.regionId, + }); + + @override + List get props => [name, description, regionId]; +} diff --git a/lib/pages/spaces_management/bloc/space_management_state.dart b/lib/pages/spaces_management/bloc/space_management_state.dart index 3f8c4af8..87a98ef2 100644 --- a/lib/pages/spaces_management/bloc/space_management_state.dart +++ b/lib/pages/spaces_management/bloc/space_management_state.dart @@ -1,6 +1,5 @@ import 'package:equatable/equatable.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; -import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; abstract class SpaceManagementState extends Equatable { const SpaceManagementState(); @@ -16,8 +15,7 @@ class SpaceManagementLoading extends SpaceManagementState {} class SpaceManagementLoaded extends SpaceManagementState { final List communities; - - const SpaceManagementLoaded({ required this.communities}); + const SpaceManagementLoaded({required this.communities}); @override List get props => [communities]; From 5d440a2b9eb03af55fe3c83b22ee36cb7a5557a5 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 9 Oct 2024 11:48:25 +0400 Subject: [PATCH 039/122] Fixed creating community --- .../bloc/space_management_bloc.dart | 21 ++++++++++++------ .../view/dialogs/create_community_dialog.dart | 9 ++++++-- .../widgets/sidebar_widget.dart | 22 +++++++++++++------ 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index f3ad73dc..00498e29 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -14,6 +14,7 @@ class SpaceManagementBloc on(_onLoadCommunityAndSpaces); on(_onCreateSpace); on(_onUpdateSpacePosition); + on(_onCreateCommunity); } void _onLoadCommunityAndSpaces( @@ -68,22 +69,28 @@ class SpaceManagementBloc // Handle space position update logic } - void _onCreateCommunity( + void _onCreateCommunity( CreateCommunityEvent event, Emitter emit, ) async { + final previousState = state; + emit(SpaceManagementLoading()); + try { - CommunityModel? community = await _api.createCommunity( + + CommunityModel? newCommunity = await _api.createCommunity( event.name, event.description, event.regionId, ); - if (community != null) { - List updatedCommunities = List.from((state as SpaceManagementLoaded).communities) - ..add(community); // Add the newly created community to the list - - emit(SpaceManagementLoaded(communities: updatedCommunities)); + if (newCommunity != null) { + if (previousState is SpaceManagementLoaded) { + final updatedCommunities = + List.from(previousState.communities) + ..add(newCommunity); + emit(SpaceManagementLoaded(communities: updatedCommunities)); + } } else { emit(const SpaceManagementError('Error creating community')); } diff --git a/lib/pages/spaces_management/view/dialogs/create_community_dialog.dart b/lib/pages/spaces_management/view/dialogs/create_community_dialog.dart index 2eb8107e..c7a96889 100644 --- a/lib/pages/spaces_management/view/dialogs/create_community_dialog.dart +++ b/lib/pages/spaces_management/view/dialogs/create_community_dialog.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; class CreateCommunityDialog extends StatefulWidget { - final Function(String) onCreateCommunity; + final Function(String name, String description, String regionId) + onCreateCommunity; const CreateCommunityDialog({super.key, required this.onCreateCommunity}); @@ -109,7 +110,11 @@ class CreateCommunityDialogState extends State { child: ElevatedButton( onPressed: () { if (enteredName.isNotEmpty) { - widget.onCreateCommunity(enteredName); + widget.onCreateCommunity( + enteredName, + "", + "42dc377a-1a39-4df9-b85a-b3817af88525", + ); Navigator.of(context).pop(); } }, diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 9314b10c..6ce2c4a7 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/common/search_bar.dart'; +import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/community_tile.dart'; @@ -30,14 +33,19 @@ class _SidebarWidgetState extends State { super.initState(); } - void _showCreateCommunityDialog() { + void _showCreateCommunityDialog(BuildContext parentContext) { showDialog( - context: context, + context: parentContext, builder: (context) => CreateCommunityDialog( - onCreateCommunity: (String communityName) { - setState(() { - // You can update the community list here when a new community is added - }); + onCreateCommunity: + (String communityName, String description, String regionId) { + parentContext.read().add( + CreateCommunityEvent( + name: communityName, + description: description, + regionId: regionId, + ), + ); }, ), ); @@ -122,7 +130,7 @@ class _SidebarWidgetState extends State { style: Theme.of(context).textTheme.titleMedium, ), GestureDetector( - onTap: _showCreateCommunityDialog, + onTap: () => _showCreateCommunityDialog(context), child: Container( width: 30, height: 30, From e35ea8e34a98ce0745076d6bfe10db20288d0950 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 9 Oct 2024 12:14:39 +0400 Subject: [PATCH 040/122] changed the hint text style in create space --- .../spaces_management/bloc/space_management_bloc.dart | 6 +++--- .../view/dialogs/create_space_dialog.dart | 10 +++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 00498e29..c8b683a3 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -1,3 +1,4 @@ + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; @@ -73,11 +74,10 @@ class SpaceManagementBloc CreateCommunityEvent event, Emitter emit, ) async { - final previousState = state; - emit(SpaceManagementLoading()); + final previousState = state; + emit(SpaceManagementLoading()); try { - CommunityModel? newCommunity = await _api.createCommunity( event.name, event.description, diff --git a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart index 4ad0b129..98babca8 100644 --- a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart @@ -79,6 +79,13 @@ class CreateSpaceDialogState extends State { ), decoration: InputDecoration( hintText: 'Please enter the name', + hintStyle: const TextStyle( + fontSize: 13, // Set your desired font size + color: ColorsManager.lightGrayColor + , // Optional: Change the color of the hint text + fontWeight: + FontWeight.w400, // Optional: Adjust the font weight + ), filled: true, fillColor: const Color(0xFFF5F6F7), border: OutlineInputBorder( @@ -119,7 +126,8 @@ class CreateSpaceDialogState extends State { child: DefaultButton( onPressed: () { if (enteredName.isNotEmpty) { - widget.onCreateSpace(enteredName, selectedIcon); // Pass name and icon back + widget.onCreateSpace( + enteredName, selectedIcon); // Pass name and icon back Navigator.of(context).pop(); // Close dialog } }, From d423552ddf92f8c6717e88e1d88a3bcc023307f5 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 9 Oct 2024 12:23:20 +0400 Subject: [PATCH 041/122] changed icon color --- .../spaces_management/view/dialogs/create_space_dialog.dart | 2 +- .../spaces_management/widgets/space_container_widget.dart | 3 ++- lib/utils/color_manager.dart | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart index 98babca8..8e766abb 100644 --- a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart @@ -80,7 +80,7 @@ class CreateSpaceDialogState extends State { decoration: InputDecoration( hintText: 'Please enter the name', hintStyle: const TextStyle( - fontSize: 13, // Set your desired font size + fontSize: 14, // Set your desired font size color: ColorsManager.lightGrayColor , // Optional: Change the color of the hint text fontWeight: diff --git a/lib/pages/spaces_management/widgets/space_container_widget.dart b/lib/pages/spaces_management/widgets/space_container_widget.dart index d7a6ee14..6657bd4a 100644 --- a/lib/pages/spaces_management/widgets/space_container_widget.dart +++ b/lib/pages/spaces_management/widgets/space_container_widget.dart @@ -38,7 +38,7 @@ class SpaceContainerWidget extends StatelessWidget { width: 40, height: 60, decoration: const BoxDecoration( - color: ColorsManager.secondaryColor, + color: ColorsManager.spaceColor, borderRadius: BorderRadius.only( topLeft: Radius.circular(15), bottomLeft: Radius.circular(15), @@ -47,6 +47,7 @@ class SpaceContainerWidget extends StatelessWidget { child: Center( child: SvgPicture.asset( icon, + color: ColorsManager.whiteColors, width: 24, height: 24, ), diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index e6dc4690..032c3153 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -12,7 +12,6 @@ abstract class ColorsManager { static const Color onSecondaryColor = Color(0xFF023DFE); static Color shadowBlackColor = Colors.black.withOpacity(0.2); - static Color dialogBlueTitle = Color(0xFF023DFE).withOpacity(0.6); static const Color primaryTextColor = Colors.black; @@ -50,6 +49,6 @@ abstract class ColorsManager { static const Color checkBoxFillColor = Color(0xFFF3F3F3); static const Color semiTransparentBlackColor = Color(0x3F000000); static const Color transparentColor = Color(0x00000000); - + static const Color spaceColor = Color(0xB2023DFE); } //0036E6 \ No newline at end of file From 5ef614f1e1cb2c9a9c7e22fb4ae65b3147433784 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 9 Oct 2024 12:37:59 +0400 Subject: [PATCH 042/122] removed center --- .../view/spaces_management_page.dart | 100 +++++++++--------- 1 file changed, 48 insertions(+), 52 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 0a771cfe..ad2fcf31 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -174,58 +174,54 @@ class SpaceManagementPageState extends State { size: Size(2000, 2000), // Explicit canvas size painter: CurvedLinePainter(connections), ), - Center( - child: spaces.isEmpty - ? AddSpaceButton( - onTap: () { - _showCreateSpaceDialog(screenSize); - }, - ) - : Stack( - children: spaces - .asMap() - .entries - .map((entry) => SpaceCardWidget( - index: entry.key, - screenSize: screenSize, - position: spaces[entry.key].position, - isHovered: - spaces[entry.key].isHovered, - onPanUpdate: - (int index, Offset delta) { - setState(() { - spaces[index].position += delta; - }); - }, - onHoverChanged: - (int index, bool isHovered) { - setState(() { - spaces[index].isHovered = - isHovered; - }); - }, - onButtonTap: (int index, - Offset newPosition, - String direction) { - _showCreateSpaceDialog( - screenSize, - position: spaces[index].position + - newPosition, - parentIndex: index, - direction: direction, - ); - }, - buildSpaceContainer: (int index) { - return SpaceContainerWidget( - index: index, - icon: spaces[index].icon, - name: spaces[index].name, - ); - }, - )) - .toList(), - ), - ), + + spaces.isEmpty + ? AddSpaceButton( + onTap: () { + _showCreateSpaceDialog(screenSize); + }, + ) + : Stack( + children: spaces + .asMap() + .entries + .map((entry) => SpaceCardWidget( + index: entry.key, + screenSize: screenSize, + position: spaces[entry.key].position, + isHovered: spaces[entry.key].isHovered, + onPanUpdate: (int index, Offset delta) { + setState(() { + spaces[index].position += delta; + }); + }, + onHoverChanged: + (int index, bool isHovered) { + setState(() { + spaces[index].isHovered = isHovered; + }); + }, + onButtonTap: (int index, + Offset newPosition, + String direction) { + _showCreateSpaceDialog( + screenSize, + position: spaces[index].position + + newPosition, + parentIndex: index, + direction: direction, + ); + }, + buildSpaceContainer: (int index) { + return SpaceContainerWidget( + index: index, + icon: spaces[index].icon, + name: spaces[index].name, + ); + }, + )) + .toList(), + ), ], ), ), From 1255a449356f2e5985685edab601b3fb1e1cda20 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 9 Oct 2024 12:38:37 +0400 Subject: [PATCH 043/122] add space button to center --- lib/pages/spaces_management/view/spaces_management_page.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index ad2fcf31..8ec604d9 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -176,11 +176,11 @@ class SpaceManagementPageState extends State { ), spaces.isEmpty - ? AddSpaceButton( + ? Center(child: AddSpaceButton( onTap: () { _showCreateSpaceDialog(screenSize); }, - ) + )) : Stack( children: spaces .asMap() From e09dc966e0d1281d389895f85afa9b780e3050d3 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 9 Oct 2024 12:52:34 +0400 Subject: [PATCH 044/122] fixed space clipping --- .../view/spaces_management_page.dart | 115 +++++++++--------- 1 file changed, 56 insertions(+), 59 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 8ec604d9..cb0318b0 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -162,69 +162,66 @@ class SpaceManagementPageState extends State { panEnabled: true, // Enable panning scaleEnabled: true, // Enable zooming child: Container( - width: 2000, // Large canvas - height: 2000, // Large canvas - color: - ColorsManager.transparentColor, // Transparent background - child: Stack( - clipBehavior: Clip.none, - children: [ - // Draw lines using a CustomPaint widget - CustomPaint( - size: Size(2000, 2000), // Explicit canvas size - painter: CurvedLinePainter(connections), - ), - - spaces.isEmpty - ? Center(child: AddSpaceButton( + color: ColorsManager + .transparentColor, // Transparent background + child: spaces.isEmpty + ? Center( + child: AddSpaceButton( onTap: () { _showCreateSpaceDialog(screenSize); }, - )) - : Stack( - children: spaces - .asMap() - .entries - .map((entry) => SpaceCardWidget( - index: entry.key, - screenSize: screenSize, - position: spaces[entry.key].position, - isHovered: spaces[entry.key].isHovered, - onPanUpdate: (int index, Offset delta) { - setState(() { - spaces[index].position += delta; - }); - }, - onHoverChanged: - (int index, bool isHovered) { - setState(() { - spaces[index].isHovered = isHovered; - }); - }, - onButtonTap: (int index, - Offset newPosition, - String direction) { - _showCreateSpaceDialog( - screenSize, - position: spaces[index].position + - newPosition, - parentIndex: index, - direction: direction, - ); - }, - buildSpaceContainer: (int index) { - return SpaceContainerWidget( - index: index, - icon: spaces[index].icon, - name: spaces[index].name, - ); - }, - )) - .toList(), ), - ], - ), - ), + ) + : Stack( + clipBehavior: Clip.none, + children: [ + CustomPaint( + size: Size(4000, 4000), + painter: CurvedLinePainter(connections), + ), + ...spaces.asMap().entries.map((entry) { + final space = entry.value; + return Positioned( + left: space.position.dx, + top: space.position.dy, + child: SpaceCardWidget( + index: entry.key, + screenSize: screenSize, + position: space.position, + isHovered: space.isHovered, + onPanUpdate: (int index, Offset delta) { + setState(() { + spaces[index].position += delta; + }); + }, + onHoverChanged: + (int index, bool isHovered) { + setState(() { + spaces[index].isHovered = isHovered; + }); + }, + onButtonTap: (int index, Offset newPosition, + String direction) { + _showCreateSpaceDialog( + screenSize, + position: spaces[index].position + + newPosition, + parentIndex: index, + direction: direction, + ); + }, + buildSpaceContainer: (int index) { + return SpaceContainerWidget( + index: index, + icon: spaces[index].icon, + name: spaces[index].name, + ); + }, + ), + ); + }), + ], + )), ), ), ], From b87bbb9a622f4a75cfe4a015830a2ef2fc42c664 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 9 Oct 2024 16:04:05 +0400 Subject: [PATCH 045/122] added circular border --- assets/icons/1_Gang_switch_icon.svg | 11 ++ assets/icons/2_Gang_Switch_icon.svg | 7 + assets/icons/3_Gang_switch_icon.svg | 9 + assets/icons/add_icon.svg | 3 + assets/icons/door_lock.svg | 28 +++ assets/icons/presence_sensor.svg | 19 ++ assets/icons/smart_gateway_icon.svg | 19 ++ assets/icons/smart_light_icon.svg | 23 +++ assets/icons/smart_thermostat_icon.svg | 21 ++ .../all_devices/models/device_type_model.dart | 22 +++ .../view/dialogs/create_space_dialog.dart | 184 +++++++++++++----- .../widgets/add_device_type_widget.dart | 135 +++++++++++++ lib/utils/constants/assets.dart | 10 + 13 files changed, 437 insertions(+), 54 deletions(-) create mode 100644 assets/icons/1_Gang_switch_icon.svg create mode 100644 assets/icons/2_Gang_Switch_icon.svg create mode 100644 assets/icons/3_Gang_switch_icon.svg create mode 100644 assets/icons/add_icon.svg create mode 100644 assets/icons/door_lock.svg create mode 100644 assets/icons/presence_sensor.svg create mode 100644 assets/icons/smart_gateway_icon.svg create mode 100644 assets/icons/smart_light_icon.svg create mode 100644 assets/icons/smart_thermostat_icon.svg create mode 100644 lib/pages/device_managment/all_devices/models/device_type_model.dart create mode 100644 lib/pages/spaces_management/widgets/add_device_type_widget.dart diff --git a/assets/icons/1_Gang_switch_icon.svg b/assets/icons/1_Gang_switch_icon.svg new file mode 100644 index 00000000..33a0755b --- /dev/null +++ b/assets/icons/1_Gang_switch_icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/icons/2_Gang_Switch_icon.svg b/assets/icons/2_Gang_Switch_icon.svg new file mode 100644 index 00000000..e72fa4f7 --- /dev/null +++ b/assets/icons/2_Gang_Switch_icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/3_Gang_switch_icon.svg b/assets/icons/3_Gang_switch_icon.svg new file mode 100644 index 00000000..45dea511 --- /dev/null +++ b/assets/icons/3_Gang_switch_icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/icons/add_icon.svg b/assets/icons/add_icon.svg new file mode 100644 index 00000000..e31d09ac --- /dev/null +++ b/assets/icons/add_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/door_lock.svg b/assets/icons/door_lock.svg new file mode 100644 index 00000000..2302d58d --- /dev/null +++ b/assets/icons/door_lock.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/presence_sensor.svg b/assets/icons/presence_sensor.svg new file mode 100644 index 00000000..f1bdfb90 --- /dev/null +++ b/assets/icons/presence_sensor.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/smart_gateway_icon.svg b/assets/icons/smart_gateway_icon.svg new file mode 100644 index 00000000..33207c58 --- /dev/null +++ b/assets/icons/smart_gateway_icon.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/smart_light_icon.svg b/assets/icons/smart_light_icon.svg new file mode 100644 index 00000000..a0013940 --- /dev/null +++ b/assets/icons/smart_light_icon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/smart_thermostat_icon.svg b/assets/icons/smart_thermostat_icon.svg new file mode 100644 index 00000000..d5782750 --- /dev/null +++ b/assets/icons/smart_thermostat_icon.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/pages/device_managment/all_devices/models/device_type_model.dart b/lib/pages/device_managment/all_devices/models/device_type_model.dart new file mode 100644 index 00000000..da228d3b --- /dev/null +++ b/lib/pages/device_managment/all_devices/models/device_type_model.dart @@ -0,0 +1,22 @@ +class DeviceTypeModel { + final String name; + final String icon; + + DeviceTypeModel({required this.name, required this.icon}); + + // Factory method for creating a new DeviceTypeModel from JSON + factory DeviceTypeModel.fromJson(Map json) { + return DeviceTypeModel( + name: json['name'], + icon: json['icon'], + ); + } + + // Convert this model to JSON format + Map toJson() { + return { + 'name': name, + 'icon': icon, + }; + } +} diff --git a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart index 8e766abb..faf31076 100644 --- a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.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/widgets/add_device_type_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class CreateSpaceDialog extends StatefulWidget { - // Add the onCreateSpace parameter as a required field final Function(String, String) onCreateSpace; const CreateSpaceDialog({super.key, required this.onCreateSpace}); @@ -16,8 +16,8 @@ class CreateSpaceDialog extends StatefulWidget { } class CreateSpaceDialogState extends State { - String selectedIcon = Assets.location; // Initially selected icon - String enteredName = ''; // Store entered space name + String selectedIcon = Assets.location; + String enteredName = ''; @override Widget build(BuildContext context) { @@ -25,7 +25,7 @@ class CreateSpaceDialogState extends State { title: const Text('Create New Space'), backgroundColor: ColorsManager.whiteColors, content: SizedBox( - width: 600, // Set width for the dialog + width: 600, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -44,7 +44,7 @@ class CreateSpaceDialogState extends State { ), ), SvgPicture.asset( - selectedIcon, // Display the selected icon here + selectedIcon, width: 60, height: 60, ), @@ -52,8 +52,7 @@ class CreateSpaceDialogState extends State { top: 2, left: 2, child: InkWell( - onTap: () => - _showIconSelectionDialog(), // Open the icon selection dialog + onTap: () => _showIconSelectionDialog(), child: Container( width: 20, height: 20, @@ -70,40 +69,121 @@ class CreateSpaceDialogState extends State { ), const SizedBox(width: 16), Expanded( - child: TextField( - onChanged: (value) { - enteredName = value; // Capture entered name - }, - style: TextStyle( - color: ColorsManager.blackColor, - ), - decoration: InputDecoration( - hintText: 'Please enter the name', - hintStyle: const TextStyle( - fontSize: 14, // Set your desired font size - color: ColorsManager.lightGrayColor - , // Optional: Change the color of the hint text - fontWeight: - FontWeight.w400, // Optional: Adjust the font weight + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Name input field + TextField( + onChanged: (value) { + enteredName = value; + }, + style: const TextStyle(color: Colors.black), + decoration: InputDecoration( + hintText: 'Please enter the name', + hintStyle: const TextStyle( + fontSize: 14, + color: ColorsManager.lightGrayColor, + fontWeight: FontWeight.w400), + filled: true, + fillColor: const Color(0xFFF5F6F7), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color( + 0xFFF5F6F7), // Light gray color when enabled (not focused) + width: 1.5, + ), + ), + // Set border when focused + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color( + 0xFFF5F6F7), // Primary color when focused + width: 1.5, + ), + ), + // Set border for disabled state + disabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color( + 0xFFF5F6F7), // Light gray for disabled state + width: 1.5, + ), + ), + // Set border for error state + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color( + 0xFFF5F6F7), // Red border when there's an error + width: 1.5, + ), + ), + // Border for focused error state + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color( + 0xFFF5F6F7), // Red border when there's an error and it's focused + width: 1.5, + ), + ), + ), ), - filled: true, - fillColor: const Color(0xFFF5F6F7), - border: OutlineInputBorder( - borderSide: const BorderSide(color: Color(0xFFF5F6F7)), - borderRadius: BorderRadius.circular(10), + const SizedBox(height: 16), + // Add Devices or Space Model Button + ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => AddDeviceWidget(), + ); + // Logic to assign devices or select a model + }, + style: ElevatedButton.styleFrom( + backgroundColor: ColorsManager.textFieldGreyColor, + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 20), + shape: RoundedRectangleBorder( + side: const BorderSide( + // Add border side here + color: Color( + 0xFFE5E5E5), // Define your desired border color + width: 2.0, // Define border width + ), + borderRadius: BorderRadius.circular(20)), + ), + child: Row( + mainAxisSize: MainAxisSize + .min, // Adjust the button size to fit the content + children: [ + SvgPicture.asset( + Assets + .addIcon, // Replace with your actual icon path + width: 20, // Set the size of the icon + height: 20, + ), + const SizedBox( + width: + 8), // Add spacing between the icon and text + const Text( + 'Add devices / Assign a space model', + style: TextStyle( + color: Colors.black, + fontSize: 16, + fontFamily: 'Aftika', + fontWeight: FontWeight.w400, + height: + 1.5, // Adjust line height for better spacing + ), + ), + const SizedBox(width: 8), + ], + ), ), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color(0xFFF5F6F7), - width: 1), // Default border - borderRadius: BorderRadius.circular(10), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: - BorderSide(color: Color(0xFFF5F6F7), width: 1), - ), - ), + ], ), ), ], @@ -126,9 +206,9 @@ class CreateSpaceDialogState extends State { child: DefaultButton( onPressed: () { if (enteredName.isNotEmpty) { - widget.onCreateSpace( - enteredName, selectedIcon); // Pass name and icon back - Navigator.of(context).pop(); // Close dialog + widget.onCreateSpace(enteredName, + selectedIcon); // Pass the name and icon back + Navigator.of(context).pop(); // Close the dialog } }, child: const Text('OK'), @@ -142,7 +222,6 @@ class CreateSpaceDialogState extends State { ); } - // Icon selection dialog void _showIconSelectionDialog() { showDialog( context: context, @@ -151,8 +230,8 @@ class CreateSpaceDialogState extends State { title: const Text('Select Icon'), backgroundColor: Colors.white, content: Container( - width: 500, // Width of the icon selection dialog - height: 200, // Height of the dialog + width: 500, + height: 200, padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: const Color(0xFFF5F6F7), @@ -160,24 +239,22 @@ class CreateSpaceDialogState extends State { ), child: GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 7, // Number of icons per row - crossAxisSpacing: 10, // Space between icons horizontally - mainAxisSpacing: 22, // Space between icons vertically + crossAxisCount: 7, + crossAxisSpacing: 10, + mainAxisSpacing: 22, ), itemCount: _iconList.length, itemBuilder: (BuildContext context, int index) { return GestureDetector( onTap: () { setState(() { - selectedIcon = - _iconList[index]; // Update the selected icon + selectedIcon = _iconList[index]; }); - Navigator.of(context) - .pop(); // Close the icon selection dialog + Navigator.of(context).pop(); }, child: SvgPicture.asset( _iconList[index], - width: 50, // Adjust size as needed + width: 50, height: 50, ), ); @@ -189,7 +266,6 @@ class CreateSpaceDialogState extends State { ); } - // Icon list containing SVG asset paths final List _iconList = [ Assets.location, Assets.villa, diff --git a/lib/pages/spaces_management/widgets/add_device_type_widget.dart b/lib/pages/spaces_management/widgets/add_device_type_widget.dart new file mode 100644 index 00000000..09abfadc --- /dev/null +++ b/lib/pages/spaces_management/widgets/add_device_type_widget.dart @@ -0,0 +1,135 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/pages/device_managment/all_devices/models/device_type_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class AddDeviceWidget extends StatefulWidget { + const AddDeviceWidget({Key? key}) : super(key: key); + + @override + _AddDeviceWidgetState createState() => _AddDeviceWidgetState(); +} + +// Create a static list of DeviceTypeModel +final List staticDeviceTypes = [ + DeviceTypeModel(name: 'Smart Light', icon: Assets.smartLightIcon), + DeviceTypeModel(name: 'Presence Sensor', icon: Assets.presenceSensor), + DeviceTypeModel(name: '3 Gang Smart switch', icon: Assets.Gang3SwitchIcon), + DeviceTypeModel(name: '2 Gang Smart switch', icon: Assets.Gang2SwitchIcon), + DeviceTypeModel(name: '1 Gang Smart switch', icon: Assets.Gang1SwitchIcon), + DeviceTypeModel(name: 'Smart Door Lock', icon: Assets.DoorLockIcon), + DeviceTypeModel(name: 'Smart Gateway', icon: Assets.SmartGatewayIcon) +]; + +class _AddDeviceWidgetState extends State { + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text('Add Devices'), + backgroundColor: ColorsManager.whiteColors, + content: Container( + width: 800, // Set width for the dialog + height: 600, // Set height for the dialog + color: Color(0xFFF4F4F4), + child: Column( + children: [ + const SizedBox(height: 16.0), + Expanded( + child: GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, // Adjust number of items per row + mainAxisSpacing: 10, + crossAxisSpacing: 10, + childAspectRatio: 1.5, + ), + itemCount: staticDeviceTypes.length, + itemBuilder: (context, index) { + final deviceType = staticDeviceTypes[index]; + return _buildDeviceTypeTile(deviceType); + }, + ), + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + // Logic to save or confirm selected devices + Navigator.of(context).pop(); + }, + child: const Text('OK'), + ), + ], + ); + } + + Widget _buildDeviceTypeTile(DeviceTypeModel deviceType) { + return SizedBox( + width: 90, // Set desired width + height: 120, // Set desired height + child: Card( + elevation: 2, + color: ColorsManager.whiteColors, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 60, // Width for the circular container + height: 60, // Height for the circular container + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: ColorsManager.textFieldGreyColor, // Border color + width: 2, // Border width + ), + color: ColorsManager.textFieldGreyColor, + ), + child: Center( + child: SvgPicture.asset( + deviceType.icon, + width: 40, + height: 40, + ), + ), + ), + const SizedBox(height: 8), + Text( + deviceType.name, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + onPressed: () { + setState(() {}); + }, + icon: const Icon(Icons.remove_circle_outline), + ), + IconButton( + onPressed: () {}, + icon: const Icon(Icons.add_circle_outline), + ), + ], + ), + ], + ), + ), + )); + } +} diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 7c01ddb4..5c605609 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -162,4 +162,14 @@ class Assets { static const String textFieldSearch = 'assets/icons/textfield_search_icon.svg'; static const String roundedAddIcon = 'assets/icons/rounded_add_icon.svg'; + static const String addIcon = 'assets/icons/add_icon.svg'; + static const String smartThermostatIcon = + 'assets/icons/smart_thermostat_icon.svg'; + static const String smartLightIcon = 'assets/icons/smart_light_icon.svg'; + static const String presenceSensor = 'assets/icons/presence_sensor.svg'; + static const String Gang3SwitchIcon = 'assets/icons/3_Gang_switch_icon.svg'; + static const String Gang2SwitchIcon = 'assets/icons/2_Gang_Switch_icon.svg'; + static const String Gang1SwitchIcon = 'assets/icons/1_Gang_switch_icon.svg'; + static const String DoorLockIcon = 'assets/icons/door_lock.svg'; + static const String SmartGatewayIcon = 'assets/icons/smart_gateway_icon.svg'; } From 29a147042822bb9025627fe20f8bae1d572c7b61 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 11 Oct 2024 10:35:14 +0400 Subject: [PATCH 046/122] updated device management --- .../bloc/space_management_event.dart | 6 +- .../model/space_response_model.dart | 1 - .../view/community_list_view.dart | 14 +- .../view/curved_line_painter.dart | 7 +- .../view/dialogs/create_community_dialog.dart | 3 +- .../view/dialogs/create_space_dialog.dart | 7 +- .../view/spaces_management_page.dart | 10 +- .../widgets/add_device_type_widget.dart | 167 ++++++++++-------- .../widgets/community_tile.dart | 4 +- .../widgets/counter_widget.dart | 67 +++++++ .../gradient_canvas_border_widget.dart | 4 +- .../widgets/plus_button_widget.dart | 10 +- .../widgets/sidebar_widget.dart | 2 +- .../widgets/space_card_widget.dart | 4 +- .../widgets/space_container_widget.dart | 4 +- .../widgets/space_tile_widget.dart | 4 +- lib/services/space_mana_api.dart | 14 +- lib/utils/color_manager.dart | 3 +- lib/utils/style.dart | 4 +- 19 files changed, 207 insertions(+), 128 deletions(-) create mode 100644 lib/pages/spaces_management/widgets/counter_widget.dart diff --git a/lib/pages/spaces_management/bloc/space_management_event.dart b/lib/pages/spaces_management/bloc/space_management_event.dart index 295c3ef9..7aaf84ae 100644 --- a/lib/pages/spaces_management/bloc/space_management_event.dart +++ b/lib/pages/spaces_management/bloc/space_management_event.dart @@ -17,7 +17,7 @@ class CreateSpaceEvent extends SpaceManagementEvent { final int? parentIndex; final String? direction; - CreateSpaceEvent({ + const CreateSpaceEvent({ required this.name, required this.icon, required this.position, @@ -39,7 +39,7 @@ class UpdateSpacePositionEvent extends SpaceManagementEvent { final int index; final Offset newPosition; - UpdateSpacePositionEvent(this.index, this.newPosition); + const UpdateSpacePositionEvent(this.index, this.newPosition); @override List get props => [index, newPosition]; @@ -51,7 +51,7 @@ class CreateCommunityEvent extends SpaceManagementEvent { final String description; final String regionId; - CreateCommunityEvent({ + const CreateCommunityEvent({ required this.name, required this.description, required this.regionId, diff --git a/lib/pages/spaces_management/model/space_response_model.dart b/lib/pages/spaces_management/model/space_response_model.dart index 11511481..34df3d30 100644 --- a/lib/pages/spaces_management/model/space_response_model.dart +++ b/lib/pages/spaces_management/model/space_response_model.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'space_model.dart'; diff --git a/lib/pages/spaces_management/view/community_list_view.dart b/lib/pages/spaces_management/view/community_list_view.dart index 13546ab0..a5049fbc 100644 --- a/lib/pages/spaces_management/view/community_list_view.dart +++ b/lib/pages/spaces_management/view/community_list_view.dart @@ -6,10 +6,10 @@ class CommunityListViewWidget extends StatelessWidget { final Function(CommunityModel?) onCommunitySelected; const CommunityListViewWidget({ - Key? key, + super.key, required this.communities, required this.onCommunitySelected, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -52,7 +52,7 @@ class CommunityListViewWidget extends StatelessWidget { // Build the blank community container Widget _buildBlankCommunityCard(Size size) { - return Container( + return SizedBox( width: size.width * .18, height: size.height * .22, child: Column( @@ -66,7 +66,7 @@ class CommunityListViewWidget extends StatelessWidget { color: Colors.white, borderRadius: BorderRadius.circular(5), border: Border.all( - color: Color(0xFFE5E5E5), + color: const Color(0xFFE5E5E5), width: 5, ), ), @@ -92,7 +92,7 @@ class CommunityListViewWidget extends StatelessWidget { // Build a single community container based on the format provided Widget _buildCommunityCard(CommunityModel community, Size size) { - return Container( + return SizedBox( width: size.width * .18, height: size.height * .22, child: Column( @@ -106,7 +106,7 @@ class CommunityListViewWidget extends StatelessWidget { color: Colors.white, borderRadius: BorderRadius.circular(5), border: Border.all( - color: Color(0xFFE5E5E5), + color: const Color(0xFFE5E5E5), width: 5, ), ), @@ -118,7 +118,7 @@ class CommunityListViewWidget extends StatelessWidget { Text( community.name ?? 'Blank', // Display community name textAlign: TextAlign.center, - style: TextStyle( + style: const TextStyle( color: Colors.black, fontSize: 18, fontFamily: 'Aftika', diff --git a/lib/pages/spaces_management/view/curved_line_painter.dart b/lib/pages/spaces_management/view/curved_line_painter.dart index 2d6c523a..4cbb7f3f 100644 --- a/lib/pages/spaces_management/view/curved_line_painter.dart +++ b/lib/pages/spaces_management/view/curved_line_painter.dart @@ -23,15 +23,14 @@ class CurvedLinePainter extends CustomPainter { for (var connection in connections) { // Ensure positions are valid before drawing lines - if (connection.startSpace.position == null || - connection.endSpace.position == null) { + if (connection.endSpace.position == null) { continue; } Offset start = connection.startSpace.position + - Offset(75, 60); // Center bottom of start space + const Offset(75, 60); // Center bottom of start space Offset end = connection.endSpace.position + - Offset(75, 0); // Center top of end space + const Offset(75, 0); // Center top of end space if (connection.direction == 'down') { // Curved line for down connections diff --git a/lib/pages/spaces_management/view/dialogs/create_community_dialog.dart b/lib/pages/spaces_management/view/dialogs/create_community_dialog.dart index c7a96889..a02ea572 100644 --- a/lib/pages/spaces_management/view/dialogs/create_community_dialog.dart +++ b/lib/pages/spaces_management/view/dialogs/create_community_dialog.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; class CreateCommunityDialog extends StatefulWidget { final Function(String name, String description, String regionId) @@ -120,7 +121,7 @@ class CreateCommunityDialogState extends State { }, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), - backgroundColor: const Color(0xFF023DFE), + backgroundColor: ColorsManager.secondaryColor, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), diff --git a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart index faf31076..a134792c 100644 --- a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart @@ -138,7 +138,7 @@ class CreateSpaceDialogState extends State { onPressed: () { showDialog( context: context, - builder: (context) => AddDeviceWidget(), + builder: (context) => const AddDeviceWidget(), ); // Logic to assign devices or select a model }, @@ -183,6 +183,7 @@ class CreateSpaceDialogState extends State { ], ), ), + ], ), ), @@ -211,9 +212,9 @@ class CreateSpaceDialogState extends State { Navigator.of(context).pop(); // Close the dialog } }, - child: const Text('OK'), - backgroundColor: const Color(0xFF023DFE), + backgroundColor: ColorsManager.secondaryColor, foregroundColor: Colors.white, + child: const Text('OK'), ), ), ], diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index cb0318b0..0076d9e0 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -18,6 +18,8 @@ import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; class SpaceManagementPage extends StatefulWidget { + const SpaceManagementPage({super.key}); + @override SpaceManagementPageState createState() => SpaceManagementPageState(); } @@ -127,14 +129,14 @@ class SpaceManagementPageState extends State { color: ColorsManager.shadowBlackColor, // Subtle shadow spreadRadius: 0, // No spread blurRadius: 8, // Softer shadow edges - offset: Offset(0, 4), // Shadow only on the bottom + offset: const Offset(0, 4), // Shadow only on the bottom ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( + const Text( 'Community Structure', style: TextStyle( fontSize: 24, @@ -144,7 +146,7 @@ class SpaceManagementPageState extends State { if (selectedCommunity != null) ...[ Text( selectedCommunity!.name, // Show community name - style: TextStyle( + style: const TextStyle( fontSize: 16, color: ColorsManager.blackColor, // Slightly muted color ), @@ -176,7 +178,7 @@ class SpaceManagementPageState extends State { clipBehavior: Clip.none, children: [ CustomPaint( - size: Size(4000, 4000), + size: const Size(4000, 4000), painter: CurvedLinePainter(connections), ), ...spaces.asMap().entries.map((entry) { diff --git a/lib/pages/spaces_management/widgets/add_device_type_widget.dart b/lib/pages/spaces_management/widgets/add_device_type_widget.dart index 09abfadc..6f7f750d 100644 --- a/lib/pages/spaces_management/widgets/add_device_type_widget.dart +++ b/lib/pages/spaces_management/widgets/add_device_type_widget.dart @@ -1,11 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.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/device_managment/all_devices/models/device_type_model.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/counter_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class AddDeviceWidget extends StatefulWidget { - const AddDeviceWidget({Key? key}) : super(key: key); + const AddDeviceWidget({super.key}); @override _AddDeviceWidgetState createState() => _AddDeviceWidgetState(); @@ -25,45 +28,68 @@ final List staticDeviceTypes = [ class _AddDeviceWidgetState extends State { @override Widget build(BuildContext context) { + Size size = MediaQuery.of(context).size; + return AlertDialog( title: const Text('Add Devices'), backgroundColor: ColorsManager.whiteColors, content: Container( - width: 800, // Set width for the dialog - height: 600, // Set height for the dialog - color: Color(0xFFF4F4F4), + width: size.width * 0.65, // Set width for the dialog + height: size.height * 0.57, // Set height for the dialog + color: ColorsManager.textFieldGreyColor, child: Column( children: [ const SizedBox(height: 16.0), Expanded( - child: GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, // Adjust number of items per row - mainAxisSpacing: 10, - crossAxisSpacing: 10, - childAspectRatio: 1.5, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 20.0), // Add horizontal padding + child: GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 6, // Display 6 items in a row + mainAxisSpacing: 10, + crossAxisSpacing: 10, + childAspectRatio: 0.7, // Adjust the aspect ratio + ), + itemCount: staticDeviceTypes.length, + itemBuilder: (context, index) { + final deviceType = staticDeviceTypes[index]; + return _buildDeviceTypeTile(deviceType); + }, ), - itemCount: staticDeviceTypes.length, - itemBuilder: (context, index) { - final deviceType = staticDeviceTypes[index]; - return _buildDeviceTypeTile(deviceType); - }, ), ), ], ), ), actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - // Logic to save or confirm selected devices - Navigator.of(context).pop(); - }, - child: const Text('OK'), + Row( + mainAxisAlignment: MainAxisAlignment + .spaceBetween, // Align cancel to the left and continue to the right + children: [ + SizedBox( + width: 200, // Define a specific width for the button + child: DefaultButton( + onPressed: () { + Navigator.of(context).pop(); + }, + backgroundColor: ColorsManager.boxColor, + foregroundColor: ColorsManager.blackColor, + child: const Text('Cancel'), + ), + ), + SizedBox( + width: 200, // Define a specific width for the button + child: DefaultButton( + onPressed: () { + Navigator.of(context).pop(); + }, + backgroundColor: ColorsManager.secondaryColor, + foregroundColor: Colors.white, + child: const Text('Continue'), + ), + ), + ], ), ], ); @@ -71,65 +97,52 @@ class _AddDeviceWidgetState extends State { Widget _buildDeviceTypeTile(DeviceTypeModel deviceType) { return SizedBox( - width: 90, // Set desired width - height: 120, // Set desired height - child: Card( - elevation: 2, - color: ColorsManager.whiteColors, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: 60, // Width for the circular container - height: 60, // Height for the circular container - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: ColorsManager.textFieldGreyColor, // Border color - width: 2, // Border width - ), - color: ColorsManager.textFieldGreyColor, - ), - child: Center( - child: SvgPicture.asset( - deviceType.icon, - width: 40, - height: 40, - ), + width: 90, + height: 150, // Increase height if needed + child: Card( + elevation: 2, + color: ColorsManager.whiteColors, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Fixed height container for the icon + Container( + height: 70, // Fixed height for the icon + child: Center( + child: SvgPicture.asset( + deviceType.icon, + width: 40, + height: 40, ), ), - const SizedBox(height: 8), - Text( + ), + const SizedBox(height: 8), + // Fixed height container for the name + Container( + height: 35, // Fixed height for the text (adjust as needed) + child: Text( deviceType.name, style: const TextStyle( - fontSize: 14, + fontSize: 12, fontWeight: FontWeight.w500, ), + textAlign: TextAlign.center, + maxLines: 2, // Allow up to 2 lines for long names + overflow: TextOverflow.ellipsis, // Handle overflow ), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton( - onPressed: () { - setState(() {}); - }, - icon: const Icon(Icons.remove_circle_outline), - ), - IconButton( - onPressed: () {}, - icon: const Icon(Icons.add_circle_outline), - ), - ], - ), - ], - ), + ), + const SizedBox(height: 8), + // The custom counter widget aligned at the bottom + CounterWidget(), + ], ), - )); + ), + ), + ); } } diff --git a/lib/pages/spaces_management/widgets/community_tile.dart b/lib/pages/spaces_management/widgets/community_tile.dart index 2943a6e5..bfc111dc 100644 --- a/lib/pages/spaces_management/widgets/community_tile.dart +++ b/lib/pages/spaces_management/widgets/community_tile.dart @@ -8,12 +8,12 @@ class CommunityTile extends StatefulWidget { final Function(String, bool) onExpansionChanged; const CommunityTile({ - Key? key, + super.key, required this.title, required this.initiallyExpanded, required this.onExpansionChanged, this.children, - }) : super(key: key); + }); @override _CommunityTileState createState() => _CommunityTileState(); diff --git a/lib/pages/spaces_management/widgets/counter_widget.dart b/lib/pages/spaces_management/widgets/counter_widget.dart new file mode 100644 index 00000000..5b1bfbf0 --- /dev/null +++ b/lib/pages/spaces_management/widgets/counter_widget.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class CounterWidget extends StatefulWidget { + @override + _CounterWidgetState createState() => _CounterWidgetState(); +} + +class _CounterWidgetState extends State { + int _counter = 0; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: ColorsManager + .counterBackgroundColor, // Background color for the counter + borderRadius: BorderRadius.circular(20), // Rounded corners + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Decrement button + GestureDetector( + onTap: () { + setState(() { + if (_counter > 0) { + _counter--; + } + }); + }, + child: Icon( + Icons.remove, + color: ColorsManager.spaceColor, // Blue color + size: 18, // Icon size + ), + ), + const SizedBox(width: 8), + // Counter value display + Text( + '$_counter', + style: const TextStyle( + color: ColorsManager.spaceColor, // Blue color + fontSize: 16, + ), + ), + const SizedBox(width: 8), + // Increment button + GestureDetector( + onTap: () { + setState(() { + _counter++; + }); + }, + child: const Icon( + Icons.add, + color: ColorsManager.spaceColor, // Blue color + size: 18, // Icon size + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/spaces_management/widgets/gradient_canvas_border_widget.dart b/lib/pages/spaces_management/widgets/gradient_canvas_border_widget.dart index 2e156f06..e1d4e11b 100644 --- a/lib/pages/spaces_management/widgets/gradient_canvas_border_widget.dart +++ b/lib/pages/spaces_management/widgets/gradient_canvas_border_widget.dart @@ -8,12 +8,12 @@ class GradientCanvasBorderWidget extends StatelessWidget { final double width; const GradientCanvasBorderWidget({ - Key? key, + super.key, this.top = 0, this.bottom = 0, this.left = 300, this.width = 8, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/pages/spaces_management/widgets/plus_button_widget.dart b/lib/pages/spaces_management/widgets/plus_button_widget.dart index ee2faad6..755dad92 100644 --- a/lib/pages/spaces_management/widgets/plus_button_widget.dart +++ b/lib/pages/spaces_management/widgets/plus_button_widget.dart @@ -9,13 +9,13 @@ class PlusButtonWidget extends StatelessWidget { final Function(int index, Offset newPosition, String direction) onButtonTap; const PlusButtonWidget({ - Key? key, + super.key, required this.index, required this.direction, required this.offset, required this.screenSize, required this.onButtonTap, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -27,13 +27,13 @@ class PlusButtonWidget extends StatelessWidget { Offset newPosition; switch (direction) { case 'left': - newPosition = Offset(-200, 0); + newPosition = const Offset(-200, 0); break; case 'right': - newPosition = Offset(200, 0); + newPosition = const Offset(200, 0); break; case 'down': - newPosition = Offset(0, 150); + newPosition = const Offset(0, 150); break; default: newPosition = Offset.zero; diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 6ce2c4a7..3a3f062f 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -17,7 +17,7 @@ class SidebarWidget extends StatefulWidget { final Function(String)? onCommunitySelected; final List communities; - SidebarWidget({this.onCommunitySelected, required this.communities}); + const SidebarWidget({super.key, this.onCommunitySelected, required this.communities}); @override _SidebarWidgetState createState() => _SidebarWidgetState(); diff --git a/lib/pages/spaces_management/widgets/space_card_widget.dart b/lib/pages/spaces_management/widgets/space_card_widget.dart index ff9909c1..5453c227 100644 --- a/lib/pages/spaces_management/widgets/space_card_widget.dart +++ b/lib/pages/spaces_management/widgets/space_card_widget.dart @@ -13,7 +13,7 @@ class SpaceCardWidget extends StatelessWidget { final Widget Function(int index) buildSpaceContainer; const SpaceCardWidget({ - Key? key, + super.key, required this.index, required this.screenSize, required this.position, @@ -22,7 +22,7 @@ class SpaceCardWidget extends StatelessWidget { required this.onHoverChanged, required this.onButtonTap, required this.buildSpaceContainer, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/pages/spaces_management/widgets/space_container_widget.dart b/lib/pages/spaces_management/widgets/space_container_widget.dart index 6657bd4a..be6e43b4 100644 --- a/lib/pages/spaces_management/widgets/space_container_widget.dart +++ b/lib/pages/spaces_management/widgets/space_container_widget.dart @@ -9,11 +9,11 @@ class SpaceContainerWidget extends StatelessWidget { final String name; const SpaceContainerWidget({ - Key? key, + super.key, required this.index, required this.icon, required this.name, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/lib/pages/spaces_management/widgets/space_tile_widget.dart b/lib/pages/spaces_management/widgets/space_tile_widget.dart index 8c48d568..98068e7d 100644 --- a/lib/pages/spaces_management/widgets/space_tile_widget.dart +++ b/lib/pages/spaces_management/widgets/space_tile_widget.dart @@ -8,12 +8,12 @@ class SpaceTile extends StatefulWidget { final List? children; const SpaceTile({ - Key? key, + super.key, required this.title, required this.initiallyExpanded, required this.onExpansionChanged, this.children, - }) : super(key: key); + }); @override _SpaceTileState createState() => _SpaceTileState(); diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index 4a60c8c3..2498dc51 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -16,15 +16,11 @@ class CommunitySpaceManagementApi { List jsonData = json['data']; // Check if jsonData is actually a List - if (jsonData is List) { - List communityList = jsonData.map((jsonItem) { - return CommunityModel.fromJson(jsonItem); - }).toList(); - return communityList; - } else { - throw Exception('Expected a list but got something else.'); - } - }, + List communityList = jsonData.map((jsonItem) { + return CommunityModel.fromJson(jsonItem); + }).toList(); + return communityList; + }, ); return response; } catch (e) { diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index 032c3153..86510efc 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -12,7 +12,7 @@ abstract class ColorsManager { static const Color onSecondaryColor = Color(0xFF023DFE); static Color shadowBlackColor = Colors.black.withOpacity(0.2); - static Color dialogBlueTitle = Color(0xFF023DFE).withOpacity(0.6); + static Color dialogBlueTitle = const Color(0xFF023DFE).withOpacity(0.6); static const Color primaryTextColor = Colors.black; @@ -50,5 +50,6 @@ abstract class ColorsManager { static const Color semiTransparentBlackColor = Color(0x3F000000); static const Color transparentColor = Color(0x00000000); static const Color spaceColor = Color(0xB2023DFE); + static const Color counterBackgroundColor = Color(0xCCF4F4F4); } //0036E6 \ No newline at end of file diff --git a/lib/utils/style.dart b/lib/utils/style.dart index bdc4eb3f..f4e84b50 100644 --- a/lib/utils/style.dart +++ b/lib/utils/style.dart @@ -21,11 +21,11 @@ InputDecoration? textBoxDecoration({bool suffixIcon = false}) => borderSide: BorderSide.none, // Remove the underline ), errorBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.red, width: 2), + borderSide: const BorderSide(color: Colors.red, width: 2), borderRadius: BorderRadius.circular(8), ), focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.red, width: 2), + borderSide: const BorderSide(color: Colors.red, width: 2), borderRadius: BorderRadius.circular(8), ), ); From cd957ed1c739d30f91e95c5cb5ac1a8dc3af1b41 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 11 Oct 2024 10:46:30 +0400 Subject: [PATCH 047/122] Removed community view --- .../view/community_list_view.dart | 132 ------------------ .../view/spaces_management_page.dart | 20 +-- .../widgets/add_device_type_widget.dart | 1 - .../widgets/sidebar_widget.dart | 8 +- 4 files changed, 12 insertions(+), 149 deletions(-) delete mode 100644 lib/pages/spaces_management/view/community_list_view.dart diff --git a/lib/pages/spaces_management/view/community_list_view.dart b/lib/pages/spaces_management/view/community_list_view.dart deleted file mode 100644 index a5049fbc..00000000 --- a/lib/pages/spaces_management/view/community_list_view.dart +++ /dev/null @@ -1,132 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; - -class CommunityListViewWidget extends StatelessWidget { - final List communities; - final Function(CommunityModel?) onCommunitySelected; - - const CommunityListViewWidget({ - super.key, - required this.communities, - required this.onCommunitySelected, - }); - - @override - Widget build(BuildContext context) { - Size size = MediaQuery.of(context).size; - - // Increase the item count by 1 to include the blank community - return Expanded( - child: Container( - color: Colors.white, - child: GridView.builder( - padding: const EdgeInsets.all(16.0), - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, // Three containers per row - crossAxisSpacing: size.height * 0.041, - mainAxisSpacing: size.width * 0.015, - childAspectRatio: 361 / 239, // Aspect ratio based on container size - ), - itemCount: communities.length + - 1, // One additional item for the blank community - itemBuilder: (context, index) { - // If the index is 0, display the blank community, otherwise display the normal ones - if (index == 0) { - return GestureDetector( - onTap: () => - onCommunitySelected(null), // Pass null for blank community - child: _buildBlankCommunityCard(size), - ); - } else { - return GestureDetector( - onTap: () => onCommunitySelected( - communities[index - 1]), // Trigger callback when tapped - child: _buildCommunityCard(communities[index - 1], size), - ); - } - }, - ), - ), - ); - } - - // Build the blank community container - Widget _buildBlankCommunityCard(Size size) { - return SizedBox( - width: size.width * .18, - height: size.height * .22, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // Inner blank container with white background and border - Container( - width: size.width * .18, - height: size.height * .16, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(5), - border: Border.all( - color: const Color(0xFFE5E5E5), - width: 5, - ), - ), - ), - SizedBox( - height: - size.height * 0.02), // Add spacing between container and text - // Text saying "Blank" for the blank community - const Text( - 'Blank', - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.black, - fontSize: 18, - fontFamily: 'Aftika', - fontWeight: FontWeight.w400, - ), - ), - ], - ), - ); - } - - // Build a single community container based on the format provided - Widget _buildCommunityCard(CommunityModel community, Size size) { - return SizedBox( - width: size.width * .18, - height: size.height * .22, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - // Inner container with white background and border - Container( - width: size.width * .18, - height: size.height * .16, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(5), - border: Border.all( - color: const Color(0xFFE5E5E5), - width: 5, - ), - ), - ), - SizedBox( - height: - size.height * 0.02), // Add spacing between container and text - // Community name text - Text( - community.name ?? 'Blank', // Display community name - textAlign: TextAlign.center, - style: const TextStyle( - color: Colors.black, - fontSize: 18, - fontFamily: 'Aftika', - fontWeight: FontWeight.w400, - ), - ), - ], - ), - ); - } -} diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 0076d9e0..ef5f4be5 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -6,7 +6,6 @@ import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event. import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; -import 'package:syncrow_web/pages/spaces_management/view/community_list_view.dart'; import 'package:syncrow_web/pages/spaces_management/view/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/gradient_canvas_border_widget.dart'; @@ -30,7 +29,6 @@ class SpaceManagementPageState extends State { List connections = []; // Track whether to show the community list view or community structure - bool showCommunityStructure = false; // Selected community CommunityModel? selectedCommunity; @@ -84,19 +82,13 @@ class SpaceManagementPageState extends State { children: [ SidebarWidget( communities: communities, - onCommunitySelected: (community) {}, + onCommunitySelected: (community) { + setState(() { + selectedCommunity = community; // Set the selected community + }); + }, ), - showCommunityStructure - ? _buildCommunityStructureArea(context, screenSize) - : CommunityListViewWidget( - communities: communities, - onCommunitySelected: (community) { - setState(() { - selectedCommunity = community; - showCommunityStructure = true; - }); - }, - ), + _buildCommunityStructureArea(context, screenSize) ], ), const GradientCanvasBorderWidget() diff --git a/lib/pages/spaces_management/widgets/add_device_type_widget.dart b/lib/pages/spaces_management/widgets/add_device_type_widget.dart index 6f7f750d..be0eb735 100644 --- a/lib/pages/spaces_management/widgets/add_device_type_widget.dart +++ b/lib/pages/spaces_management/widgets/add_device_type_widget.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.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/device_managment/all_devices/models/device_type_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/counter_widget.dart'; diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 3a3f062f..bf0164e2 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -14,10 +14,11 @@ import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/style.dart'; class SidebarWidget extends StatefulWidget { - final Function(String)? onCommunitySelected; + final Function(CommunityModel)? onCommunitySelected; final List communities; - const SidebarWidget({super.key, this.onCommunitySelected, required this.communities}); + const SidebarWidget( + {super.key, this.onCommunitySelected, required this.communities}); @override _SidebarWidgetState createState() => _SidebarWidgetState(); @@ -185,6 +186,9 @@ class _SidebarWidgetState extends State { debugPrint( 'CommunityTile onExpansionChanged called for $title, expanded: $expanded'); _handleExpansionChange(community.uuid, expanded); + if (widget.onCommunitySelected != null) { + widget.onCommunitySelected!(community); // Pass the entire community + } }, children: hasChildren ? community.spaces.map((space) => _buildSpaceTile(space)).toList() From 56c4c858bed0144cbe79346addbb2c34ec9cfdf4 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 11 Oct 2024 13:21:30 +0400 Subject: [PATCH 048/122] expansion only on clicking icon --- lib/common/custom_expansion_tile.dart | 84 +++++++++++-------- .../widgets/community_tile.dart | 36 +++----- .../widgets/sidebar_widget.dart | 28 +++++-- .../widgets/space_tile_widget.dart | 1 + 4 files changed, 83 insertions(+), 66 deletions(-) diff --git a/lib/common/custom_expansion_tile.dart b/lib/common/custom_expansion_tile.dart index f637f118..e5dc2e16 100644 --- a/lib/common/custom_expansion_tile.dart +++ b/lib/common/custom_expansion_tile.dart @@ -5,8 +5,10 @@ class CustomExpansionTile extends StatefulWidget { final String title; final List? children; final bool initiallyExpanded; + final bool isSelected; // Add this to track selection final bool? isExpanded; // External control over expansion final ValueChanged? onExpansionChanged; // Notify when expansion changes + final VoidCallback? onItemSelected; // Callback for selecting the item CustomExpansionTile({ required this.title, @@ -14,6 +16,8 @@ class CustomExpansionTile extends StatefulWidget { this.initiallyExpanded = false, this.isExpanded, // Allow external control over expansion this.onExpansionChanged, // Notify when expansion changes + this.onItemSelected, // Trigger item selection when name is tapped + required this.isSelected, // Add this to initialize selection state }); @override @@ -22,7 +26,6 @@ class CustomExpansionTile extends StatefulWidget { class CustomExpansionTileState extends State { bool _isExpanded = false; // Local expansion state - bool _isChecked = false; // Local checkbox state @override void initState() { @@ -51,57 +54,66 @@ class CustomExpansionTileState extends State { Widget build(BuildContext context) { return Column( children: [ - // The main clickable row for the expansion tile - InkWell( - onTap: () { - setState(() { - _isExpanded = !_isExpanded; - widget.onExpansionChanged?.call(_isExpanded); - }); - }, - child: Row( - children: [ - // Checkbox with independent state management - Checkbox( - value: _isChecked, - onChanged: (bool? value) { + Row( + children: [ + // Checkbox with independent state management + Checkbox( + value: false, + onChanged: (bool? value) { + setState(() {}); + }, + side: WidgetStateBorderSide.resolveWith((states) { + return const BorderSide(color: ColorsManager.grayBorder); + }), + fillColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return ColorsManager.grayBorder; + } else { + return ColorsManager.checkBoxFillColor; + } + }), + checkColor: ColorsManager.whiteColors, + ), + // Expand/collapse icon, now wrapped in a GestureDetector for specific onTap + if (widget.children != null && widget.children!.isNotEmpty) + GestureDetector( + onTap: () { setState(() { - _isChecked = value ?? false; + _isExpanded = !_isExpanded; + widget.onExpansionChanged?.call(_isExpanded); }); }, - side: MaterialStateBorderSide.resolveWith((states) { - return const BorderSide(color: ColorsManager.grayBorder); - }), - fillColor: MaterialStateProperty.resolveWith((states) { - if (states.contains(MaterialState.selected)) { - return ColorsManager.grayBorder; - } else { - return ColorsManager.checkBoxFillColor; - } - }), - checkColor: ColorsManager.whiteColors, - ), - // Show the expand/collapse icon - if (widget.children != null && widget.children!.isNotEmpty) - Icon( + child: Icon( _isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right, color: Colors.grey, size: 16.0, // Adjusted size for better alignment ), - // The title text with dynamic styling - Expanded( + ), + // The title text, wrapped in GestureDetector to handle selection + Expanded( + child: GestureDetector( + onTap: () { + // Triggerxq the onItemSelected callback when the name is tapped + if (widget.onItemSelected != null) { + widget.onItemSelected!(); + } + debugPrint('${widget.title} ${widget.isSelected} tapped for selection'); + }, child: Text( _capitalizeFirstLetter(widget.title), style: TextStyle( - color:ColorsManager.lightGrayColor, + color: widget.isSelected + ? Colors.black // Change color to black when selected + : ColorsManager + .lightGrayColor, // Gray when not selected fontWeight: FontWeight.w400, ), ), ), - ], - ), + ), + ], ), // The expanded section (children) that shows when the tile is expanded if (_isExpanded && diff --git a/lib/pages/spaces_management/widgets/community_tile.dart b/lib/pages/spaces_management/widgets/community_tile.dart index bfc111dc..5b9f79f2 100644 --- a/lib/pages/spaces_management/widgets/community_tile.dart +++ b/lib/pages/spaces_management/widgets/community_tile.dart @@ -1,45 +1,35 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/common/custom_expansion_tile.dart'; -class CommunityTile extends StatefulWidget { +class CommunityTile extends StatelessWidget { final String title; final List? children; - final bool initiallyExpanded; + final bool isExpanded; + final bool isSelected; final Function(String, bool) onExpansionChanged; + final Function() onItemSelected; const CommunityTile({ super.key, required this.title, - required this.initiallyExpanded, + required this.isExpanded, required this.onExpansionChanged, + required this.onItemSelected, + required this.isSelected, this.children, }); - @override - _CommunityTileState createState() => _CommunityTileState(); -} - -class _CommunityTileState extends State { - late bool _isExpanded; - - @override - void initState() { - super.initState(); - _isExpanded = widget.initiallyExpanded; - } - @override Widget build(BuildContext context) { return CustomExpansionTile( - title: widget.title, - initiallyExpanded: _isExpanded, + title: title, + initiallyExpanded: isExpanded, + isSelected: isSelected, onExpansionChanged: (bool expanded) { - setState(() { - _isExpanded = expanded; - }); - widget.onExpansionChanged(widget.title, expanded); + onExpansionChanged(title, expanded); }, - children: widget.children ?? [], + onItemSelected: onItemSelected, + children: children ?? [], ); } } diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index bf0164e2..ffc15db2 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -175,20 +175,34 @@ class _SidebarWidgetState extends State { Widget _buildCommunityTile(CommunityModel community) { bool hasChildren = community.spaces.isNotEmpty; - bool isSelectedCommunity = _selectedCommunityUuid == community.uuid; + bool isSelectedCommunity = _selectedCommunityUuid == + community.uuid; // Check if this community is selected - debugPrint( - 'Building CommunityTile for ${community.name}, hasChildren: $hasChildren'); + debugPrint('Building CommunityTile for ${community.name} with UUID: ${community.uuid}'); + debugPrint('Currently selected community UUID: $_selectedCommunityUuid'); + debugPrint('Is selected: $isSelectedCommunity'); + return CommunityTile( title: community.name, - initiallyExpanded: isSelectedCommunity, + isSelected: isSelectedCommunity, + isExpanded: false, + onItemSelected: () { + setState(() { + _selectedSpaceUuid = community.uuid; // Update the selected community + debugPrint( + 'Selected community: ${community.name}, UUID: ${community.uuid}'); + debugPrint( + 'Updated selected community UUID: $_selectedCommunityUuid'); + }); + + if (widget.onCommunitySelected != null) { + widget.onCommunitySelected!(community); // Pass the entire community + } + }, onExpansionChanged: (String title, bool expanded) { debugPrint( 'CommunityTile onExpansionChanged called for $title, expanded: $expanded'); _handleExpansionChange(community.uuid, expanded); - if (widget.onCommunitySelected != null) { - widget.onCommunitySelected!(community); // Pass the entire community - } }, children: hasChildren ? community.spaces.map((space) => _buildSpaceTile(space)).toList() diff --git a/lib/pages/spaces_management/widgets/space_tile_widget.dart b/lib/pages/spaces_management/widgets/space_tile_widget.dart index 98068e7d..e40fc5ce 100644 --- a/lib/pages/spaces_management/widgets/space_tile_widget.dart +++ b/lib/pages/spaces_management/widgets/space_tile_widget.dart @@ -31,6 +31,7 @@ class _SpaceTileState extends State { @override Widget build(BuildContext context) { return CustomExpansionTile( + isSelected: false, title: widget.title, initiallyExpanded: _isExpanded, onExpansionChanged: (bool expanded) { From 220cfdbd27c1199be6cbdc6f04a561b03f465b7c Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 14 Oct 2024 09:44:22 +0400 Subject: [PATCH 049/122] fixing canvas issue --- .../view/spaces_management_page.dart | 36 ++++++-- .../widgets/space_card_widget.dart | 85 +++++++++---------- 2 files changed, 70 insertions(+), 51 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index ef5f4be5..d4176610 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; @@ -27,6 +29,8 @@ class SpaceManagementPageState extends State { // Store created spaces List spaces = []; List connections = []; + double canvasWidth = 4000; // Initial canvas width + double canvasHeight = 4000; // Initial canvas height // Track whether to show the community list view or community structure @@ -44,6 +48,23 @@ class SpaceManagementPageState extends State { super.initState(); } + void updateCanvasSize() { + double maxX = 0; + double maxY = 0; + + for (var space in spaces) { + maxX = max(maxX, space.position.dx + 150); // Width of space + maxY = max(maxY, space.position.dy + 60); // Height of space + } + + setState(() { + canvasWidth = max(canvasWidth, maxX + 200); // Ensure padding + canvasHeight = max(canvasHeight, maxY + 200); // Ensure padding + }); + + print("Updated canvas size: $canvasWidth x $canvasHeight"); + } + @override Widget build(BuildContext context) { Size screenSize = MediaQuery.of(context).size; @@ -100,7 +121,7 @@ class SpaceManagementPageState extends State { return Expanded( child: Container( decoration: const BoxDecoration( - color: ColorsManager.whiteColors, + color: ColorsManager.blackColor, border: Border( left: BorderSide( color: ColorsManager.whiteColors, @@ -115,7 +136,7 @@ class SpaceManagementPageState extends State { padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), width: double.infinity, decoration: BoxDecoration( - color: ColorsManager.whiteColors, + color: ColorsManager.blackColor, boxShadow: [ BoxShadow( color: ColorsManager.shadowBlackColor, // Subtle shadow @@ -150,14 +171,16 @@ class SpaceManagementPageState extends State { // Use Expanded to ensure InteractiveViewer takes the available space Flexible( child: InteractiveViewer( - boundaryMargin: const EdgeInsets.all(500), // Adjusted to 500 + boundaryMargin: const EdgeInsets.all(20000), // Adjusted to 500 minScale: 0.5, // Minimum zoom scale - maxScale: 2.5, // Maximum zoom scale + maxScale: 5.5, // Maximum zoom scale panEnabled: true, // Enable panning scaleEnabled: true, // Enable zooming child: Container( - color: ColorsManager - .transparentColor, // Transparent background + width: canvasWidth, // Large width for free movement + height: canvasHeight, // Large height for free movement + + color: ColorsManager.blue1, // Transparent background child: spaces.isEmpty ? Center( child: AddSpaceButton( @@ -187,6 +210,7 @@ class SpaceManagementPageState extends State { setState(() { spaces[index].position += delta; }); + updateCanvasSize(); }, onHoverChanged: (int index, bool isHovered) { diff --git a/lib/pages/spaces_management/widgets/space_card_widget.dart b/lib/pages/spaces_management/widgets/space_card_widget.dart index 5453c227..afc108c2 100644 --- a/lib/pages/spaces_management/widgets/space_card_widget.dart +++ b/lib/pages/spaces_management/widgets/space_card_widget.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; - import 'plus_button_widget.dart'; // Make sure to import your PlusButtonWidget class SpaceCardWidget extends StatelessWidget { @@ -26,52 +25,48 @@ class SpaceCardWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Positioned( - left: position.dx, - top: position.dy, - child: GestureDetector( - onPanUpdate: (details) { - // Call the provided callback to update the position - onPanUpdate(index, details.delta); + return GestureDetector( + onPanUpdate: (details) { + // Call the provided callback to update the position + onPanUpdate(index, details.delta); + }, + child: MouseRegion( + onEnter: (_) { + // Call the provided callback to handle hover state + onHoverChanged(index, true); }, - child: MouseRegion( - onEnter: (_) { - // Call the provided callback to handle hover state - onHoverChanged(index, true); - }, - onExit: (_) { - // Call the provided callback to handle hover state - onHoverChanged(index, false); - }, - child: Stack( - clipBehavior: Clip.none, - children: [ - buildSpaceContainer(index), // Build the space container - if (isHovered) ...[ - PlusButtonWidget( - index: index, - direction: 'left', - offset: const Offset(-21, 20), - screenSize: screenSize, - onButtonTap: onButtonTap, - ), - PlusButtonWidget( - index: index, - direction: 'right', - offset: const Offset(140, 20), - screenSize: screenSize, - onButtonTap: onButtonTap, - ), - PlusButtonWidget( - index: index, - direction: 'down', - offset: const Offset(63, 50), - screenSize: screenSize, - onButtonTap: onButtonTap, - ), - ], + onExit: (_) { + // Call the provided callback to handle hover state + onHoverChanged(index, false); + }, + child: Stack( + clipBehavior: Clip.none, // Allow hovering elements to be displayed outside the boundary + children: [ + buildSpaceContainer(index), // Build the space container + if (isHovered) ...[ + PlusButtonWidget( + index: index, + direction: 'left', + offset: const Offset(-21, 20), + screenSize: screenSize, + onButtonTap: onButtonTap, + ), + PlusButtonWidget( + index: index, + direction: 'right', + offset: const Offset(140, 20), + screenSize: screenSize, + onButtonTap: onButtonTap, + ), + PlusButtonWidget( + index: index, + direction: 'down', + offset: const Offset(63, 50), + screenSize: screenSize, + onButtonTap: onButtonTap, + ), ], - ), + ], ), ), ); From df3b3131be46b027bca7b8fb08d3a209e13af2bc Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 14 Oct 2024 10:07:29 +0400 Subject: [PATCH 050/122] remoived debug print --- .../bloc/space_management_bloc.dart | 4 ---- .../view/spaces_management_page.dart | 4 +++- .../widgets/sidebar_widget.dart | 16 ---------------- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index c8b683a3..31d04ab8 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -32,10 +32,6 @@ class SpaceManagementBloc communities.map((community) async { List spaces = await _api.getSpaceHierarchy(community.uuid); - - debugPrint( - 'Fetched spaces for community ${community.name}: ${spaces.length}'); - return CommunityModel( uuid: community.uuid, createdAt: community.createdAt, diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index d4176610..dcebc8d2 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -56,6 +56,7 @@ class SpaceManagementPageState extends State { maxX = max(maxX, space.position.dx + 150); // Width of space maxY = max(maxY, space.position.dy + 60); // Height of space } + print("Max X: $maxX, Max Y: $maxY"); setState(() { canvasWidth = max(canvasWidth, maxX + 200); // Ensure padding @@ -210,7 +211,6 @@ class SpaceManagementPageState extends State { setState(() { spaces[index].position += delta; }); - updateCanvasSize(); }, onHoverChanged: (int index, bool isHovered) { @@ -255,6 +255,8 @@ class SpaceManagementPageState extends State { builder: (BuildContext context) { return CreateSpaceDialog( onCreateSpace: (String name, String icon) { + updateCanvasSize(); + setState(() { // Set the first space in the center or use passed position Offset centerPosition = position ?? diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index ffc15db2..0a8d743e 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -177,11 +177,6 @@ class _SidebarWidgetState extends State { bool hasChildren = community.spaces.isNotEmpty; bool isSelectedCommunity = _selectedCommunityUuid == community.uuid; // Check if this community is selected - - debugPrint('Building CommunityTile for ${community.name} with UUID: ${community.uuid}'); - debugPrint('Currently selected community UUID: $_selectedCommunityUuid'); - debugPrint('Is selected: $isSelectedCommunity'); - return CommunityTile( title: community.name, isSelected: isSelectedCommunity, @@ -189,10 +184,6 @@ class _SidebarWidgetState extends State { onItemSelected: () { setState(() { _selectedSpaceUuid = community.uuid; // Update the selected community - debugPrint( - 'Selected community: ${community.name}, UUID: ${community.uuid}'); - debugPrint( - 'Updated selected community UUID: $_selectedCommunityUuid'); }); if (widget.onCommunitySelected != null) { @@ -200,8 +191,6 @@ class _SidebarWidgetState extends State { } }, onExpansionChanged: (String title, bool expanded) { - debugPrint( - 'CommunityTile onExpansionChanged called for $title, expanded: $expanded'); _handleExpansionChange(community.uuid, expanded); }, children: hasChildren @@ -213,15 +202,10 @@ class _SidebarWidgetState extends State { Widget _buildSpaceTile(SpaceModel space) { bool isSelectedSpace = _isSpaceOrChildSelected(space); // Check if space should be expanded - - debugPrint( - 'Building SpaceTile for ${space.name}, hasChildren: ${space.children.isNotEmpty}'); return SpaceTile( title: space.name, initiallyExpanded: isSelectedSpace, onExpansionChanged: (bool expanded) { - debugPrint( - 'SpaceTile onExpansionChanged called for ${space.name}, expanded: $expanded'); _handleExpansionChange(space.uuid, expanded); }, children: space.children.isNotEmpty From 3f9bd105829fb4baf052562c815798625ecb3c34 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 14 Oct 2024 11:11:11 +0400 Subject: [PATCH 051/122] dynamic update --- .../view/spaces_management_page.dart | 30 +++++++++++++------ .../widgets/space_card_widget.dart | 1 + 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index dcebc8d2..ac135011 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -29,8 +29,8 @@ class SpaceManagementPageState extends State { // Store created spaces List spaces = []; List connections = []; - double canvasWidth = 4000; // Initial canvas width - double canvasHeight = 4000; // Initial canvas height + double canvasWidth = 1000; // Initial canvas width + double canvasHeight = 1000; // Initial canvas height // Track whether to show the community list view or community structure @@ -49,20 +49,31 @@ class SpaceManagementPageState extends State { } void updateCanvasSize() { + print("test"); double maxX = 0; double maxY = 0; + // Calculate the maximum X and Y positions of all spaces for (var space in spaces) { - maxX = max(maxX, space.position.dx + 150); // Width of space - maxY = max(maxY, space.position.dy + 60); // Height of space + print( + "Space Position: ${space.position.dx}, ${space.position.dy}"); // Log the position of each space + maxX = max(maxX, space.position.dx + 150); // Add width of space + maxY = max(maxY, space.position.dy + 60); // Add height of space } - print("Max X: $maxX, Max Y: $maxY"); + // Add padding (but avoid adding arbitrary amounts like 1000) + double newWidth = + max(maxX + 500, canvasWidth); // Use max to ensure the canvas only grows + double newHeight = max(maxY + 500, canvasHeight); + + // Set the new canvas size dynamically setState(() { - canvasWidth = max(canvasWidth, maxX + 200); // Ensure padding - canvasHeight = max(canvasHeight, maxY + 200); // Ensure padding + canvasWidth = newWidth; + canvasHeight = newHeight; }); + // Log the updated canvas size for debugging + print("Max X: $maxX, Max Y: $maxY"); print("Updated canvas size: $canvasWidth x $canvasHeight"); } @@ -208,9 +219,11 @@ class SpaceManagementPageState extends State { position: space.position, isHovered: space.isHovered, onPanUpdate: (int index, Offset delta) { + debugPrint("Check it works"); setState(() { spaces[index].position += delta; }); + updateCanvasSize(); }, onHoverChanged: (int index, bool isHovered) { @@ -255,8 +268,6 @@ class SpaceManagementPageState extends State { builder: (BuildContext context) { return CreateSpaceDialog( onCreateSpace: (String name, String icon) { - updateCanvasSize(); - setState(() { // Set the first space in the center or use passed position Offset centerPosition = position ?? @@ -269,6 +280,7 @@ class SpaceManagementPageState extends State { SpaceData newSpace = SpaceData(name: name, icon: icon, position: centerPosition); spaces.add(newSpace); + updateCanvasSize(); // Add connection for down-button if (parentIndex != null && direction != null) { diff --git a/lib/pages/spaces_management/widgets/space_card_widget.dart b/lib/pages/spaces_management/widgets/space_card_widget.dart index afc108c2..bac917d9 100644 --- a/lib/pages/spaces_management/widgets/space_card_widget.dart +++ b/lib/pages/spaces_management/widgets/space_card_widget.dart @@ -26,6 +26,7 @@ class SpaceCardWidget extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( + behavior: HitTestBehavior.opaque, onPanUpdate: (details) { // Call the provided callback to update the position onPanUpdate(index, details.delta); From fcd91305e5f65e593688eec052e23c65ee21b225 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 11 Nov 2024 21:02:47 +0400 Subject: [PATCH 052/122] removed debig colors --- .../spaces_management/view/spaces_management_page.dart | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 8f3d5329..92d59271 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -133,7 +133,6 @@ class SpaceManagementPageState extends State { return Expanded( child: Container( decoration: const BoxDecoration( - color: ColorsManager.blackColor, border: Border( left: BorderSide( color: ColorsManager.whiteColors, @@ -148,7 +147,7 @@ class SpaceManagementPageState extends State { padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), width: double.infinity, decoration: BoxDecoration( - color: ColorsManager.blackColor, + color: ColorsManager.whiteColors, boxShadow: [ BoxShadow( color: ColorsManager.shadowBlackColor, // Subtle shadow @@ -191,8 +190,6 @@ class SpaceManagementPageState extends State { child: Container( width: canvasWidth, // Large width for free movement height: canvasHeight, // Large height for free movement - - color: ColorsManager.blue1, // Transparent background child: spaces.isEmpty ? Center( child: AddSpaceButton( @@ -219,7 +216,6 @@ class SpaceManagementPageState extends State { position: space.position, isHovered: space.isHovered, onPanUpdate: (int index, Offset delta) { - debugPrint("Check it works"); setState(() { spaces[index].position += delta; }); From d0229ed81f731788428bb5f70f33b7f3479f4f4f Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 11 Nov 2024 21:20:41 +0400 Subject: [PATCH 053/122] fixed spaces api --- .../model/connection_model.dart | 12 +++++++ .../model/space_data_model.dart | 15 +++++++++ .../spaces_management/model/space_model.dart | 20 +++++------- .../view/curved_line_painter.dart | 2 +- .../view/spaces_management_page.dart | 32 ++----------------- lib/utils/constants/api_const.dart | 2 +- 6 files changed, 40 insertions(+), 43 deletions(-) create mode 100644 lib/pages/spaces_management/model/connection_model.dart create mode 100644 lib/pages/spaces_management/model/space_data_model.dart diff --git a/lib/pages/spaces_management/model/connection_model.dart b/lib/pages/spaces_management/model/connection_model.dart new file mode 100644 index 00000000..085ceefc --- /dev/null +++ b/lib/pages/spaces_management/model/connection_model.dart @@ -0,0 +1,12 @@ +import 'package:syncrow_web/pages/spaces_management/model/space_data_model.dart'; + +class Connection { + final SpaceData startSpace; + final SpaceData endSpace; + final String direction; + + Connection( + {required this.startSpace, + required this.endSpace, + required this.direction}); +} diff --git a/lib/pages/spaces_management/model/space_data_model.dart b/lib/pages/spaces_management/model/space_data_model.dart new file mode 100644 index 00000000..74465dad --- /dev/null +++ b/lib/pages/spaces_management/model/space_data_model.dart @@ -0,0 +1,15 @@ +import 'dart:ui'; + +class SpaceData { + final String name; + final String icon; + Offset position; + bool isHovered; + + SpaceData({ + required this.name, + required this.icon, + required this.position, + this.isHovered = false, + }); +} diff --git a/lib/pages/spaces_management/model/space_model.dart b/lib/pages/spaces_management/model/space_model.dart index 273c09cd..fcd55b61 100644 --- a/lib/pages/spaces_management/model/space_model.dart +++ b/lib/pages/spaces_management/model/space_model.dart @@ -9,7 +9,6 @@ class SpaceModel { final String name; final bool isPrivate; final String? invitationCode; - final bool isParent; final SpaceModel? parent; final CommunityModel? community; final List children; @@ -25,7 +24,6 @@ class SpaceModel { required this.name, required this.isPrivate, this.invitationCode, - required this.isParent, this.parent, this.community, required this.children, @@ -40,21 +38,20 @@ class SpaceModel { createdAt: DateTime.parse(json['createdAt']), updatedAt: DateTime.parse(json['updatedAt']), spaceTuyaUuid: json['spaceTuyaUuid'], - name: json['name'], - isPrivate: json['isPrivate'], + name: json['spaceName'], + isPrivate: json['isPrivate'] ?? false, invitationCode: json['invitationCode'], - isParent: json['isParent'], parent: json['parent'] != null ? SpaceModel.fromJson(json['parent']) : null, community: json['community'] != null ? CommunityModel.fromJson(json['community']) : null, - children: json['children'] != null - ? (json['children'] as List) - .map((child) => SpaceModel.fromJson(child)) - .toList() - : [], - icon: json['icon'], + children: json['children'] != null + ? (json['children'] as List) + .map((child) => SpaceModel.fromJson(child)) + .toList() + : [], + icon: json['icon'] as String?, position: json['position'] != null ? Offset(json['position']['dx'], json['position']['dy']) : const Offset(0, 0), @@ -71,7 +68,6 @@ class SpaceModel { 'name': name, 'isPrivate': isPrivate, 'invitationCode': invitationCode, - 'isParent': isParent, 'parent': parent?.toMap(), 'community': community?.toMap(), 'children': children.map((child) => child.toMap()).toList(), diff --git a/lib/pages/spaces_management/view/curved_line_painter.dart b/lib/pages/spaces_management/view/curved_line_painter.dart index 4cbb7f3f..f9a79fdf 100644 --- a/lib/pages/spaces_management/view/curved_line_painter.dart +++ b/lib/pages/spaces_management/view/curved_line_painter.dart @@ -1,7 +1,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; -import 'package:syncrow_web/pages/spaces_management/view/spaces_management_page.dart'; +import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class CurvedLinePainter extends CustomPainter { diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 92d59271..c70d8814 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -7,6 +7,8 @@ import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.d import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_data_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; @@ -172,7 +174,7 @@ class SpaceManagementPageState extends State { selectedCommunity!.name, // Show community name style: const TextStyle( fontSize: 16, - color: ColorsManager.blackColor, // Slightly muted color + color: ColorsManager.blackColor, ), ), ] @@ -294,31 +296,3 @@ class SpaceManagementPageState extends State { } } -// Function to open the Create Space dialog - -// Model for storing space information -class SpaceData { - final String name; - final String icon; - Offset position; - bool isHovered; - - SpaceData({ - required this.name, - required this.icon, - required this.position, - this.isHovered = false, - }); -} - -// Class for connection lines between spaces -class Connection { - final SpaceData startSpace; - final SpaceData endSpace; - final String direction; - - Connection( - {required this.startSpace, - required this.endSpace, - required this.direction}); -} diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 6198d7c1..950b7223 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -50,7 +50,7 @@ abstract class ApiEndpoints { static const String getSpace = '/communities/{communityId}/spaces/{spaceId}'; static const String getSpaceHierarchy = - '/communities/{communityId}/spaces/hierarchy'; + '/communities/{communityId}/spaces'; // Community Module static const String createCommunity = '/communities'; From ddea69f332aced169fc275592cf2b5a51bc9f7d4 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 12 Nov 2024 12:27:17 +0400 Subject: [PATCH 054/122] removed logs --- .../spaces_management/view/spaces_management_page.dart | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index c70d8814..ef33604c 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -51,14 +51,11 @@ class SpaceManagementPageState extends State { } void updateCanvasSize() { - print("test"); double maxX = 0; double maxY = 0; // Calculate the maximum X and Y positions of all spaces for (var space in spaces) { - print( - "Space Position: ${space.position.dx}, ${space.position.dy}"); // Log the position of each space maxX = max(maxX, space.position.dx + 150); // Add width of space maxY = max(maxY, space.position.dy + 60); // Add height of space } @@ -73,10 +70,6 @@ class SpaceManagementPageState extends State { canvasWidth = newWidth; canvasHeight = newHeight; }); - - // Log the updated canvas size for debugging - print("Max X: $maxX, Max Y: $maxY"); - print("Updated canvas size: $canvasWidth x $canvasHeight"); } @override From eacee98de2dc44949def9f0e702bc1015fb7f2c5 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 14 Nov 2024 21:33:35 +0400 Subject: [PATCH 055/122] fixed space layout --- .../view/spaces_management_page.dart | 461 ++++++++++-------- .../widgets/space_container_widget.dart | 1 + 2 files changed, 269 insertions(+), 193 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index ef33604c..7d308dd1 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -19,6 +19,7 @@ import 'package:syncrow_web/pages/spaces_management/widgets/space_container_widg import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; +import 'package:fl_chart/fl_chart.dart'; class SpaceManagementPage extends StatefulWidget { const SpaceManagementPage({super.key}); @@ -28,75 +29,44 @@ class SpaceManagementPage extends StatefulWidget { } class SpaceManagementPageState extends State { - // Store created spaces - List spaces = []; - List connections = []; - double canvasWidth = 1000; // Initial canvas width - double canvasHeight = 1000; // Initial canvas height - - // Track whether to show the community list view or community structure - - // Selected community CommunityModel? selectedCommunity; - - // API instance final CommunitySpaceManagementApi _api = CommunitySpaceManagementApi(); - - // Data structure to store community and associated spaces Map> communitySpaces = {}; + double canvasWidth = 1000; + double canvasHeight = 1000; + + final List nodes = [ + NodeData(id: 'Node 1', position: Offset(100, 100)), + NodeData(id: 'Node 2', position: Offset(300, 300)), + NodeData(id: 'Node 3', position: Offset(500, 500)), + ]; @override void initState() { super.initState(); } - void updateCanvasSize() { - double maxX = 0; - double maxY = 0; - - // Calculate the maximum X and Y positions of all spaces - for (var space in spaces) { - maxX = max(maxX, space.position.dx + 150); // Add width of space - maxY = max(maxY, space.position.dy + 60); // Add height of space - } - - // Add padding (but avoid adding arbitrary amounts like 1000) - double newWidth = - max(maxX + 500, canvasWidth); // Use max to ensure the canvas only grows - double newHeight = max(maxY + 500, canvasHeight); - - // Set the new canvas size dynamically - setState(() { - canvasWidth = newWidth; - canvasHeight = newHeight; - }); - } - @override Widget build(BuildContext context) { Size screenSize = MediaQuery.of(context).size; - return BlocProvider( create: (context) => SpaceManagementBloc(CommunitySpaceManagementApi()) ..add(LoadCommunityAndSpacesEvent()), child: WebScaffold( - appBarTitle: Text( - 'Space Management', - style: Theme.of(context).textTheme.headlineLarge, - ), + appBarTitle: Text('Space Management', + style: Theme.of(context).textTheme.headlineLarge), enableMenuSidebar: false, scaffoldBody: BlocBuilder( - builder: (context, state) { - if (state is SpaceManagementLoading) { - return const Center(child: CircularProgressIndicator()); - } else if (state is SpaceManagementLoaded) { - return _buildLoadedState(context, screenSize, state.communities); - } else if (state is SpaceManagementError) { - return Center(child: Text('Error: ${state.errorMessage}')); - } - return Container(); - }, - ), + builder: (context, state) { + if (state is SpaceManagementLoading) { + return const Center(child: CircularProgressIndicator()); + } else if (state is SpaceManagementLoaded) { + return _buildLoadedState(context, screenSize, state.communities); + } else if (state is SpaceManagementError) { + return Center(child: Text('Error: ${state.errorMessage}')); + } + return Container(); + }), ), ); } @@ -126,166 +96,271 @@ class SpaceManagementPageState extends State { Widget _buildCommunityStructureArea(BuildContext context, Size screenSize) { return Expanded( - child: Container( - decoration: const BoxDecoration( - border: Border( - left: BorderSide( - color: ColorsManager.whiteColors, - width: 1.0), // Light left border to match + child: Container( + decoration: const BoxDecoration( + border: Border( + left: BorderSide( + color: ColorsManager.whiteColors, + width: 1.0), // Light left border to match + ), + ), + // Background color for canvas + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Container( + padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), + width: double.infinity, + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + boxShadow: [ + BoxShadow( + color: ColorsManager.shadowBlackColor, // Subtle shadow + spreadRadius: 0, // No spread + blurRadius: 8, // Softer shadow edges + offset: const Offset(0, 4), // Shadow only on the bottom + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Community Structure', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + if (selectedCommunity != null) ...[ + Text( + selectedCommunity!.name, // Show community name + style: const TextStyle( + fontSize: 16, + color: ColorsManager.blackColor, + ), + ), + ], + ], ), ), - // Background color for canvas - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + Flexible( + child: InteractiveViewer( + boundaryMargin: EdgeInsets.all(500), // Adjusted for smoother panning + minScale: 0.5, + maxScale: 3.0, + constrained: false, + child: Container( + width: canvasWidth, + height: canvasHeight, + child: Stack( + children: [ + // Draw connections between nodes + for (int i = 0; i < nodes.length - 1; i++) + CustomPaint( + painter: + EdgePainter(nodes[i].position, nodes[i + 1].position), + ), + // Render each node and make it draggable + for (var node in nodes) + Positioned( + left: node.position.dx, + top: node.position.dy, + child: DraggableNode( + data: node, + onAddNode: (direction) => _addNode(node, direction), + onPositionChanged: (newPosition) { + _updateNodePosition(node, newPosition); + }, + ), + ), + ], + ), + ), + )) + ]), + )); + } + + void _updateNodePosition(NodeData node, Offset newPosition) { + setState(() { + node.position = newPosition; + + // Expand canvas to the right when node approaches the right edge + if (node.position.dx >= canvasWidth - 200) { + canvasWidth += 200; + print("Canvas width expanded to $canvasWidth"); + } + + // Expand canvas downward when node approaches the bottom edge + if (node.position.dy >= canvasHeight - 200) { + canvasHeight += 200; + print("Canvas height expanded to $canvasHeight"); + } + + // Expand canvas to the left when node approaches the left edge + if (node.position.dx <= 200) { + double shiftAmount = 200; + canvasWidth += shiftAmount; + + // Shift all nodes to the right by shiftAmount + for (var n in nodes) { + n.position = Offset(n.position.dx + shiftAmount, n.position.dy); + } + + print("Canvas expanded to the left. New width: $canvasWidth"); + } + + // Prevent nodes from going out of bounds on top edge + if (node.position.dy < 0) { + node.position = Offset(node.position.dx, 0); + } + + // Log the current canvas size for debugging + print( + "Current canvas size: width = $canvasWidth, height = $canvasHeight"); + }); + } + + void _addNode(NodeData parent, String direction) { + Offset newPosition; + switch (direction) { + case "right": + newPosition = parent.position + Offset(200, 0); + break; + case "left": + newPosition = parent.position - Offset(200, 0); + break; + case "bottom": + newPosition = parent.position + Offset(0, 200); + break; + default: + return; + } + + setState(() { + nodes + .add(NodeData(id: 'Node ${nodes.length + 1}', position: newPosition)); + + // Expand the canvas if necessary + if (newPosition.dx >= canvasWidth - 200) canvasWidth += 200; + if (newPosition.dy >= canvasHeight - 200) canvasHeight += 200; + if (newPosition.dx < 0) canvasWidth += 200; + if (newPosition.dy < 0) canvasHeight += 200; + + print("New node added in direction $direction at $newPosition"); + }); + } +} + +class NodeData { + String id; + Offset position; + + NodeData({required this.id, required this.position}); +} + +class DraggableNode extends StatefulWidget { + final NodeData data; + final ValueChanged onPositionChanged; + final ValueChanged + onAddNode; // Callback for adding a node in a specific direction + + DraggableNode({ + required this.data, + required this.onPositionChanged, + required this.onAddNode, + }); + + @override + _DraggableNodeState createState() => _DraggableNodeState(); +} + +class _DraggableNodeState extends State { + bool isHovered = false; + + @override + Widget build(BuildContext context) { + return MouseRegion( + onEnter: (_) => setState(() => isHovered = true), + onExit: (_) => setState(() => isHovered = false), + child: GestureDetector( + onPanUpdate: (details) { + final newPosition = widget.data.position + details.delta; + widget.onPositionChanged(newPosition); + }, + child: Stack( + alignment: Alignment.center, children: [ + // Main node container Container( - padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), - width: double.infinity, + padding: EdgeInsets.all(8), + width: 150, + height: 60, decoration: BoxDecoration( color: ColorsManager.whiteColors, + borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( - color: ColorsManager.shadowBlackColor, // Subtle shadow - spreadRadius: 0, // No spread - blurRadius: 8, // Softer shadow edges - offset: const Offset(0, 4), // Shadow only on the bottom + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: const Offset(0, 3), // shadow position ), ], ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Community Structure', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - if (selectedCommunity != null) ...[ - Text( - selectedCommunity!.name, // Show community name - style: const TextStyle( - fontSize: 16, - color: ColorsManager.blackColor, - ), - ), - ] - ], + child: Text( + widget.data.id, + style: TextStyle(color: Colors.white), ), ), - // Use Expanded to ensure InteractiveViewer takes the available space - Flexible( - child: InteractiveViewer( - boundaryMargin: const EdgeInsets.all(20000), // Adjusted to 500 - minScale: 0.5, // Minimum zoom scale - maxScale: 5.5, // Maximum zoom scale - panEnabled: true, // Enable panning - scaleEnabled: true, // Enable zooming - child: Container( - width: canvasWidth, // Large width for free movement - height: canvasHeight, // Large height for free movement - child: spaces.isEmpty - ? Center( - child: AddSpaceButton( - onTap: () { - _showCreateSpaceDialog(screenSize); - }, - ), - ) - : Stack( - clipBehavior: Clip.none, - children: [ - CustomPaint( - size: const Size(4000, 4000), - painter: CurvedLinePainter(connections), - ), - ...spaces.asMap().entries.map((entry) { - final space = entry.value; - return Positioned( - left: space.position.dx, - top: space.position.dy, - child: SpaceCardWidget( - index: entry.key, - screenSize: screenSize, - position: space.position, - isHovered: space.isHovered, - onPanUpdate: (int index, Offset delta) { - setState(() { - spaces[index].position += delta; - }); - updateCanvasSize(); - }, - onHoverChanged: - (int index, bool isHovered) { - setState(() { - spaces[index].isHovered = isHovered; - }); - }, - onButtonTap: (int index, Offset newPosition, - String direction) { - _showCreateSpaceDialog( - screenSize, - position: spaces[index].position + - newPosition, - parentIndex: index, - direction: direction, - ); - }, - buildSpaceContainer: (int index) { - return SpaceContainerWidget( - index: index, - icon: spaces[index].icon, - name: spaces[index].name, - ); - }, - ), - ); - }), - ], - )), + if (isHovered) ...[ + // Add icon on the right + Positioned( + right: -20, + child: IconButton( + icon: Icon(Icons.add_circle, color: Colors.green, size: 20), + onPressed: () => widget.onAddNode("right"), + ), ), - ), + // Add icon on the left + Positioned( + left: -20, + child: IconButton( + icon: Icon(Icons.add_circle, color: Colors.green, size: 20), + onPressed: () => widget.onAddNode("left"), + ), + ), + // Add icon on the bottom + Positioned( + bottom: -20, + child: IconButton( + icon: Icon(Icons.add_circle, color: Colors.green, size: 20), + onPressed: () => widget.onAddNode("bottom"), + ), + ), + ], ], ), ), ); } - - void _showCreateSpaceDialog(Size screenSize, - {Offset? position, int? parentIndex, String? direction}) { - showDialog( - context: context, - builder: (BuildContext context) { - return CreateSpaceDialog( - onCreateSpace: (String name, String icon) { - setState(() { - // Set the first space in the center or use passed position - Offset centerPosition = position ?? - Offset( - screenSize.width / 2 - 75, // Center horizontally - screenSize.height / 2 - - 100, // Slightly above the center vertically - ); - - SpaceData newSpace = - SpaceData(name: name, icon: icon, position: centerPosition); - spaces.add(newSpace); - updateCanvasSize(); - - // Add connection for down-button - if (parentIndex != null && direction != null) { - connections.add(Connection( - startSpace: spaces[parentIndex], - endSpace: newSpace, - direction: direction, - )); - } - }); - }, - ); - }, - ); - } } +class EdgePainter extends CustomPainter { + final Offset start; + final Offset end; + + EdgePainter(this.start, this.end); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = Colors.black + ..strokeWidth = 2.0 + ..style = PaintingStyle.stroke; + + canvas.drawLine(start, end, paint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => true; +} diff --git a/lib/pages/spaces_management/widgets/space_container_widget.dart b/lib/pages/spaces_management/widgets/space_container_widget.dart index be6e43b4..0f98c236 100644 --- a/lib/pages/spaces_management/widgets/space_container_widget.dart +++ b/lib/pages/spaces_management/widgets/space_container_widget.dart @@ -63,6 +63,7 @@ class SpaceContainerWidget extends StatelessWidget { ), ], ), + ); } } From de57e0f21d045f46d4e84c28f5b4a5cb91393309 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 15 Nov 2024 00:38:14 +0400 Subject: [PATCH 056/122] Fixed space layout --- .../view/spaces_management_page.dart | 306 +----------------- .../widgets/community_structure_widget.dart | 215 ++++++++++++ .../curved_line_painter.dart | 16 +- .../widgets/loaded_space_widget.dart | 42 +++ .../widgets/plus_button_widget.dart | 2 - .../widgets/space_card_widget.dart | 13 +- .../widgets/space_widget.dart | 4 +- 7 files changed, 292 insertions(+), 306 deletions(-) create mode 100644 lib/pages/spaces_management/widgets/community_structure_widget.dart rename lib/pages/spaces_management/{view => widgets}/curved_line_painter.dart (83%) create mode 100644 lib/pages/spaces_management/widgets/loaded_space_widget.dart diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 7d308dd1..9d735df1 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -1,8 +1,5 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; @@ -10,16 +7,12 @@ import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_data_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; -import 'package:syncrow_web/pages/spaces_management/view/curved_line_painter.dart'; -import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/community_structure_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/gradient_canvas_border_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/loaded_space_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/sidebar_widget.dart'; -import 'package:syncrow_web/pages/spaces_management/widgets/space_card_widget.dart'; -import 'package:syncrow_web/pages/spaces_management/widgets/space_container_widget.dart'; import 'package:syncrow_web/services/space_mana_api.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; -import 'package:fl_chart/fl_chart.dart'; class SpaceManagementPage extends StatefulWidget { const SpaceManagementPage({super.key}); @@ -34,12 +27,8 @@ class SpaceManagementPageState extends State { Map> communitySpaces = {}; double canvasWidth = 1000; double canvasHeight = 1000; - - final List nodes = [ - NodeData(id: 'Node 1', position: Offset(100, 100)), - NodeData(id: 'Node 2', position: Offset(300, 300)), - NodeData(id: 'Node 3', position: Offset(500, 500)), - ]; + List spaces = []; + List connections = []; @override void initState() { @@ -48,7 +37,6 @@ class SpaceManagementPageState extends State { @override Widget build(BuildContext context) { - Size screenSize = MediaQuery.of(context).size; return BlocProvider( create: (context) => SpaceManagementBloc(CommunitySpaceManagementApi()) ..add(LoadCommunityAndSpacesEvent()), @@ -61,7 +49,15 @@ class SpaceManagementPageState extends State { if (state is SpaceManagementLoading) { return const Center(child: CircularProgressIndicator()); } else if (state is SpaceManagementLoaded) { - return _buildLoadedState(context, screenSize, state.communities); + return LoadedSpaceView( + communities: state.communities, + selectedCommunity: selectedCommunity, + onCommunitySelected: (community) { + setState(() { + selectedCommunity = community; + }); + }, + ); } else if (state is SpaceManagementError) { return Center(child: Text('Error: ${state.errorMessage}')); } @@ -72,7 +68,7 @@ class SpaceManagementPageState extends State { } Widget _buildLoadedState( - BuildContext context, Size screenSize, List communities) { + BuildContext context, List communities) { return Stack( clipBehavior: Clip.none, children: [ @@ -82,285 +78,15 @@ class SpaceManagementPageState extends State { communities: communities, onCommunitySelected: (community) { setState(() { - selectedCommunity = community; // Set the selected community + selectedCommunity = community; }); }, ), - _buildCommunityStructureArea(context, screenSize) + CommunityStructureArea(selectedCommunity: selectedCommunity), ], ), const GradientCanvasBorderWidget() ], ); } - - Widget _buildCommunityStructureArea(BuildContext context, Size screenSize) { - return Expanded( - child: Container( - decoration: const BoxDecoration( - border: Border( - left: BorderSide( - color: ColorsManager.whiteColors, - width: 1.0), // Light left border to match - ), - ), - // Background color for canvas - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), - width: double.infinity, - decoration: BoxDecoration( - color: ColorsManager.whiteColors, - boxShadow: [ - BoxShadow( - color: ColorsManager.shadowBlackColor, // Subtle shadow - spreadRadius: 0, // No spread - blurRadius: 8, // Softer shadow edges - offset: const Offset(0, 4), // Shadow only on the bottom - ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Community Structure', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - if (selectedCommunity != null) ...[ - Text( - selectedCommunity!.name, // Show community name - style: const TextStyle( - fontSize: 16, - color: ColorsManager.blackColor, - ), - ), - ], - ], - ), - ), - Flexible( - child: InteractiveViewer( - boundaryMargin: EdgeInsets.all(500), // Adjusted for smoother panning - minScale: 0.5, - maxScale: 3.0, - constrained: false, - child: Container( - width: canvasWidth, - height: canvasHeight, - child: Stack( - children: [ - // Draw connections between nodes - for (int i = 0; i < nodes.length - 1; i++) - CustomPaint( - painter: - EdgePainter(nodes[i].position, nodes[i + 1].position), - ), - // Render each node and make it draggable - for (var node in nodes) - Positioned( - left: node.position.dx, - top: node.position.dy, - child: DraggableNode( - data: node, - onAddNode: (direction) => _addNode(node, direction), - onPositionChanged: (newPosition) { - _updateNodePosition(node, newPosition); - }, - ), - ), - ], - ), - ), - )) - ]), - )); - } - - void _updateNodePosition(NodeData node, Offset newPosition) { - setState(() { - node.position = newPosition; - - // Expand canvas to the right when node approaches the right edge - if (node.position.dx >= canvasWidth - 200) { - canvasWidth += 200; - print("Canvas width expanded to $canvasWidth"); - } - - // Expand canvas downward when node approaches the bottom edge - if (node.position.dy >= canvasHeight - 200) { - canvasHeight += 200; - print("Canvas height expanded to $canvasHeight"); - } - - // Expand canvas to the left when node approaches the left edge - if (node.position.dx <= 200) { - double shiftAmount = 200; - canvasWidth += shiftAmount; - - // Shift all nodes to the right by shiftAmount - for (var n in nodes) { - n.position = Offset(n.position.dx + shiftAmount, n.position.dy); - } - - print("Canvas expanded to the left. New width: $canvasWidth"); - } - - // Prevent nodes from going out of bounds on top edge - if (node.position.dy < 0) { - node.position = Offset(node.position.dx, 0); - } - - // Log the current canvas size for debugging - print( - "Current canvas size: width = $canvasWidth, height = $canvasHeight"); - }); - } - - void _addNode(NodeData parent, String direction) { - Offset newPosition; - switch (direction) { - case "right": - newPosition = parent.position + Offset(200, 0); - break; - case "left": - newPosition = parent.position - Offset(200, 0); - break; - case "bottom": - newPosition = parent.position + Offset(0, 200); - break; - default: - return; - } - - setState(() { - nodes - .add(NodeData(id: 'Node ${nodes.length + 1}', position: newPosition)); - - // Expand the canvas if necessary - if (newPosition.dx >= canvasWidth - 200) canvasWidth += 200; - if (newPosition.dy >= canvasHeight - 200) canvasHeight += 200; - if (newPosition.dx < 0) canvasWidth += 200; - if (newPosition.dy < 0) canvasHeight += 200; - - print("New node added in direction $direction at $newPosition"); - }); - } -} - -class NodeData { - String id; - Offset position; - - NodeData({required this.id, required this.position}); -} - -class DraggableNode extends StatefulWidget { - final NodeData data; - final ValueChanged onPositionChanged; - final ValueChanged - onAddNode; // Callback for adding a node in a specific direction - - DraggableNode({ - required this.data, - required this.onPositionChanged, - required this.onAddNode, - }); - - @override - _DraggableNodeState createState() => _DraggableNodeState(); -} - -class _DraggableNodeState extends State { - bool isHovered = false; - - @override - Widget build(BuildContext context) { - return MouseRegion( - onEnter: (_) => setState(() => isHovered = true), - onExit: (_) => setState(() => isHovered = false), - child: GestureDetector( - onPanUpdate: (details) { - final newPosition = widget.data.position + details.delta; - widget.onPositionChanged(newPosition); - }, - child: Stack( - alignment: Alignment.center, - children: [ - // Main node container - Container( - padding: EdgeInsets.all(8), - width: 150, - height: 60, - decoration: BoxDecoration( - color: ColorsManager.whiteColors, - borderRadius: BorderRadius.circular(15), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: const Offset(0, 3), // shadow position - ), - ], - ), - child: Text( - widget.data.id, - style: TextStyle(color: Colors.white), - ), - ), - if (isHovered) ...[ - // Add icon on the right - Positioned( - right: -20, - child: IconButton( - icon: Icon(Icons.add_circle, color: Colors.green, size: 20), - onPressed: () => widget.onAddNode("right"), - ), - ), - // Add icon on the left - Positioned( - left: -20, - child: IconButton( - icon: Icon(Icons.add_circle, color: Colors.green, size: 20), - onPressed: () => widget.onAddNode("left"), - ), - ), - // Add icon on the bottom - Positioned( - bottom: -20, - child: IconButton( - icon: Icon(Icons.add_circle, color: Colors.green, size: 20), - onPressed: () => widget.onAddNode("bottom"), - ), - ), - ], - ], - ), - ), - ); - } -} - -class EdgePainter extends CustomPainter { - final Offset start; - final Offset end; - - EdgePainter(this.start, this.end); - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = Colors.black - ..strokeWidth = 2.0 - ..style = PaintingStyle.stroke; - - canvas.drawLine(start, end, paint); - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) => true; } diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart new file mode 100644 index 00000000..3e628810 --- /dev/null +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -0,0 +1,215 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; +import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/space_card_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_data_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/space_container_widget.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class CommunityStructureArea extends StatefulWidget { + final CommunityModel? selectedCommunity; + + CommunityStructureArea({this.selectedCommunity}); + + @override + _CommunityStructureAreaState createState() => _CommunityStructureAreaState(); +} + +class _CommunityStructureAreaState extends State { + double canvasWidth = 1000; + double canvasHeight = 1000; + List spaces = []; + List connections = []; + + @override + Widget build(BuildContext context) { + Size screenSize = MediaQuery.of(context).size; + return Expanded( + child: Container( + decoration: const BoxDecoration( + border: Border( + left: BorderSide(color: ColorsManager.whiteColors, width: 1.0), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildHeader(), + Flexible( + child: InteractiveViewer( + boundaryMargin: EdgeInsets.all(500), + minScale: 0.5, + maxScale: 3.0, + constrained: false, + child: Container( + width: canvasWidth, + height: canvasHeight, + child: spaces.isEmpty + ? Center( + child: AddSpaceButton( + onTap: () { + _showCreateSpaceDialog(screenSize); + }, + ), + ) + : Stack( + children: [ + for (var connection in connections) + CustomPaint( + painter: CurvedLinePainter([connection])), + for (var entry in spaces.asMap().entries) + Positioned( + left: entry.value.position.dx, + top: entry.value.position.dy, + child: SpaceCardWidget( + index: entry.key, + onButtonTap: (int index, Offset newPosition, + String direction) { + _showCreateSpaceDialog( + screenSize, + position: + spaces[index].position + newPosition, + parentIndex: index, + direction: direction, + ); + }, + position: entry.value.position, + isHovered: entry.value.isHovered, + screenSize: screenSize, + onHoverChanged: _handleHoverChanged, + onPositionChanged: (newPosition) { + _updateNodePosition( + entry.value, newPosition); + }, + buildSpaceContainer: (int index) { + return SpaceContainerWidget( + index: index, + icon: spaces[index].icon, + name: spaces[index].name, + ); + }, + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildHeader() { + return Container( + padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), + width: double.infinity, + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + boxShadow: [ + BoxShadow( + color: ColorsManager.shadowBlackColor, + spreadRadius: 0, + blurRadius: 8, + offset: const Offset(0, 4), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Community Structure', + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ), + if (widget.selectedCommunity != null) + Text( + widget.selectedCommunity!.name, + style: const TextStyle( + fontSize: 16, color: ColorsManager.blackColor), + ), + ], + ), + ); + } + + void _updateNodePosition(SpaceData node, Offset newPosition) { + setState(() { + node.position = newPosition; + + // Expand canvas to the right when node approaches the right edge + if (node.position.dx >= canvasWidth - 200) { + canvasWidth += 200; + } + + // Expand canvas downward when node approaches the bottom edge + if (node.position.dy >= canvasHeight - 200) { + canvasHeight += 200; + } + + // Expand canvas to the left when node approaches the left edge + if (node.position.dx <= 200) { + double shiftAmount = 200; + canvasWidth += shiftAmount; + + // Shift all nodes to the right by shiftAmount + for (var n in spaces) { + n.position = Offset(n.position.dx + shiftAmount, n.position.dy); + } + } + + // Prevent nodes from going out of bounds on top edge + if (node.position.dy < 0) { + node.position = Offset(node.position.dx, 0); + } + }); + } + + void _showCreateSpaceDialog(Size screenSize, + {Offset? position, int? parentIndex, String? direction}) { + showDialog( + context: context, + builder: (BuildContext context) { + return CreateSpaceDialog( + onCreateSpace: (String name, String icon) { + setState(() { + // Set the first space in the center or use passed position + Offset centerPosition = position ?? + Offset( + screenSize.width / 2 - 75, // Center horizontally + screenSize.height / 2 - + 100, // Slightly above the center vertically + ); + + SpaceData newSpace = + SpaceData(name: name, icon: icon, position: centerPosition); + spaces.add(newSpace); + + _updateNodePosition(newSpace, newSpace.position); + + // Add connection for down-button + if (parentIndex != null && direction != null) { + SpaceData parentSpace = spaces[parentIndex]; + connections.add(Connection( + startSpace: parentSpace, + endSpace: newSpace, + direction: direction, + )); + } + }); + }, + ); + }, + ); + } + + void _handleHoverChanged(int index, bool isHovered) { + setState(() { + spaces[index].isHovered = isHovered; + }); + } +} diff --git a/lib/pages/spaces_management/view/curved_line_painter.dart b/lib/pages/spaces_management/widgets/curved_line_painter.dart similarity index 83% rename from lib/pages/spaces_management/view/curved_line_painter.dart rename to lib/pages/spaces_management/widgets/curved_line_painter.dart index f9a79fdf..263f0edc 100644 --- a/lib/pages/spaces_management/view/curved_line_painter.dart +++ b/lib/pages/spaces_management/widgets/curved_line_painter.dart @@ -1,5 +1,3 @@ -import 'dart:ui'; - import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -40,14 +38,20 @@ class CurvedLinePainter extends CustomPainter { ..quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy); canvas.drawPath(path, paint); } else if (connection.direction == 'right') { - // Straight line for right connections + start = connection.startSpace.position + + const Offset(150, 30); // Right center + end = connection.endSpace.position + const Offset(0, 30); // Left center + canvas.drawLine(start, end, paint); } else if (connection.direction == 'left') { - // Straight line for left connections + start = + connection.startSpace.position + const Offset(0, 30); // Left center + end = connection.endSpace.position + + const Offset(150, 30); // Right center + canvas.drawLine(start, end, paint); } - - // Draw small connection dots at the start and end points + final dotPaint = Paint()..color = ColorsManager.blackColor; canvas.drawCircle(start, 5, dotPaint); // Start dot canvas.drawCircle(end, 5, dotPaint); // End dot diff --git a/lib/pages/spaces_management/widgets/loaded_space_widget.dart b/lib/pages/spaces_management/widgets/loaded_space_widget.dart new file mode 100644 index 00000000..25e41e1a --- /dev/null +++ b/lib/pages/spaces_management/widgets/loaded_space_widget.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/community_structure_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/gradient_canvas_border_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/sidebar_widget.dart'; + +class LoadedSpaceView extends StatefulWidget { + final List communities; + final CommunityModel? selectedCommunity; + final ValueChanged onCommunitySelected; + + const LoadedSpaceView({ + Key? key, + required this.communities, + this.selectedCommunity, + required this.onCommunitySelected, + }) : super(key: key); + + @override + _LoadedStateViewState createState() => _LoadedStateViewState(); +} + +class _LoadedStateViewState extends State { + @override + Widget build(BuildContext context) { + return Stack( + clipBehavior: Clip.none, + children: [ + Row( + children: [ + SidebarWidget( + communities: widget.communities, + onCommunitySelected: widget.onCommunitySelected, + ), + CommunityStructureArea(selectedCommunity: widget.selectedCommunity), + ], + ), + const GradientCanvasBorderWidget(), + ], + ); + } +} diff --git a/lib/pages/spaces_management/widgets/plus_button_widget.dart b/lib/pages/spaces_management/widgets/plus_button_widget.dart index 755dad92..e6d1cde8 100644 --- a/lib/pages/spaces_management/widgets/plus_button_widget.dart +++ b/lib/pages/spaces_management/widgets/plus_button_widget.dart @@ -5,7 +5,6 @@ class PlusButtonWidget extends StatelessWidget { final int index; final String direction; final Offset offset; - final Size screenSize; final Function(int index, Offset newPosition, String direction) onButtonTap; const PlusButtonWidget({ @@ -13,7 +12,6 @@ class PlusButtonWidget extends StatelessWidget { required this.index, required this.direction, required this.offset, - required this.screenSize, required this.onButtonTap, }); diff --git a/lib/pages/spaces_management/widgets/space_card_widget.dart b/lib/pages/spaces_management/widgets/space_card_widget.dart index bac917d9..0a78da52 100644 --- a/lib/pages/spaces_management/widgets/space_card_widget.dart +++ b/lib/pages/spaces_management/widgets/space_card_widget.dart @@ -6,18 +6,18 @@ class SpaceCardWidget extends StatelessWidget { final Size screenSize; final Offset position; final bool isHovered; - final Function(int index, Offset delta) onPanUpdate; final Function(int index, bool isHovered) onHoverChanged; final Function(int index, Offset newPosition, String direction) onButtonTap; final Widget Function(int index) buildSpaceContainer; + final ValueChanged onPositionChanged; const SpaceCardWidget({ super.key, required this.index, + required this.onPositionChanged, required this.screenSize, required this.position, required this.isHovered, - required this.onPanUpdate, required this.onHoverChanged, required this.onButtonTap, required this.buildSpaceContainer, @@ -29,7 +29,8 @@ class SpaceCardWidget extends StatelessWidget { behavior: HitTestBehavior.opaque, onPanUpdate: (details) { // Call the provided callback to update the position - onPanUpdate(index, details.delta); + final newPosition = position + details.delta; + onPositionChanged(newPosition); }, child: MouseRegion( onEnter: (_) { @@ -41,7 +42,8 @@ class SpaceCardWidget extends StatelessWidget { onHoverChanged(index, false); }, child: Stack( - clipBehavior: Clip.none, // Allow hovering elements to be displayed outside the boundary + clipBehavior: Clip + .none, // Allow hovering elements to be displayed outside the boundary children: [ buildSpaceContainer(index), // Build the space container if (isHovered) ...[ @@ -49,21 +51,18 @@ class SpaceCardWidget extends StatelessWidget { index: index, direction: 'left', offset: const Offset(-21, 20), - screenSize: screenSize, onButtonTap: onButtonTap, ), PlusButtonWidget( index: index, direction: 'right', offset: const Offset(140, 20), - screenSize: screenSize, onButtonTap: onButtonTap, ), PlusButtonWidget( index: index, direction: 'down', offset: const Offset(63, 50), - screenSize: screenSize, onButtonTap: onButtonTap, ), ], diff --git a/lib/pages/spaces_management/widgets/space_widget.dart b/lib/pages/spaces_management/widgets/space_widget.dart index 0d661292..e4ce27cc 100644 --- a/lib/pages/spaces_management/widgets/space_widget.dart +++ b/lib/pages/spaces_management/widgets/space_widget.dart @@ -19,7 +19,8 @@ class SpaceWidget extends StatelessWidget { top: position.dy, child: GestureDetector( onTap: onTap, - child: Container( + child: + Container( padding: const EdgeInsets.all(8.0), decoration: BoxDecoration( color: Colors.white, @@ -41,6 +42,7 @@ class SpaceWidget extends StatelessWidget { ], ), ), + ), ); } From 1d9a93646b070bbcdbae33e4c41cf02beb70249a Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 15 Nov 2024 00:46:24 +0400 Subject: [PATCH 057/122] removed space create button out of interactive viewer --- .../widgets/community_structure_widget.dart | 121 +++++++++--------- 1 file changed, 62 insertions(+), 59 deletions(-) diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 3e628810..df72adad 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -39,65 +39,68 @@ class _CommunityStructureAreaState extends State { children: [ _buildHeader(), Flexible( - child: InteractiveViewer( - boundaryMargin: EdgeInsets.all(500), - minScale: 0.5, - maxScale: 3.0, - constrained: false, - child: Container( - width: canvasWidth, - height: canvasHeight, - child: spaces.isEmpty - ? Center( - child: AddSpaceButton( - onTap: () { - _showCreateSpaceDialog(screenSize); - }, - ), - ) - : Stack( - children: [ - for (var connection in connections) - CustomPaint( - painter: CurvedLinePainter([connection])), - for (var entry in spaces.asMap().entries) - Positioned( - left: entry.value.position.dx, - top: entry.value.position.dy, - child: SpaceCardWidget( - index: entry.key, - onButtonTap: (int index, Offset newPosition, - String direction) { - _showCreateSpaceDialog( - screenSize, - position: - spaces[index].position + newPosition, - parentIndex: index, - direction: direction, - ); - }, - position: entry.value.position, - isHovered: entry.value.isHovered, - screenSize: screenSize, - onHoverChanged: _handleHoverChanged, - onPositionChanged: (newPosition) { - _updateNodePosition( - entry.value, newPosition); - }, - buildSpaceContainer: (int index) { - return SpaceContainerWidget( - index: index, - icon: spaces[index].icon, - name: spaces[index].name, - ); - }, - ), + child: Stack( + children: [ + if (spaces.isNotEmpty) + InteractiveViewer( + boundaryMargin: EdgeInsets.all(500), + minScale: 0.5, + maxScale: 3.0, + constrained: false, + child: Container( + width: canvasWidth, + height: canvasHeight, + child: Stack( + children: [ + for (var connection in connections) + CustomPaint( + painter: CurvedLinePainter([connection])), + for (var entry in spaces.asMap().entries) + Positioned( + left: entry.value.position.dx, + top: entry.value.position.dy, + child: SpaceCardWidget( + index: entry.key, + onButtonTap: (int index, Offset newPosition, + String direction) { + _showCreateSpaceDialog( + screenSize, + position: + spaces[index].position + newPosition, + parentIndex: index, + direction: direction, + ); + }, + position: entry.value.position, + isHovered: entry.value.isHovered, + screenSize: screenSize, + onHoverChanged: _handleHoverChanged, + onPositionChanged: (newPosition) { + _updateNodePosition(entry.value, newPosition); + }, + buildSpaceContainer: (int index) { + return SpaceContainerWidget( + index: index, + icon: spaces[index].icon, + name: spaces[index].name, + ); + }, ), - ], - ), - ), - ), - ), + ), + ], + ), + ), + ), + if (spaces.isEmpty) + Center( + child: AddSpaceButton( + onTap: () { + _showCreateSpaceDialog(screenSize); + }, + ), + ), + ], + )), ], ), ), @@ -182,7 +185,7 @@ class _CommunityStructureAreaState extends State { Offset( screenSize.width / 2 - 75, // Center horizontally screenSize.height / 2 - - 100, // Slightly above the center vertically + 50, // Slightly above the center vertically ); SpaceData newSpace = From e33a07ac565fcb6626a8d43e71063948a3a9c7d6 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 15 Nov 2024 11:20:20 +0400 Subject: [PATCH 058/122] changed color of plus button --- lib/pages/spaces_management/widgets/plus_button_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/spaces_management/widgets/plus_button_widget.dart b/lib/pages/spaces_management/widgets/plus_button_widget.dart index e6d1cde8..b077ac9d 100644 --- a/lib/pages/spaces_management/widgets/plus_button_widget.dart +++ b/lib/pages/spaces_management/widgets/plus_button_widget.dart @@ -42,7 +42,7 @@ class PlusButtonWidget extends StatelessWidget { width: 30, height: 30, decoration: const BoxDecoration( - color: ColorsManager.secondaryColor, + color: ColorsManager.spaceColor, shape: BoxShape.circle, ), child: const Icon(Icons.add, color: Colors.white, size: 20), From 836c44fd95b382e4b606baeb5479ad3f903979f2 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 18 Nov 2024 15:36:33 +0400 Subject: [PATCH 059/122] updated api --- .../bloc/space_management_bloc.dart | 31 +++++++++ .../bloc/space_management_event.dart | 17 ++++- .../model/connection_model.dart | 6 +- .../spaces_management/model/space_model.dart | 30 ++++----- .../view/spaces_management_page.dart | 34 ++-------- .../widgets/community_structure_widget.dart | 66 ++++++++++++++----- .../widgets/sidebar_widget.dart | 2 +- lib/services/space_mana_api.dart | 14 ++-- 8 files changed, 125 insertions(+), 75 deletions(-) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 31d04ab8..1b4aa221 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -94,4 +94,35 @@ class SpaceManagementBloc emit(SpaceManagementError('Error creating community: $e')); } } + +void _onSaveSpaces( + SaveSpacesEvent event, + Emitter emit, +) async { + final previousState = state; + emit(SpaceManagementLoading()); + + try { + // Save spaces one by one + for (var space in event.spaces) { + await _api.createSpace( + communityId: event.communityUuid, + name: space.name, + parentId: space.parent?.uuid, + isPrivate: space.isPrivate, + position: space.position, + ); + } + + emit(SpaceCreationSuccess()); + } catch (e) { + emit(SpaceManagementError('Error saving spaces: $e')); + + // Revert back to the previous state if an error occurs + if (previousState is SpaceManagementLoaded) { + emit(previousState); + } + } +} + } diff --git a/lib/pages/spaces_management/bloc/space_management_event.dart b/lib/pages/spaces_management/bloc/space_management_event.dart index 7aaf84ae..3bea27e8 100644 --- a/lib/pages/spaces_management/bloc/space_management_event.dart +++ b/lib/pages/spaces_management/bloc/space_management_event.dart @@ -1,5 +1,6 @@ import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; // Import for Offset +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; // Import for Offset abstract class SpaceManagementEvent extends Equatable { const SpaceManagementEvent(); @@ -35,6 +36,19 @@ class CreateSpaceEvent extends SpaceManagementEvent { ]; } +class SaveSpacesEvent extends SpaceManagementEvent { + final List spaces; + final String communityUuid; + + const SaveSpacesEvent({ + required this.spaces, + required this.communityUuid, + }); + + @override + List get props => [spaces, communityUuid]; +} + class UpdateSpacePositionEvent extends SpaceManagementEvent { final int index; final Offset newPosition; @@ -45,7 +59,6 @@ class UpdateSpacePositionEvent extends SpaceManagementEvent { List get props => [index, newPosition]; } - class CreateCommunityEvent extends SpaceManagementEvent { final String name; final String description; diff --git a/lib/pages/spaces_management/model/connection_model.dart b/lib/pages/spaces_management/model/connection_model.dart index 085ceefc..a078e550 100644 --- a/lib/pages/spaces_management/model/connection_model.dart +++ b/lib/pages/spaces_management/model/connection_model.dart @@ -1,8 +1,8 @@ -import 'package:syncrow_web/pages/spaces_management/model/space_data_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; class Connection { - final SpaceData startSpace; - final SpaceData endSpace; + final SpaceModel startSpace; + final SpaceModel endSpace; final String direction; Connection( diff --git a/lib/pages/spaces_management/model/space_model.dart b/lib/pages/spaces_management/model/space_model.dart index fcd55b61..71af8cce 100644 --- a/lib/pages/spaces_management/model/space_model.dart +++ b/lib/pages/spaces_management/model/space_model.dart @@ -2,9 +2,8 @@ import 'dart:ui'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; class SpaceModel { - final String uuid; - final DateTime createdAt; - final DateTime updatedAt; + final String? uuid; + final String? icon; final String? spaceTuyaUuid; final String name; final bool isPrivate; @@ -12,31 +11,26 @@ class SpaceModel { final SpaceModel? parent; final CommunityModel? community; final List children; - final String? icon; Offset position; bool isHovered; SpaceModel({ - required this.uuid, - required this.createdAt, - required this.updatedAt, + this.uuid, this.spaceTuyaUuid, + required this.icon, required this.name, required this.isPrivate, this.invitationCode, this.parent, this.community, required this.children, - required this.icon, required this.position, this.isHovered = false, }); factory SpaceModel.fromJson(Map json) { return SpaceModel( - uuid: json['uuid'], - createdAt: DateTime.parse(json['createdAt']), - updatedAt: DateTime.parse(json['updatedAt']), + uuid: json['uuid'] ?? '', spaceTuyaUuid: json['spaceTuyaUuid'], name: json['spaceName'], isPrivate: json['isPrivate'] ?? false, @@ -46,11 +40,11 @@ class SpaceModel { community: json['community'] != null ? CommunityModel.fromJson(json['community']) : null, - children: json['children'] != null - ? (json['children'] as List) - .map((child) => SpaceModel.fromJson(child)) - .toList() - : [], + children: json['children'] != null + ? (json['children'] as List) + .map((child) => SpaceModel.fromJson(child)) + .toList() + : [], icon: json['icon'] as String?, position: json['position'] != null ? Offset(json['position']['dx'], json['position']['dy']) @@ -61,9 +55,7 @@ class SpaceModel { Map toMap() { return { - 'uuid': uuid, - 'createdAt': createdAt.toIso8601String(), - 'updatedAt': updatedAt.toIso8601String(), + 'uuid': uuid ?? '', 'spaceTuyaUuid': spaceTuyaUuid, 'name': name, 'isPrivate': isPrivate, diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 9d735df1..ac076b84 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -38,14 +38,13 @@ class SpaceManagementPageState extends State { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => SpaceManagementBloc(CommunitySpaceManagementApi()) - ..add(LoadCommunityAndSpacesEvent()), + create: (context) => + SpaceManagementBloc(CommunitySpaceManagementApi())..add(LoadCommunityAndSpacesEvent()), child: WebScaffold( - appBarTitle: Text('Space Management', - style: Theme.of(context).textTheme.headlineLarge), + appBarTitle: Text('Space Management', style: Theme.of(context).textTheme.headlineLarge), enableMenuSidebar: false, - scaffoldBody: BlocBuilder( - builder: (context, state) { + scaffoldBody: + BlocBuilder(builder: (context, state) { if (state is SpaceManagementLoading) { return const Center(child: CircularProgressIndicator()); } else if (state is SpaceManagementLoaded) { @@ -66,27 +65,4 @@ class SpaceManagementPageState extends State { ), ); } - - Widget _buildLoadedState( - BuildContext context, List communities) { - return Stack( - clipBehavior: Clip.none, - children: [ - Row( - children: [ - SidebarWidget( - communities: communities, - onCommunitySelected: (community) { - setState(() { - selectedCommunity = community; - }); - }, - ), - CommunityStructureArea(selectedCommunity: selectedCommunity), - ], - ), - const GradientCanvasBorderWidget() - ], - ); - } } diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index df72adad..7c1c29e1 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/space_card_widget.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; -import 'package:syncrow_web/pages/spaces_management/model/space_data_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/space_container_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -21,7 +21,7 @@ class CommunityStructureArea extends StatefulWidget { class _CommunityStructureAreaState extends State { double canvasWidth = 1000; double canvasHeight = 1000; - List spaces = []; + List spaces = []; List connections = []; @override @@ -81,7 +81,7 @@ class _CommunityStructureAreaState extends State { buildSpaceContainer: (int index) { return SpaceContainerWidget( index: index, - icon: spaces[index].icon, + icon: spaces[index].icon ?? '', name: spaces[index].name, ); }, @@ -122,25 +122,51 @@ class _CommunityStructureAreaState extends State { ), ], ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( - 'Community Structure', - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Community Structure', + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ), + if (widget.selectedCommunity != null) + Text( + widget.selectedCommunity!.name, + style: const TextStyle( + fontSize: 16, color: ColorsManager.blackColor), + ), + ], ), - if (widget.selectedCommunity != null) - Text( - widget.selectedCommunity!.name, - style: const TextStyle( - fontSize: 16, color: ColorsManager.blackColor), + // Show "Save" button only if there are spaces + if (spaces.isNotEmpty && widget.selectedCommunity != null ) + ElevatedButton.icon( + onPressed: () { + _saveSpaces(); + }, + icon: const Icon(Icons.save, size: 18), + label: const Text("Save"), + style: ElevatedButton.styleFrom( + + backgroundColor: ColorsManager.whiteColors, + foregroundColor: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + padding: + const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), + side: BorderSide(color: Colors.grey.shade300), + elevation: 0, + ), ), ], ), ); } - void _updateNodePosition(SpaceData node, Offset newPosition) { + void _updateNodePosition(SpaceModel node, Offset newPosition) { setState(() { node.position = newPosition; @@ -188,15 +214,15 @@ class _CommunityStructureAreaState extends State { 50, // Slightly above the center vertically ); - SpaceData newSpace = - SpaceData(name: name, icon: icon, position: centerPosition); + SpaceModel newSpace = + SpaceModel(name: name, icon: icon, position: centerPosition, isPrivate: false, children: []); spaces.add(newSpace); _updateNodePosition(newSpace, newSpace.position); // Add connection for down-button if (parentIndex != null && direction != null) { - SpaceData parentSpace = spaces[parentIndex]; + SpaceModel parentSpace = spaces[parentIndex]; connections.add(Connection( startSpace: parentSpace, endSpace: newSpace, @@ -215,4 +241,10 @@ class _CommunityStructureAreaState extends State { spaces[index].isHovered = isHovered; }); } + + void _saveSpaces() { + // Implement your save functionality here + + print("Spaces saved: ${spaces.length}"); + } } diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 0a8d743e..2771ff09 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -206,7 +206,7 @@ class _SidebarWidgetState extends State { title: space.name, initiallyExpanded: isSelectedSpace, onExpansionChanged: (bool expanded) { - _handleExpansionChange(space.uuid, expanded); + _handleExpansionChange(space.uuid ?? '', expanded); }, children: space.children.isNotEmpty ? space.children diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index 2498dc51..12372326 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -20,7 +20,7 @@ class CommunitySpaceManagementApi { return CommunityModel.fromJson(jsonItem); }).toList(); return communityList; - }, + }, ); return response; } catch (e) { @@ -141,12 +141,19 @@ class CommunitySpaceManagementApi { } } - Future createSpace(String communityId, String name, - {String? parentId, bool isPrivate = false}) async { + Future createSpace({ + required String communityId, + required String name, + String? parentId, + bool isPrivate = false, + required Offset position, + }) async { try { final body = { 'name': name, 'isPrivate': isPrivate, + 'x': position.dx, + 'y': position.dy, }; if (parentId != null) { body['parentId'] = parentId; @@ -219,5 +226,4 @@ class CommunitySpaceManagementApi { return []; } } - } From d0b74ca68afdafe4acf9e391fd5da80f1bae0d14 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 18 Nov 2024 20:40:41 +0400 Subject: [PATCH 060/122] added incoming and outgoing connections --- .../model/connection_model.dart | 21 +++++-- .../spaces_management/model/space_model.dart | 31 ++++++---- .../widgets/community_structure_widget.dart | 57 ++++++++----------- 3 files changed, 62 insertions(+), 47 deletions(-) diff --git a/lib/pages/spaces_management/model/connection_model.dart b/lib/pages/spaces_management/model/connection_model.dart index a078e550..650781d6 100644 --- a/lib/pages/spaces_management/model/connection_model.dart +++ b/lib/pages/spaces_management/model/connection_model.dart @@ -5,8 +5,21 @@ class Connection { final SpaceModel endSpace; final String direction; - Connection( - {required this.startSpace, - required this.endSpace, - required this.direction}); + Connection({required this.startSpace, required this.endSpace, required this.direction}); + + Map toMap() { + return { + 'startUuid': startSpace.uuid ?? 'unsaved-start-space-${startSpace.name}', // Fallback for unsaved spaces + 'endUuid': endSpace.uuid ?? 'unsaved-end-space-${endSpace.name}', // Fallback for unsaved spaces + 'direction': direction, + }; + } + + static Connection fromMap(Map map, Map spaces) { + return Connection( + startSpace: spaces[map['startUuid']]!, + endSpace: spaces[map['endUuid']]!, + direction: map['direction'], + ); + } } diff --git a/lib/pages/spaces_management/model/space_model.dart b/lib/pages/spaces_management/model/space_model.dart index 71af8cce..6363a331 100644 --- a/lib/pages/spaces_management/model/space_model.dart +++ b/lib/pages/spaces_management/model/space_model.dart @@ -1,5 +1,6 @@ import 'dart:ui'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; class SpaceModel { final String? uuid; @@ -8,12 +9,15 @@ class SpaceModel { final String name; final bool isPrivate; final String? invitationCode; - final SpaceModel? parent; + SpaceModel? parent; final CommunityModel? community; - final List children; + List children; Offset position; bool isHovered; + List outgoingConnections = []; // Connections from this space + List incomingConnections = []; // Connections to this space + SpaceModel({ this.uuid, this.spaceTuyaUuid, @@ -35,15 +39,10 @@ class SpaceModel { name: json['spaceName'], isPrivate: json['isPrivate'] ?? false, invitationCode: json['invitationCode'], - parent: - json['parent'] != null ? SpaceModel.fromJson(json['parent']) : null, - community: json['community'] != null - ? CommunityModel.fromJson(json['community']) - : null, + parent: json['parent'] != null ? SpaceModel.fromJson(json['parent']) : null, + community: json['community'] != null ? CommunityModel.fromJson(json['community']) : null, children: json['children'] != null - ? (json['children'] as List) - .map((child) => SpaceModel.fromJson(child)) - .toList() + ? (json['children'] as List).map((child) => SpaceModel.fromJson(child)).toList() : [], icon: json['icon'] as String?, position: json['position'] != null @@ -60,12 +59,22 @@ class SpaceModel { 'name': name, 'isPrivate': isPrivate, 'invitationCode': invitationCode, - 'parent': parent?.toMap(), + 'parent': parent?.uuid, 'community': community?.toMap(), 'children': children.map((child) => child.toMap()).toList(), 'icon': icon, 'position': {'dx': position.dx, 'dy': position.dy}, 'isHovered': isHovered, + 'outgoingConnections': outgoingConnections.map((c) => c.toMap()).toList(), + 'incomingConnections': incomingConnections.map((c) => c.toMap()).toList(), }; } + + void addOutgoingConnection(Connection connection) { + outgoingConnections.add(connection); + } + + void addIncomingConnection(Connection connection) { + incomingConnections.add(connection); + } } diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 7c1c29e1..9eca6913 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -53,20 +53,17 @@ class _CommunityStructureAreaState extends State { child: Stack( children: [ for (var connection in connections) - CustomPaint( - painter: CurvedLinePainter([connection])), + CustomPaint(painter: CurvedLinePainter([connection])), for (var entry in spaces.asMap().entries) Positioned( left: entry.value.position.dx, top: entry.value.position.dy, child: SpaceCardWidget( index: entry.key, - onButtonTap: (int index, Offset newPosition, - String direction) { + onButtonTap: (int index, Offset newPosition, String direction) { _showCreateSpaceDialog( screenSize, - position: - spaces[index].position + newPosition, + position: spaces[index].position + newPosition, parentIndex: index, direction: direction, ); @@ -135,13 +132,12 @@ class _CommunityStructureAreaState extends State { if (widget.selectedCommunity != null) Text( widget.selectedCommunity!.name, - style: const TextStyle( - fontSize: 16, color: ColorsManager.blackColor), + style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), ), ], ), // Show "Save" button only if there are spaces - if (spaces.isNotEmpty && widget.selectedCommunity != null ) + if (spaces.isNotEmpty && widget.selectedCommunity != null) ElevatedButton.icon( onPressed: () { _saveSpaces(); @@ -149,14 +145,12 @@ class _CommunityStructureAreaState extends State { icon: const Icon(Icons.save, size: 18), label: const Text("Save"), style: ElevatedButton.styleFrom( - backgroundColor: ColorsManager.whiteColors, foregroundColor: Colors.black, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), - padding: - const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), + padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), side: BorderSide(color: Colors.grey.shade300), elevation: 0, ), @@ -169,29 +163,19 @@ class _CommunityStructureAreaState extends State { void _updateNodePosition(SpaceModel node, Offset newPosition) { setState(() { node.position = newPosition; - - // Expand canvas to the right when node approaches the right edge if (node.position.dx >= canvasWidth - 200) { canvasWidth += 200; } - - // Expand canvas downward when node approaches the bottom edge if (node.position.dy >= canvasHeight - 200) { canvasHeight += 200; } - - // Expand canvas to the left when node approaches the left edge if (node.position.dx <= 200) { double shiftAmount = 200; canvasWidth += shiftAmount; - - // Shift all nodes to the right by shiftAmount for (var n in spaces) { n.position = Offset(n.position.dx + shiftAmount, n.position.dy); } } - - // Prevent nodes from going out of bounds on top edge if (node.position.dy < 0) { node.position = Offset(node.position.dx, 0); } @@ -210,25 +194,30 @@ class _CommunityStructureAreaState extends State { Offset centerPosition = position ?? Offset( screenSize.width / 2 - 75, // Center horizontally - screenSize.height / 2 - - 50, // Slightly above the center vertically + screenSize.height / 2 - 50, // Slightly above the center vertically ); - SpaceModel newSpace = - SpaceModel(name: name, icon: icon, position: centerPosition, isPrivate: false, children: []); - spaces.add(newSpace); + SpaceModel newSpace = SpaceModel( + name: name, + icon: icon, + position: centerPosition, + isPrivate: false, + children: [], + ); - _updateNodePosition(newSpace, newSpace.position); - - // Add connection for down-button if (parentIndex != null && direction != null) { SpaceModel parentSpace = spaces[parentIndex]; + newSpace.parent = parentSpace; + parentSpace.children.add(newSpace); connections.add(Connection( startSpace: parentSpace, endSpace: newSpace, direction: direction, )); } + + spaces.add(newSpace); + _updateNodePosition(newSpace, newSpace.position); }); }, ); @@ -243,8 +232,12 @@ class _CommunityStructureAreaState extends State { } void _saveSpaces() { - // Implement your save functionality here + List> spaceData = spaces.map((space) => space.toMap()).toList(); + List> connectionData = + connections.map((connection) => connection.toMap()).toList(); - print("Spaces saved: ${spaces.length}"); + // Save to local storage, a file, or send to a backend + print('Spaces: ${spaceData}'); + print('Connections: ${connectionData}'); } } From 7241f78566aec677db4589ba0792f50cee901219 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Mon, 18 Nov 2024 20:44:42 +0400 Subject: [PATCH 061/122] add ongoing and incoming connections --- .../widgets/community_structure_widget.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 9eca6913..f162b49f 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -209,11 +209,14 @@ class _CommunityStructureAreaState extends State { SpaceModel parentSpace = spaces[parentIndex]; newSpace.parent = parentSpace; parentSpace.children.add(newSpace); - connections.add(Connection( + final newConnection = Connection( startSpace: parentSpace, endSpace: newSpace, direction: direction, - )); + ); + connections.add(newConnection); + newSpace.addIncomingConnection(newConnection); + parentSpace.addOutgoingConnection(newConnection); } spaces.add(newSpace); @@ -239,5 +242,6 @@ class _CommunityStructureAreaState extends State { // Save to local storage, a file, or send to a backend print('Spaces: ${spaceData}'); print('Connections: ${connectionData}'); + } } From 20f94e290de9ce27d711c466bf1ce797814972be Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 19 Nov 2024 01:21:32 +0400 Subject: [PATCH 062/122] fixed page load for space --- .../bloc/space_management_bloc.dart | 131 ++++++++++++------ .../bloc/space_management_event.dart | 10 ++ .../bloc/space_management_state.dart | 10 +- .../spaces_management/model/space_model.dart | 70 +++++++--- .../view/spaces_management_page.dart | 3 - .../widgets/community_structure_widget.dart | 117 ++++++++++++++-- .../widgets/loaded_space_widget.dart | 6 +- lib/services/space_mana_api.dart | 64 +++++---- 8 files changed, 303 insertions(+), 108 deletions(-) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 1b4aa221..0f5b2ad7 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -1,5 +1,3 @@ - -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; @@ -7,15 +5,14 @@ import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event. import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; import 'package:syncrow_web/services/space_mana_api.dart'; -class SpaceManagementBloc - extends Bloc { +class SpaceManagementBloc extends Bloc { final CommunitySpaceManagementApi _api; SpaceManagementBloc(this._api) : super(SpaceManagementInitial()) { on(_onLoadCommunityAndSpaces); - on(_onCreateSpace); on(_onUpdateSpacePosition); on(_onCreateCommunity); + on(_onSaveSpaces); } void _onLoadCommunityAndSpaces( @@ -30,8 +27,7 @@ class SpaceManagementBloc // Use Future.wait to handle async calls within map List updatedCommunities = await Future.wait( communities.map((community) async { - List spaces = - await _api.getSpaceHierarchy(community.uuid); + List spaces = await _api.getSpaceHierarchy(community.uuid); return CommunityModel( uuid: community.uuid, createdAt: community.createdAt, @@ -50,15 +46,6 @@ class SpaceManagementBloc } } - void _onCreateSpace( - CreateSpaceEvent event, - Emitter emit, - ) { - // Handle space creation logic - // You can emit a new state here based on your needs - emit(SpaceCreationSuccess()); - } - void _onUpdateSpacePosition( UpdateSpacePositionEvent event, Emitter emit, @@ -82,9 +69,8 @@ class SpaceManagementBloc if (newCommunity != null) { if (previousState is SpaceManagementLoaded) { - final updatedCommunities = - List.from(previousState.communities) - ..add(newCommunity); + final updatedCommunities = List.from(previousState.communities) + ..add(newCommunity); emit(SpaceManagementLoaded(communities: updatedCommunities)); } } else { @@ -95,34 +81,91 @@ class SpaceManagementBloc } } -void _onSaveSpaces( - SaveSpacesEvent event, - Emitter emit, -) async { - final previousState = state; - emit(SpaceManagementLoading()); + void _onSaveSpaces( + SaveSpacesEvent event, + Emitter emit, + ) async { + final previousState = state; + emit(SpaceManagementLoading()); - try { - // Save spaces one by one - for (var space in event.spaces) { - await _api.createSpace( - communityId: event.communityUuid, - name: space.name, - parentId: space.parent?.uuid, - isPrivate: space.isPrivate, - position: space.position, - ); - } + try { + final updatedSpaces = await saveSpacesHierarchically(event.spaces, event.communityUuid); - emit(SpaceCreationSuccess()); - } catch (e) { - emit(SpaceManagementError('Error saving spaces: $e')); - - // Revert back to the previous state if an error occurs - if (previousState is SpaceManagementLoaded) { - emit(previousState); + emit(SpaceCreationSuccess(spaces: updatedSpaces)); + add(LoadCommunityAndSpacesEvent()); + } catch (e) { + emit(SpaceManagementError('Error saving spaces: $e')); + if (previousState is SpaceManagementLoaded) { + emit(previousState); + } } } -} + Future> saveSpacesHierarchically( + List spaces, String communityUuid) async { + final orderedSpaces = flattenHierarchy(spaces); + + for (var space in orderedSpaces) { + try { + if (space.uuid != null && space.uuid!.isNotEmpty) { + // Call update if the space already has a UUID + final response = await _api.updateSpace( + communityId: communityUuid, + spaceId: space.uuid!, + name: space.name, + parentId: space.parent?.uuid, + isPrivate: space.isPrivate, + position: space.position, + icon: space.icon, + direction: space.incomingConnection?.direction, + ); + } else { + // Call create if the space does not have a UUID + final response = await _api.createSpace( + communityId: communityUuid, + name: space.name, + parentId: space.parent?.uuid, + isPrivate: space.isPrivate, + position: space.position, + icon: space.icon, + direction: space.incomingConnection?.direction, + ); + space.uuid = response?.uuid; + } + } catch (e) { + print('Error creating space ${space.name}: $e'); + rethrow; // Stop further execution on failure + } + } + return spaces; + } + + List flattenHierarchy(List spaces) { + final result = {}; // Use a Set to avoid duplicates + + // Collect all top-level spaces (those without a parent) + final topLevelSpaces = spaces.where((space) => space.parent == null); + + void visit(SpaceModel space) { + if (!result.contains(space)) { + result.add(space); // Add the space + for (var child in spaces.where((s) => s.parent == space)) { + visit(child); // Recursively add children based on parent + } + } + } + + // Start with top-level spaces + for (var space in topLevelSpaces) { + visit(space); + } + + // Add any spaces that were not part of the hierarchy + for (var space in spaces) { + if (!result.contains(space)) { + result.add(space); // Add standalone or orphan spaces + } + } + return result.toList(); // Convert back to a list + } } diff --git a/lib/pages/spaces_management/bloc/space_management_event.dart b/lib/pages/spaces_management/bloc/space_management_event.dart index 3bea27e8..cc0ba9b2 100644 --- a/lib/pages/spaces_management/bloc/space_management_event.dart +++ b/lib/pages/spaces_management/bloc/space_management_event.dart @@ -73,3 +73,13 @@ class CreateCommunityEvent extends SpaceManagementEvent { @override List get props => [name, description, regionId]; } + + +class LoadSpaceHierarchyEvent extends SpaceManagementEvent { + final String communityId; + + const LoadSpaceHierarchyEvent({required this.communityId}); + + @override + List get props => [communityId]; +} \ No newline at end of file diff --git a/lib/pages/spaces_management/bloc/space_management_state.dart b/lib/pages/spaces_management/bloc/space_management_state.dart index 87a98ef2..3d859547 100644 --- a/lib/pages/spaces_management/bloc/space_management_state.dart +++ b/lib/pages/spaces_management/bloc/space_management_state.dart @@ -1,5 +1,6 @@ import 'package:equatable/equatable.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; abstract class SpaceManagementState extends Equatable { const SpaceManagementState(); @@ -21,7 +22,14 @@ class SpaceManagementLoaded extends SpaceManagementState { List get props => [communities]; } -class SpaceCreationSuccess extends SpaceManagementState {} +class SpaceCreationSuccess extends SpaceManagementState { + final List spaces; + + const SpaceCreationSuccess({required this.spaces}); + + @override + List get props => [spaces]; +} class SpaceManagementError extends SpaceManagementState { final String errorMessage; diff --git a/lib/pages/spaces_management/model/space_model.dart b/lib/pages/spaces_management/model/space_model.dart index 6363a331..f6c3620c 100644 --- a/lib/pages/spaces_management/model/space_model.dart +++ b/lib/pages/spaces_management/model/space_model.dart @@ -3,7 +3,7 @@ import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; class SpaceModel { - final String? uuid; + String? uuid; final String? icon; final String? spaceTuyaUuid; final String name; @@ -16,24 +16,25 @@ class SpaceModel { bool isHovered; List outgoingConnections = []; // Connections from this space - List incomingConnections = []; // Connections to this space + Connection? incomingConnection; // Connections to this space - SpaceModel({ - this.uuid, - this.spaceTuyaUuid, - required this.icon, - required this.name, - required this.isPrivate, - this.invitationCode, - this.parent, - this.community, - required this.children, - required this.position, - this.isHovered = false, - }); + SpaceModel( + {this.uuid, + this.spaceTuyaUuid, + required this.icon, + required this.name, + required this.isPrivate, + this.invitationCode, + this.parent, + this.community, + required this.children, + required this.position, + this.isHovered = false, + this.incomingConnection}); factory SpaceModel.fromJson(Map json) { - return SpaceModel( + // Create SpaceModel instance first + final instance = SpaceModel( uuid: json['uuid'] ?? '', spaceTuyaUuid: json['spaceTuyaUuid'], name: json['spaceName'], @@ -45,11 +46,38 @@ class SpaceModel { ? (json['children'] as List).map((child) => SpaceModel.fromJson(child)).toList() : [], icon: json['icon'] as String?, - position: json['position'] != null - ? Offset(json['position']['dx'], json['position']['dy']) + position: json['x'] != null && json['y'] != null + ? Offset(json['x'], json['y']) : const Offset(0, 0), isHovered: false, ); + + // Add incomingConnection to the instance after creation + if (json['incomingConnections'] != null && + json['incomingConnections'] is List && + (json['incomingConnections'] as List).isNotEmpty && instance.parent != null ) { + final conn = json['incomingConnections'][0]; + instance.incomingConnection = Connection( + startSpace: instance.parent ?? instance, // Parent space + endSpace: instance, // This space instance + direction: conn['direction'], + ); + } + + return instance; + } + @override + String toString() { + return ''' +SpaceModel { + uuid: $uuid, + name: $name, + isPrivate: $isPrivate, + position: {dx: ${position.dx}, dy: ${position.dy}}, + parentUuid: ${parent?.uuid}, + children: ${children.map((child) => child.name).toList()}, + isHovered: $isHovered +}'''; } Map toMap() { @@ -66,15 +94,11 @@ class SpaceModel { 'position': {'dx': position.dx, 'dy': position.dy}, 'isHovered': isHovered, 'outgoingConnections': outgoingConnections.map((c) => c.toMap()).toList(), - 'incomingConnections': incomingConnections.map((c) => c.toMap()).toList(), + 'incomingConnection': incomingConnection?.toMap(), }; } void addOutgoingConnection(Connection connection) { outgoingConnections.add(connection); } - - void addIncomingConnection(Connection connection) { - incomingConnections.add(connection); - } } diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index ac076b84..79d57072 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -7,10 +7,7 @@ import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_data_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; -import 'package:syncrow_web/pages/spaces_management/widgets/community_structure_widget.dart'; -import 'package:syncrow_web/pages/spaces_management/widgets/gradient_canvas_border_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/loaded_space_widget.dart'; -import 'package:syncrow_web/pages/spaces_management/widgets/sidebar_widget.dart'; import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index f162b49f..ce5007e0 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; +import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart'; @@ -11,8 +14,14 @@ import 'package:syncrow_web/utils/color_manager.dart'; class CommunityStructureArea extends StatefulWidget { final CommunityModel? selectedCommunity; + final List spaces; + final List connections; - CommunityStructureArea({this.selectedCommunity}); + CommunityStructureArea({ + this.selectedCommunity, + required this.spaces, + required this.connections, + }); @override _CommunityStructureAreaState createState() => _CommunityStructureAreaState(); @@ -24,6 +33,28 @@ class _CommunityStructureAreaState extends State { List spaces = []; List connections = []; + @override + void initState() { + super.initState(); + spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; + connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; + } + + @override + void didUpdateWidget(covariant CommunityStructureArea oldWidget) { + super.didUpdateWidget(oldWidget); + + if (oldWidget.spaces != widget.spaces || oldWidget.connections != widget.connections) { + setState(() { + spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; + connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; + }); + for (var space in spaces) { + print('InitState - Space Position ${space.name}: ${space.incomingConnection?.direction}'); + } + } + } + @override Widget build(BuildContext context) { Size screenSize = MediaQuery.of(context).size; @@ -196,7 +227,6 @@ class _CommunityStructureAreaState extends State { screenSize.width / 2 - 75, // Center horizontally screenSize.height / 2 - 50, // Slightly above the center vertically ); - SpaceModel newSpace = SpaceModel( name: name, icon: icon, @@ -208,15 +238,15 @@ class _CommunityStructureAreaState extends State { if (parentIndex != null && direction != null) { SpaceModel parentSpace = spaces[parentIndex]; newSpace.parent = parentSpace; - parentSpace.children.add(newSpace); final newConnection = Connection( startSpace: parentSpace, endSpace: newSpace, direction: direction, ); connections.add(newConnection); - newSpace.addIncomingConnection(newConnection); + newSpace.incomingConnection = newConnection; parentSpace.addOutgoingConnection(newConnection); + parentSpace.children.add(newSpace); } spaces.add(newSpace); @@ -234,14 +264,77 @@ class _CommunityStructureAreaState extends State { }); } - void _saveSpaces() { - List> spaceData = spaces.map((space) => space.toMap()).toList(); - List> connectionData = - connections.map((connection) => connection.toMap()).toList(); + List flattenSpaces(List spaces) { + List result = []; - // Save to local storage, a file, or send to a backend - print('Spaces: ${spaceData}'); - print('Connections: ${connectionData}'); - + void flatten(SpaceModel space) { + // Add the current space to the result + result.add(space); + + // Recursively flatten child spaces + for (var child in space.children) { + flatten(child); + } + } + + // Process each top-level space + for (var space in spaces) { + flatten(space); + } + + return result; + } + + List createConnections(List spaces) { + List connections = []; + + void addConnections(SpaceModel parent, String direction) { + for (var child in parent.children) { + // Create a connection object + connections.add( + Connection( + startSpace: parent, + endSpace: child, + direction: child.incomingConnection?.direction ?? + "down", // Assuming "down" for all direct children + ), + ); + + // Recursively process the child's children + addConnections(child, direction); + } + } + + // Process each top-level space + for (var space in spaces) { + addConnections(space, "down"); + } + + return connections; + } + + void _saveSpaces() { + if (widget.selectedCommunity == null) { + print("No community selected for saving spaces."); + return; + } + + // Prepare spaces and connections data + List spaceList = spaces; + String communityUuid = widget.selectedCommunity!.uuid; + + for (var space in spaceList) { + print("Processing space: ${space.name}, UUID: ${space.uuid}"); + // Example: Log connections + print("Parent UUID: ${space.parent?.uuid}"); + print("Incoming connection: ${space.incomingConnection?.direction}"); + print("Outgoing connections: ${space.outgoingConnections.map((c) => c.direction).toList()}"); + } + + // Dispatch the save event + context.read().add(SaveSpacesEvent( + spaces: spaceList, + communityUuid: communityUuid, + )); } } diff --git a/lib/pages/spaces_management/widgets/loaded_space_widget.dart b/lib/pages/spaces_management/widgets/loaded_space_widget.dart index 25e41e1a..3b3ebeab 100644 --- a/lib/pages/spaces_management/widgets/loaded_space_widget.dart +++ b/lib/pages/spaces_management/widgets/loaded_space_widget.dart @@ -32,7 +32,11 @@ class _LoadedStateViewState extends State { communities: widget.communities, onCommunitySelected: widget.onCommunitySelected, ), - CommunityStructureArea(selectedCommunity: widget.selectedCommunity), + CommunityStructureArea( + selectedCommunity: widget.selectedCommunity, + spaces: widget.selectedCommunity?.spaces ?? [], + connections: [], + ), ], ), const GradientCanvasBorderWidget(), diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index 12372326..2461d5c8 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -32,8 +32,7 @@ class CommunitySpaceManagementApi { Future getCommunityById(String communityId) async { try { final response = await HTTPService().get( - path: ApiEndpoints.getCommunityById - .replaceAll('{communityId}', communityId), + path: ApiEndpoints.getCommunityById.replaceAll('{communityId}', communityId), expectedResponseModel: (json) { return CommunityModel.fromJson(json['data']); }, @@ -45,8 +44,7 @@ class CommunitySpaceManagementApi { } } - Future createCommunity( - String name, String description, String regionId) async { + Future createCommunity(String name, String description, String regionId) async { try { final response = await HTTPService().post( path: ApiEndpoints.createCommunity, @@ -66,12 +64,10 @@ class CommunitySpaceManagementApi { } } - Future updateCommunity( - String communityId, CommunityModel community) async { + Future updateCommunity(String communityId, CommunityModel community) async { try { final response = await HTTPService().put( - path: ApiEndpoints.updateCommunity - .replaceAll('{communityId}', communityId), + path: ApiEndpoints.updateCommunity.replaceAll('{communityId}', communityId), body: community.toMap(), expectedResponseModel: (json) { return json['success'] ?? false; @@ -87,8 +83,7 @@ class CommunitySpaceManagementApi { Future deleteCommunity(String communityId) async { try { final response = await HTTPService().delete( - path: ApiEndpoints.deleteCommunity - .replaceAll('{communityId}', communityId), + path: ApiEndpoints.deleteCommunity.replaceAll('{communityId}', communityId), expectedResponseModel: (json) { return json['success'] ?? false; }, @@ -145,20 +140,24 @@ class CommunitySpaceManagementApi { required String communityId, required String name, String? parentId, + String? direction, bool isPrivate = false, required Offset position, + String? icon, }) async { try { final body = { - 'name': name, + 'spaceName': name, 'isPrivate': isPrivate, 'x': position.dx, 'y': position.dy, + 'direction': direction, + 'icon': icon }; if (parentId != null) { - body['parentId'] = parentId; + body['parentUuid'] = parentId; } - + print(ApiEndpoints.createSpace.replaceAll('{communityId}', communityId)); final response = await HTTPService().post( path: ApiEndpoints.createSpace.replaceAll('{communityId}', communityId), body: body, @@ -173,22 +172,42 @@ class CommunitySpaceManagementApi { } } - Future updateSpace( - String communityId, String spaceId, SpaceModel space) async { + Future updateSpace({ + required String communityId, + required spaceId, + required String name, + String? parentId, + String? icon, + String? direction, + bool isPrivate = false, + required Offset position, + }) async { try { + final body = { + 'spaceName': name, + 'isPrivate': isPrivate, + 'x': position.dx, + 'y': position.dy, + 'direction': direction, + 'icon': icon + }; + if (parentId != null) { + body['parentUuid'] = parentId; + } + final response = await HTTPService().put( path: ApiEndpoints.updateSpace .replaceAll('{communityId}', communityId) .replaceAll('{spaceId}', spaceId), - body: space.toMap(), + body: body, expectedResponseModel: (json) { - return json['success'] ?? false; + return SpaceModel.fromJson(json['data']); }, ); return response; } catch (e) { - debugPrint('Error updating space: $e'); - return false; + debugPrint('Error creating space: $e'); + return null; } } @@ -212,12 +231,9 @@ class CommunitySpaceManagementApi { Future> getSpaceHierarchy(String communityId) async { try { final response = await HTTPService().get( - path: ApiEndpoints.getSpaceHierarchy - .replaceAll('{communityId}', communityId), + path: ApiEndpoints.getSpaceHierarchy.replaceAll('{communityId}', communityId), expectedResponseModel: (json) { - return (json['data'] as List) - .map((spaceJson) => SpaceModel.fromJson(spaceJson)) - .toList(); + return (json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList(); }, ); return response; From 3147fc154fc1f4e3e364520122a297d9707ecdf7 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 19 Nov 2024 12:37:57 +0400 Subject: [PATCH 063/122] adjust the canvas if spaces are far positioned --- .../bloc/space_management_bloc.dart | 12 +++------ .../widgets/community_structure_widget.dart | 25 +++++++++++-------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 0f5b2ad7..0df905d9 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -141,29 +141,25 @@ class SpaceManagementBloc extends Bloc flattenHierarchy(List spaces) { - final result = {}; // Use a Set to avoid duplicates - - // Collect all top-level spaces (those without a parent) + final result = {}; final topLevelSpaces = spaces.where((space) => space.parent == null); void visit(SpaceModel space) { if (!result.contains(space)) { - result.add(space); // Add the space + result.add(space); for (var child in spaces.where((s) => s.parent == space)) { - visit(child); // Recursively add children based on parent + visit(child); } } } - // Start with top-level spaces for (var space in topLevelSpaces) { visit(space); } - // Add any spaces that were not part of the hierarchy for (var space in spaces) { if (!result.contains(space)) { - result.add(space); // Add standalone or orphan spaces + result.add(space); } } return result.toList(); // Convert back to a list diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index ce5007e0..27381a61 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -38,6 +38,7 @@ class _CommunityStructureAreaState extends State { super.initState(); spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; + _adjustCanvasSizeForSpaces(); } @override @@ -48,10 +49,8 @@ class _CommunityStructureAreaState extends State { setState(() { spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; + _adjustCanvasSizeForSpaces(); }); - for (var space in spaces) { - print('InitState - Space Position ${space.name}: ${space.incomingConnection?.direction}'); - } } } @@ -213,6 +212,18 @@ class _CommunityStructureAreaState extends State { }); } + void _adjustCanvasSizeForSpaces() { + for (var space in spaces) { + if (space.position.dx >= canvasWidth - 200) { + canvasWidth = space.position.dx + 200; + } + + if (space.position.dy >= canvasHeight - 200) { + canvasHeight = space.position.dy + 200; + } + } + } + void _showCreateSpaceDialog(Size screenSize, {Offset? position, int? parentIndex, String? direction}) { showDialog( @@ -323,14 +334,6 @@ class _CommunityStructureAreaState extends State { List spaceList = spaces; String communityUuid = widget.selectedCommunity!.uuid; - for (var space in spaceList) { - print("Processing space: ${space.name}, UUID: ${space.uuid}"); - // Example: Log connections - print("Parent UUID: ${space.parent?.uuid}"); - print("Incoming connection: ${space.incomingConnection?.direction}"); - print("Outgoing connections: ${space.outgoingConnections.map((c) => c.direction).toList()}"); - } - // Dispatch the save event context.read().add(SaveSpacesEvent( spaces: spaceList, From 3a333188f857129c979424eb404ecd21358ee8cb Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 19 Nov 2024 13:11:57 +0400 Subject: [PATCH 064/122] save only if there is a change or new space --- .../spaces_management/model/space_model.dart | 34 +++++++++++-------- .../view/spaces_management_page.dart | 7 ++++ .../widgets/community_structure_widget.dart | 19 ++++++++--- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/lib/pages/spaces_management/model/space_model.dart b/lib/pages/spaces_management/model/space_model.dart index f6c3620c..e64b741f 100644 --- a/lib/pages/spaces_management/model/space_model.dart +++ b/lib/pages/spaces_management/model/space_model.dart @@ -2,6 +2,8 @@ import 'dart:ui'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; +enum SpaceStatus { newSpace, modified, unchanged } + class SpaceModel { String? uuid; final String? icon; @@ -14,23 +16,26 @@ class SpaceModel { List children; Offset position; bool isHovered; + SpaceStatus status; List outgoingConnections = []; // Connections from this space Connection? incomingConnection; // Connections to this space - SpaceModel( - {this.uuid, - this.spaceTuyaUuid, - required this.icon, - required this.name, - required this.isPrivate, - this.invitationCode, - this.parent, - this.community, - required this.children, - required this.position, - this.isHovered = false, - this.incomingConnection}); + SpaceModel({ + this.uuid, + this.spaceTuyaUuid, + required this.icon, + required this.name, + required this.isPrivate, + this.invitationCode, + this.parent, + this.community, + required this.children, + required this.position, + this.isHovered = false, + this.incomingConnection, + this.status = SpaceStatus.unchanged, + }); factory SpaceModel.fromJson(Map json) { // Create SpaceModel instance first @@ -55,7 +60,8 @@ class SpaceModel { // Add incomingConnection to the instance after creation if (json['incomingConnections'] != null && json['incomingConnections'] is List && - (json['incomingConnections'] as List).isNotEmpty && instance.parent != null ) { + (json['incomingConnections'] as List).isNotEmpty && + instance.parent != null) { final conn = json['incomingConnections'][0]; instance.incomingConnection = Connection( startSpace: instance.parent ?? instance, // Parent space diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 79d57072..95d2e9d3 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -45,6 +45,13 @@ class SpaceManagementPageState extends State { if (state is SpaceManagementLoading) { return const Center(child: CircularProgressIndicator()); } else if (state is SpaceManagementLoaded) { + int selectedIndex = state.communities.indexWhere( + (community) => community.uuid == selectedCommunity?.uuid, + ); + if (selectedIndex != -1) { + selectedCommunity = state.communities[selectedIndex]; + } + return LoadedSpaceView( communities: state.communities, selectedCommunity: selectedCommunity, diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 27381a61..9fb20606 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -193,6 +193,9 @@ class _CommunityStructureAreaState extends State { void _updateNodePosition(SpaceModel node, Offset newPosition) { setState(() { node.position = newPosition; + if (node.status != SpaceStatus.newSpace) { + node.status = SpaceStatus.modified; // Mark as modified + } if (node.position.dx >= canvasWidth - 200) { canvasWidth += 200; } @@ -244,6 +247,7 @@ class _CommunityStructureAreaState extends State { position: centerPosition, isPrivate: false, children: [], + status: SpaceStatus.newSpace, ); if (parentIndex != null && direction != null) { @@ -330,14 +334,21 @@ class _CommunityStructureAreaState extends State { return; } - // Prepare spaces and connections data - List spaceList = spaces; + List spacesToSave = spaces.where((space) { + return space.status == SpaceStatus.newSpace || space.status == SpaceStatus.modified; + }).toList(); + + if (spacesToSave.isEmpty) { + print("No new or modified spaces to save."); + return; + } + String communityUuid = widget.selectedCommunity!.uuid; - // Dispatch the save event context.read().add(SaveSpacesEvent( - spaces: spaceList, + spaces: spacesToSave, communityUuid: communityUuid, )); } + } From a464788e88262ec176f5e88296765b596075c17d Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 19 Nov 2024 15:32:33 +0400 Subject: [PATCH 065/122] selecting space selects community --- .../widgets/sidebar_widget.dart | 42 +++++++++---------- .../widgets/space_tile_widget.dart | 6 +++ 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 2771ff09..10f03d4a 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -17,8 +17,7 @@ class SidebarWidget extends StatefulWidget { final Function(CommunityModel)? onCommunitySelected; final List communities; - const SidebarWidget( - {super.key, this.onCommunitySelected, required this.communities}); + const SidebarWidget({super.key, this.onCommunitySelected, required this.communities}); @override _SidebarWidgetState createState() => _SidebarWidgetState(); @@ -38,8 +37,7 @@ class _SidebarWidgetState extends State { showDialog( context: parentContext, builder: (context) => CreateCommunityDialog( - onCreateCommunity: - (String communityName, String description, String regionId) { + onCreateCommunity: (String communityName, String description, String regionId) { parentContext.read().add( CreateCommunityEvent( name: communityName, @@ -65,12 +63,11 @@ class _SidebarWidgetState extends State { return widget.communities.where((community) { final containsQueryInCommunity = community.name.toLowerCase().contains(_searchQuery.toLowerCase()); - final containsQueryInSpaces = community.spaces - .any((space) => _containsQuery(space, _searchQuery.toLowerCase())); + final containsQueryInSpaces = + community.spaces.any((space) => _containsQuery(space, _searchQuery.toLowerCase())); if (containsQueryInCommunity || containsQueryInSpaces) { - _selectedCommunityUuid = - community.uuid; // Set the community to expanded + _selectedCommunityUuid = community.uuid; // Set the community to expanded } return containsQueryInCommunity || containsQueryInSpaces; @@ -80,8 +77,8 @@ class _SidebarWidgetState extends State { // 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 + final matchesChildren = + space.children.any((child) => _containsQuery(child, query)); // Recursive check for children // If the space or any of its children match the query, expand this space if (matchesSpace || matchesChildren) { @@ -115,8 +112,7 @@ class _SidebarWidgetState extends State { width: 300, decoration: subSectionContainerDecoration, child: Column( - mainAxisSize: - MainAxisSize.min, // Ensures the Column only takes necessary height + mainAxisSize: MainAxisSize.min, // Ensures the Column only takes necessary height crossAxisAlignment: CrossAxisAlignment.start, children: [ // Communities title with the add button @@ -175,8 +171,8 @@ class _SidebarWidgetState extends State { Widget _buildCommunityTile(CommunityModel community) { bool hasChildren = community.spaces.isNotEmpty; - bool isSelectedCommunity = _selectedCommunityUuid == - community.uuid; // Check if this community is selected + bool isSelectedCommunity = + _selectedCommunityUuid == community.uuid; // Check if this community is selected return CommunityTile( title: community.name, isSelected: isSelectedCommunity, @@ -194,24 +190,28 @@ class _SidebarWidgetState extends State { _handleExpansionChange(community.uuid, expanded); }, children: hasChildren - ? community.spaces.map((space) => _buildSpaceTile(space)).toList() + ? community.spaces.map((space) => _buildSpaceTile(space, community)).toList() : null, // Render spaces within the community ); } - Widget _buildSpaceTile(SpaceModel space) { - bool isSelectedSpace = - _isSpaceOrChildSelected(space); // Check if space should be expanded + Widget _buildSpaceTile(SpaceModel space, CommunityModel community) { + bool isSelectedSpace = _isSpaceOrChildSelected(space); // Check if space should be expanded return SpaceTile( title: space.name, + isSelected: isSelectedSpace, initiallyExpanded: isSelectedSpace, onExpansionChanged: (bool expanded) { _handleExpansionChange(space.uuid ?? '', expanded); }, + onItemSelected: () => { + if (widget.onCommunitySelected != null) + { + widget.onCommunitySelected!(community) // Pass the entire community + } + }, children: space.children.isNotEmpty - ? space.children - .map((childSpace) => _buildSpaceTile(childSpace)) - .toList() + ? space.children.map((childSpace) => _buildSpaceTile(childSpace, community)).toList() : [], // Recursively render child spaces if available ); } diff --git a/lib/pages/spaces_management/widgets/space_tile_widget.dart b/lib/pages/spaces_management/widgets/space_tile_widget.dart index e40fc5ce..95311b9a 100644 --- a/lib/pages/spaces_management/widgets/space_tile_widget.dart +++ b/lib/pages/spaces_management/widgets/space_tile_widget.dart @@ -3,15 +3,20 @@ import 'package:syncrow_web/common/custom_expansion_tile.dart'; class SpaceTile extends StatefulWidget { final String title; + final bool isSelected; + final bool initiallyExpanded; final ValueChanged onExpansionChanged; final List? children; + final Function() onItemSelected; const SpaceTile({ super.key, required this.title, required this.initiallyExpanded, required this.onExpansionChanged, + required this.onItemSelected, + required this.isSelected, this.children, }); @@ -34,6 +39,7 @@ class _SpaceTileState extends State { isSelected: false, title: widget.title, initiallyExpanded: _isExpanded, + onItemSelected: widget.onItemSelected, onExpansionChanged: (bool expanded) { setState(() { _isExpanded = expanded; From 8a2efb2694de40f33409a1d340424e963806508c Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 19 Nov 2024 18:16:53 +0400 Subject: [PATCH 066/122] pass selected space --- .../view/spaces_management_page.dart | 7 +++++++ .../widgets/community_structure_widget.dart | 6 ++++-- .../widgets/loaded_space_widget.dart | 7 +++++++ .../widgets/sidebar_widget.dart | 17 +++++++++++------ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 95d2e9d3..fc01f5ac 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -20,6 +20,7 @@ class SpaceManagementPage extends StatefulWidget { class SpaceManagementPageState extends State { CommunityModel? selectedCommunity; + SpaceModel? selectedSpace; final CommunitySpaceManagementApi _api = CommunitySpaceManagementApi(); Map> communitySpaces = {}; double canvasWidth = 1000; @@ -55,11 +56,17 @@ class SpaceManagementPageState extends State { return LoadedSpaceView( communities: state.communities, selectedCommunity: selectedCommunity, + selectedSpace: selectedSpace, onCommunitySelected: (community) { setState(() { selectedCommunity = community; }); }, + onSpaceSelected: (space) { + setState(() { + selectedSpace = space; + }); + }, ); } else if (state is SpaceManagementError) { return Center(child: Text('Error: ${state.errorMessage}')); diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 9fb20606..26e268c7 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -14,11 +14,14 @@ import 'package:syncrow_web/utils/color_manager.dart'; class CommunityStructureArea extends StatefulWidget { final CommunityModel? selectedCommunity; + final SpaceModel? selectedSpace; + final List spaces; final List connections; CommunityStructureArea({ this.selectedCommunity, + this.selectedSpace, required this.spaces, required this.connections, }); @@ -161,7 +164,7 @@ class _CommunityStructureAreaState extends State { ), if (widget.selectedCommunity != null) Text( - widget.selectedCommunity!.name, + widget.selectedCommunity?.name ?? '', style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), ), ], @@ -350,5 +353,4 @@ class _CommunityStructureAreaState extends State { communityUuid: communityUuid, )); } - } diff --git a/lib/pages/spaces_management/widgets/loaded_space_widget.dart b/lib/pages/spaces_management/widgets/loaded_space_widget.dart index 3b3ebeab..f3318107 100644 --- a/lib/pages/spaces_management/widgets/loaded_space_widget.dart +++ b/lib/pages/spaces_management/widgets/loaded_space_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/community_structure_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/gradient_canvas_border_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/sidebar_widget.dart'; @@ -7,13 +8,17 @@ import 'package:syncrow_web/pages/spaces_management/widgets/sidebar_widget.dart' class LoadedSpaceView extends StatefulWidget { final List communities; final CommunityModel? selectedCommunity; + final SpaceModel? selectedSpace; final ValueChanged onCommunitySelected; + final ValueChanged onSpaceSelected; const LoadedSpaceView({ Key? key, required this.communities, this.selectedCommunity, + this.selectedSpace, required this.onCommunitySelected, + required this.onSpaceSelected, }) : super(key: key); @override @@ -31,9 +36,11 @@ class _LoadedStateViewState extends State { SidebarWidget( communities: widget.communities, onCommunitySelected: widget.onCommunitySelected, + onSpaceSelected: widget.onSpaceSelected, ), CommunityStructureArea( selectedCommunity: widget.selectedCommunity, + selectedSpace: widget.selectedSpace, spaces: widget.selectedCommunity?.spaces ?? [], connections: [], ), diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 10f03d4a..f50663b3 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -15,9 +15,11 @@ import 'package:syncrow_web/utils/style.dart'; class SidebarWidget extends StatefulWidget { final Function(CommunityModel)? onCommunitySelected; + final Function(SpaceModel)? onSpaceSelected; final List communities; - const SidebarWidget({super.key, this.onCommunitySelected, required this.communities}); + const SidebarWidget( + {super.key, this.onCommunitySelected, this.onSpaceSelected, required this.communities}); @override _SidebarWidgetState createState() => _SidebarWidgetState(); @@ -204,11 +206,14 @@ class _SidebarWidgetState extends State { onExpansionChanged: (bool expanded) { _handleExpansionChange(space.uuid ?? '', expanded); }, - onItemSelected: () => { - if (widget.onCommunitySelected != null) - { - widget.onCommunitySelected!(community) // Pass the entire community - } + onItemSelected: () { + if (widget.onCommunitySelected != null || widget.onSpaceSelected != null) { + widget.onCommunitySelected!(community); // Pass the entire community + } + + if (widget.onSpaceSelected != null) { + widget.onSpaceSelected!(space); // Pass the entire community + } }, children: space.children.isNotEmpty ? space.children.map((childSpace) => _buildSpaceTile(childSpace, community)).toList() From c3a5b0b351ddd671057a3e1b6fbf046648d6d067 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 19 Nov 2024 18:52:26 +0400 Subject: [PATCH 067/122] selected space/community become dark --- lib/common/custom_expansion_tile.dart | 12 ++----- .../widgets/sidebar_widget.dart | 34 ++++++++++++------- .../widgets/space_tile_widget.dart | 2 +- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/lib/common/custom_expansion_tile.dart b/lib/common/custom_expansion_tile.dart index e5dc2e16..a6412b3c 100644 --- a/lib/common/custom_expansion_tile.dart +++ b/lib/common/custom_expansion_tile.dart @@ -84,9 +84,7 @@ class CustomExpansionTileState extends State { }); }, child: Icon( - _isExpanded - ? Icons.keyboard_arrow_down - : Icons.keyboard_arrow_right, + _isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right, color: Colors.grey, size: 16.0, // Adjusted size for better alignment ), @@ -95,7 +93,6 @@ class CustomExpansionTileState extends State { Expanded( child: GestureDetector( onTap: () { - // Triggerxq the onItemSelected callback when the name is tapped if (widget.onItemSelected != null) { widget.onItemSelected!(); } @@ -106,8 +103,7 @@ class CustomExpansionTileState extends State { style: TextStyle( color: widget.isSelected ? Colors.black // Change color to black when selected - : ColorsManager - .lightGrayColor, // Gray when not selected + : ColorsManager.lightGrayColor, // Gray when not selected fontWeight: FontWeight.w400, ), ), @@ -116,9 +112,7 @@ class CustomExpansionTileState extends State { ], ), // The expanded section (children) that shows when the tile is expanded - if (_isExpanded && - widget.children != null && - widget.children!.isNotEmpty) + if (_isExpanded && widget.children != null && widget.children!.isNotEmpty) Padding( padding: const EdgeInsets.only(left: 48.0), // Indented children child: Column( diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index f50663b3..58782f6b 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -29,6 +29,7 @@ class _SidebarWidgetState extends State { String _searchQuery = ''; // Track search query String? _selectedCommunityUuid; String? _selectedSpaceUuid; + String? _selectedId; @override void initState() { @@ -173,15 +174,19 @@ class _SidebarWidgetState extends State { Widget _buildCommunityTile(CommunityModel community) { bool hasChildren = community.spaces.isNotEmpty; - bool isSelectedCommunity = - _selectedCommunityUuid == community.uuid; // Check if this community is selected + bool isSelectedCommunity = _selectedCommunityUuid == community.uuid; + + // Check if this community is selected return CommunityTile( title: community.name, - isSelected: isSelectedCommunity, + key: ValueKey(community.uuid), + isSelected: _selectedId == community.uuid, isExpanded: false, onItemSelected: () { setState(() { - _selectedSpaceUuid = community.uuid; // Update the selected community + _selectedId = community.uuid; + _selectedCommunityUuid = community.uuid; + _selectedSpaceUuid = null; // Update the selected community }); if (widget.onCommunitySelected != null) { @@ -198,21 +203,26 @@ class _SidebarWidgetState extends State { } Widget _buildSpaceTile(SpaceModel space, CommunityModel community) { - bool isSelectedSpace = _isSpaceOrChildSelected(space); // Check if space should be expanded + bool isExpandedSpace = _isSpaceOrChildSelected(space); + bool isSelectedSpace = _selectedSpaceUuid == space.uuid; +// Check if space should be expanded return SpaceTile( title: space.name, - isSelected: isSelectedSpace, - initiallyExpanded: isSelectedSpace, + key: ValueKey(space.uuid), + isSelected: _selectedId == space.uuid, + initiallyExpanded: isExpandedSpace, onExpansionChanged: (bool expanded) { _handleExpansionChange(space.uuid ?? '', expanded); }, onItemSelected: () { - if (widget.onCommunitySelected != null || widget.onSpaceSelected != null) { - widget.onCommunitySelected!(community); // Pass the entire community - } - + setState(() { + _selectedId = space.uuid; + _selectedSpaceUuid = space.uuid; + _selectedCommunityUuid = community.uuid; // Update selected community + }); + print(_selectedSpaceUuid); if (widget.onSpaceSelected != null) { - widget.onSpaceSelected!(space); // Pass the entire community + widget.onSpaceSelected!(space); } }, children: space.children.isNotEmpty diff --git a/lib/pages/spaces_management/widgets/space_tile_widget.dart b/lib/pages/spaces_management/widgets/space_tile_widget.dart index 95311b9a..80753f9d 100644 --- a/lib/pages/spaces_management/widgets/space_tile_widget.dart +++ b/lib/pages/spaces_management/widgets/space_tile_widget.dart @@ -36,7 +36,7 @@ class _SpaceTileState extends State { @override Widget build(BuildContext context) { return CustomExpansionTile( - isSelected: false, + isSelected: widget.isSelected, title: widget.title, initiallyExpanded: _isExpanded, onItemSelected: widget.onItemSelected, From bf0dc7b56cbf48ddbe56863b86b05354b7c81f8e Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 20 Nov 2024 12:31:27 +0400 Subject: [PATCH 068/122] Added assets --- assets/icons/1G_touch_switch.svg | 5 ++ assets/icons/2G_touch_switch.svg | 6 ++ assets/icons/3G_touch_switch.svg | 7 ++ assets/icons/garage_opener.svg | 135 +++++++++++++++++++++++++++++ assets/icons/power_clamp.svg | 43 +++++++++ assets/icons/water_leak_sensor.svg | 8 ++ lib/utils/constants/assets.dart | 56 ++++++------ 7 files changed, 234 insertions(+), 26 deletions(-) create mode 100644 assets/icons/1G_touch_switch.svg create mode 100644 assets/icons/2G_touch_switch.svg create mode 100644 assets/icons/3G_touch_switch.svg create mode 100644 assets/icons/garage_opener.svg create mode 100644 assets/icons/power_clamp.svg create mode 100644 assets/icons/water_leak_sensor.svg diff --git a/assets/icons/1G_touch_switch.svg b/assets/icons/1G_touch_switch.svg new file mode 100644 index 00000000..45679cfb --- /dev/null +++ b/assets/icons/1G_touch_switch.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/2G_touch_switch.svg b/assets/icons/2G_touch_switch.svg new file mode 100644 index 00000000..1893fd21 --- /dev/null +++ b/assets/icons/2G_touch_switch.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/3G_touch_switch.svg b/assets/icons/3G_touch_switch.svg new file mode 100644 index 00000000..4a271a4a --- /dev/null +++ b/assets/icons/3G_touch_switch.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/garage_opener.svg b/assets/icons/garage_opener.svg new file mode 100644 index 00000000..20a31aca --- /dev/null +++ b/assets/icons/garage_opener.svg @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/power_clamp.svg b/assets/icons/power_clamp.svg new file mode 100644 index 00000000..a2f21e3a --- /dev/null +++ b/assets/icons/power_clamp.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/water_leak_sensor.svg b/assets/icons/water_leak_sensor.svg new file mode 100644 index 00000000..8f67d0ee --- /dev/null +++ b/assets/icons/water_leak_sensor.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index ed30fd74..c57cbdb1 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -16,8 +16,7 @@ class Assets { static const String invisiblePassword = "assets/images/Password_invisible.svg"; static const String visiblePassword = "assets/images/password_visible.svg"; static const String accessIcon = "assets/images/access_icon.svg"; - static const String spaseManagementIcon = - "assets/images/spase_management_icon.svg"; + static const String spaseManagementIcon = "assets/images/spase_management_icon.svg"; static const String devicesIcon = "assets/images/devices_icon.svg"; static const String moveinIcon = "assets/images/movein_icon.svg"; static const String constructionIcon = "assets/images/construction_icon.svg"; @@ -30,15 +29,13 @@ class Assets { static const String emptyTable = "assets/images/empty_table.svg"; // General assets - static const String motionlessDetection = - "assets/icons/motionless_detection.svg"; + static const String motionlessDetection = "assets/icons/motionless_detection.svg"; static const String acHeating = "assets/icons/ac_heating.svg"; static const String acPowerOff = "assets/icons/ac_power_off.svg"; static const String acFanMiddle = "assets/icons/ac_fan_middle.svg"; static const String switchAlarmSound = "assets/icons/switch_alarm_sound.svg"; static const String resetOff = "assets/icons/reset_off.svg"; - static const String sensitivityOperationIcon = - "assets/icons/sesitivity_operation_icon.svg"; + static const String sensitivityOperationIcon = "assets/icons/sesitivity_operation_icon.svg"; static const String motionDetection = "assets/icons/motion_detection.svg"; static const String freezing = "assets/icons/freezing.svg"; static const String indicator = "assets/icons/indicator.svg"; @@ -59,40 +56,41 @@ class Assets { static const String celsiusDegrees = "assets/icons/celsius_degrees.svg"; static const String masterState = "assets/icons/master_state.svg"; static const String acPower = "assets/icons/ac_power.svg"; - static const String farDetectionFunction = - "assets/icons/far_detection_function.svg"; + static const String farDetectionFunction = "assets/icons/far_detection_function.svg"; static const String nobodyTime = "assets/icons/nobody_time.svg"; // Automation functions - static const String tempPasswordUnlock = "assets/icons/automation_functions/temp_password_unlock.svg"; - static const String doorlockNormalOpen = "assets/icons/automation_functions/doorlock_normal_open.svg"; + static const String tempPasswordUnlock = + "assets/icons/automation_functions/temp_password_unlock.svg"; + static const String doorlockNormalOpen = + "assets/icons/automation_functions/doorlock_normal_open.svg"; static const String doorbell = "assets/icons/automation_functions/doorbell.svg"; - static const String remoteUnlockViaApp = "assets/icons/automation_functions/remote_unlock_via_app.svg"; + static const String remoteUnlockViaApp = + "assets/icons/automation_functions/remote_unlock_via_app.svg"; static const String doubleLock = "assets/icons/automation_functions/double_lock.svg"; static const String selfTestResult = "assets/icons/automation_functions/self_test_result.svg"; static const String lockAlarm = "assets/icons/automation_functions/lock_alarm.svg"; static const String presenceState = "assets/icons/automation_functions/presence_state.svg"; static const String currentTemp = "assets/icons/automation_functions/current_temp.svg"; static const String presence = "assets/icons/automation_functions/presence.svg"; - static const String residualElectricity = "assets/icons/automation_functions/residual_electricity.svg"; + static const String residualElectricity = + "assets/icons/automation_functions/residual_electricity.svg"; static const String hijackAlarm = "assets/icons/automation_functions/hijack_alarm.svg"; static const String passwordUnlock = "assets/icons/automation_functions/password_unlock.svg"; - static const String remoteUnlockRequest = "assets/icons/automation_functions/remote_unlock_req.svg"; + static const String remoteUnlockRequest = + "assets/icons/automation_functions/remote_unlock_req.svg"; static const String cardUnlock = "assets/icons/automation_functions/card_unlock.svg"; static const String motion = "assets/icons/automation_functions/motion.svg"; - static const String fingerprintUnlock = "assets/icons/automation_functions/fingerprint_unlock.svg"; + static const String fingerprintUnlock = + "assets/icons/automation_functions/fingerprint_unlock.svg"; // Presence Sensor Assets static const String sensorMotionIcon = "assets/icons/sensor_motion_ic.svg"; - static const String sensorPresenceIcon = - "assets/icons/sensor_presence_ic.svg"; + static const String sensorPresenceIcon = "assets/icons/sensor_presence_ic.svg"; static const String sensorVacantIcon = "assets/icons/sensor_vacant_ic.svg"; - static const String illuminanceRecordIcon = - "assets/icons/illuminance_record_ic.svg"; - static const String presenceRecordIcon = - "assets/icons/presence_record_ic.svg"; - static const String helpDescriptionIcon = - "assets/icons/help_description_ic.svg"; + static const String illuminanceRecordIcon = "assets/icons/illuminance_record_ic.svg"; + static const String presenceRecordIcon = "assets/icons/presence_record_ic.svg"; + static const String helpDescriptionIcon = "assets/icons/help_description_ic.svg"; static const String lightPulp = "assets/icons/light_pulb.svg"; static const String acDevice = "assets/icons/ac_device.svg"; @@ -142,12 +140,10 @@ class Assets { static const String unit = 'assets/icons/unit_icon.svg'; static const String villa = 'assets/icons/villa_icon.svg'; static const String iconEdit = 'assets/icons/icon_edit_icon.svg'; - static const String textFieldSearch = - 'assets/icons/textfield_search_icon.svg'; + static const String textFieldSearch = 'assets/icons/textfield_search_icon.svg'; static const String roundedAddIcon = 'assets/icons/rounded_add_icon.svg'; static const String addIcon = 'assets/icons/add_icon.svg'; - static const String smartThermostatIcon = - 'assets/icons/smart_thermostat_icon.svg'; + static const String smartThermostatIcon = 'assets/icons/smart_thermostat_icon.svg'; static const String smartLightIcon = 'assets/icons/smart_light_icon.svg'; static const String presenceSensor = 'assets/icons/presence_sensor.svg'; static const String Gang3SwitchIcon = 'assets/icons/3_Gang_switch_icon.svg'; @@ -229,4 +225,12 @@ class Assets { //assets/icons/sos_normal.svg static const String sosNormal = 'assets/icons/sos_normal.svg'; + + static const String waterLeakSensor = 'assets/icons/water_leak_sensor.svg'; + static const String powerClamp = 'assets/icons/power_clamp.svg'; + static const String threeTouchSwitch = 'assets/icons/3G_touch_switch.svg'; + static const String twoTouchSwitch = 'assets/icons/2G_touch_switch.svg'; + static const String oneTouchSwitch = 'assets/icons/1G_touch_switch.svg'; + + static const String garageDoor = 'assets/icons/garage_opener.svg'; } From 9affae02695f8da8575fabff812d5e36d00d0ed4 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 20 Nov 2024 20:10:39 +0400 Subject: [PATCH 069/122] add device type comes from products --- .../bloc/space_management_bloc.dart | 35 ++++- .../bloc/space_management_event.dart | 3 +- .../bloc/space_management_state.dart | 7 +- .../model/product_model.dart | 70 +++++++++ .../view/dialogs/create_space_dialog.dart | 60 ++++---- .../view/spaces_management_page.dart | 8 +- .../widgets/add_device_type_widget.dart | 135 +++++++++++------- .../widgets/community_structure_widget.dart | 4 + .../widgets/counter_widget.dart | 24 +++- .../widgets/loaded_space_widget.dart | 4 + .../widgets/sidebar_widget.dart | 1 - 11 files changed, 256 insertions(+), 95 deletions(-) create mode 100644 lib/pages/spaces_management/model/product_model.dart diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 0df905d9..831c6f30 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -1,18 +1,41 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; +import 'package:syncrow_web/services/product_api.dart'; import 'package:syncrow_web/services/space_mana_api.dart'; class SpaceManagementBloc extends Bloc { final CommunitySpaceManagementApi _api; + final ProductApi _productApi; - SpaceManagementBloc(this._api) : super(SpaceManagementInitial()) { + List? _cachedProducts; + + SpaceManagementBloc(this._api, this._productApi) : super(SpaceManagementInitial()) { on(_onLoadCommunityAndSpaces); on(_onUpdateSpacePosition); on(_onCreateCommunity); on(_onSaveSpaces); + on(_onFetchProducts); + } + + void _onFetchProducts( + FetchProductsEvent event, + Emitter emit, + ) async { + if (_cachedProducts != null) { + // Products are already cached, no need to fetch again + return; + } + + try { + final products = await _productApi.fetchProducts(); + _cachedProducts = products; // Cache the products locally + } catch (e) { + emit(SpaceManagementError('Error fetching products: $e')); + } } void _onLoadCommunityAndSpaces( @@ -21,6 +44,11 @@ class SpaceManagementBloc extends Bloc communities = await _api.fetchCommunities(); @@ -40,7 +68,7 @@ class SpaceManagementBloc extends Bloc.from(previousState.communities) ..add(newCommunity); - emit(SpaceManagementLoaded(communities: updatedCommunities)); + emit(SpaceManagementLoaded( + communities: updatedCommunities, products: _cachedProducts ?? [])); } } else { emit(const SpaceManagementError('Error creating community')); diff --git a/lib/pages/spaces_management/bloc/space_management_event.dart b/lib/pages/spaces_management/bloc/space_management_event.dart index cc0ba9b2..2906849f 100644 --- a/lib/pages/spaces_management/bloc/space_management_event.dart +++ b/lib/pages/spaces_management/bloc/space_management_event.dart @@ -74,6 +74,7 @@ class CreateCommunityEvent extends SpaceManagementEvent { List get props => [name, description, regionId]; } +class FetchProductsEvent extends SpaceManagementEvent {} class LoadSpaceHierarchyEvent extends SpaceManagementEvent { final String communityId; @@ -82,4 +83,4 @@ class LoadSpaceHierarchyEvent extends SpaceManagementEvent { @override List get props => [communityId]; -} \ No newline at end of file +} diff --git a/lib/pages/spaces_management/bloc/space_management_state.dart b/lib/pages/spaces_management/bloc/space_management_state.dart index 3d859547..0e05b42e 100644 --- a/lib/pages/spaces_management/bloc/space_management_state.dart +++ b/lib/pages/spaces_management/bloc/space_management_state.dart @@ -1,5 +1,6 @@ import 'package:equatable/equatable.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; abstract class SpaceManagementState extends Equatable { @@ -15,11 +16,9 @@ class SpaceManagementLoading extends SpaceManagementState {} class SpaceManagementLoaded extends SpaceManagementState { final List communities; + final List products; // Include products in the state - const SpaceManagementLoaded({required this.communities}); - - @override - List get props => [communities]; + SpaceManagementLoaded({required this.communities, required this.products}); } class SpaceCreationSuccess extends SpaceManagementState { diff --git a/lib/pages/spaces_management/model/product_model.dart b/lib/pages/spaces_management/model/product_model.dart new file mode 100644 index 00000000..5a0e92e1 --- /dev/null +++ b/lib/pages/spaces_management/model/product_model.dart @@ -0,0 +1,70 @@ +import 'package:syncrow_web/utils/constants/assets.dart'; + +class ProductModel { + final String uuid; + final String catName; + String? name; + final String prodId; + final String prodType; + String? icon; + + ProductModel({ + required this.uuid, + required this.catName, + required this.prodId, + required this.prodType, + required this.name, + this.icon, + }); + + // Factory method to create a Product from JSON + factory ProductModel.fromMap(Map json) { + String icon = _mapIconToProduct(json['prodType']); + return ProductModel( + uuid: json['uuid'], + catName: json['catName'], + prodId: json['prodId'], + prodType: json['prodType'], + name: json['name'] ?? '', + icon: _mapIconToProduct(json['prodType'])); + } + + // Method to convert a Product to JSON + Map toMap() { + return { + 'uuid': uuid, + 'catName': catName, + 'prodId': prodId, + 'prodType': prodType, + }; + } + + static String _mapIconToProduct(String prodType) { + const iconMapping = { + '1G': Assets.Gang1SwitchIcon, + '1GT': Assets.oneTouchSwitch, + '2G': Assets.Gang2SwitchIcon, + '2GT': Assets.twoTouchSwitch, + '3G': Assets.Gang3SwitchIcon, + '3GT': Assets.threeTouchSwitch, + 'CUR': Assets.curtain, + 'GD': Assets.garageDoor, + 'GW': Assets.SmartGatewayIcon, + 'DL': Assets.DoorLockIcon, + 'WL': Assets.waterLeakSensor, + 'WH': Assets.waterHeater, + 'AC': Assets.ac, + 'CPS': Assets.presenceSensor, + 'PC': Assets.powerClamp, + 'WPS': Assets.presenceSensor, + 'DS': Assets.doorSensor + }; + + return iconMapping[prodType] ?? Assets.presenceSensor; + } + + @override + String toString() { + return 'ProductModel(uuid: $uuid, catName: $catName, prodId: $prodId, prodType: $prodType, name: $name, icon: $icon)'; + } +} diff --git a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart index a134792c..4351203f 100644 --- a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart @@ -2,14 +2,16 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.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/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/add_device_type_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class CreateSpaceDialog extends StatefulWidget { final Function(String, String) onCreateSpace; + final List? products; - const CreateSpaceDialog({super.key, required this.onCreateSpace}); + const CreateSpaceDialog({super.key, required this.onCreateSpace, this.products}); @override CreateSpaceDialogState createState() => CreateSpaceDialogState(); @@ -18,6 +20,7 @@ class CreateSpaceDialog extends StatefulWidget { class CreateSpaceDialogState extends State { String selectedIcon = Assets.location; String enteredName = ''; + Map selectedProducts = {}; @override Widget build(BuildContext context) { @@ -60,8 +63,7 @@ class CreateSpaceDialogState extends State { color: Colors.white, shape: BoxShape.circle, ), - child: SvgPicture.asset(Assets.iconEdit, - width: 10, height: 10), + child: SvgPicture.asset(Assets.iconEdit, width: 10, height: 10), ), ), ), @@ -89,8 +91,8 @@ class CreateSpaceDialogState extends State { enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide( - color: Color( - 0xFFF5F6F7), // Light gray color when enabled (not focused) + color: + Color(0xFFF5F6F7), // Light gray color when enabled (not focused) width: 1.5, ), ), @@ -98,8 +100,7 @@ class CreateSpaceDialogState extends State { focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide( - color: Color( - 0xFFF5F6F7), // Primary color when focused + color: Color(0xFFF5F6F7), // Primary color when focused width: 1.5, ), ), @@ -107,8 +108,7 @@ class CreateSpaceDialogState extends State { disabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide( - color: Color( - 0xFFF5F6F7), // Light gray for disabled state + color: Color(0xFFF5F6F7), // Light gray for disabled state width: 1.5, ), ), @@ -116,8 +116,7 @@ class CreateSpaceDialogState extends State { errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide( - color: Color( - 0xFFF5F6F7), // Red border when there's an error + color: Color(0xFFF5F6F7), // Red border when there's an error width: 1.5, ), ), @@ -125,8 +124,7 @@ class CreateSpaceDialogState extends State { focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide( - color: Color( - 0xFFF5F6F7), // Red border when there's an error and it's focused + color: ColorsManager.boxColor, // Red border when there's an error and it's focused width: 1.5, ), ), @@ -138,36 +136,37 @@ class CreateSpaceDialogState extends State { onPressed: () { showDialog( context: context, - builder: (context) => const AddDeviceWidget(), + builder: (context) => AddDeviceWidget( + products: widget.products, + onProductsSelected: (selectedProductsMap) { + setState(() { + selectedProducts = selectedProductsMap; + }); + }, + ), ); - // Logic to assign devices or select a model }, style: ElevatedButton.styleFrom( backgroundColor: ColorsManager.textFieldGreyColor, - padding: const EdgeInsets.symmetric( - horizontal: 16, vertical: 20), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), shape: RoundedRectangleBorder( side: const BorderSide( // Add border side here - color: Color( - 0xFFE5E5E5), // Define your desired border color + color: ColorsManager.neutralGray, // Define your desired border color width: 2.0, // Define border width ), borderRadius: BorderRadius.circular(20)), ), child: Row( - mainAxisSize: MainAxisSize - .min, // Adjust the button size to fit the content + mainAxisSize: + MainAxisSize.min, // Adjust the button size to fit the content children: [ SvgPicture.asset( - Assets - .addIcon, // Replace with your actual icon path + Assets.addIcon, // Replace with your actual icon path width: 20, // Set the size of the icon height: 20, ), - const SizedBox( - width: - 8), // Add spacing between the icon and text + const SizedBox(width: 8), // Add spacing between the icon and text const Text( 'Add devices / Assign a space model', style: TextStyle( @@ -175,15 +174,13 @@ class CreateSpaceDialogState extends State { fontSize: 16, fontFamily: 'Aftika', fontWeight: FontWeight.w400, - height: - 1.5, // Adjust line height for better spacing + height: 1.5, // Adjust line height for better spacing ), ), const SizedBox(width: 8), ], ), ), - ], ), ), @@ -207,13 +204,12 @@ class CreateSpaceDialogState extends State { child: DefaultButton( onPressed: () { if (enteredName.isNotEmpty) { - widget.onCreateSpace(enteredName, - selectedIcon); // Pass the name and icon back + widget.onCreateSpace(enteredName, selectedIcon); // Pass the name and icon back Navigator.of(context).pop(); // Close the dialog } }, backgroundColor: ColorsManager.secondaryColor, - foregroundColor: Colors.white, + foregroundColor: ColorsManager.whiteColors, child: const Text('OK'), ), ), diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index fc01f5ac..49c4b064 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -5,9 +5,11 @@ import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event. import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_data_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/loaded_space_widget.dart'; +import 'package:syncrow_web/services/product_api.dart'; import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; @@ -22,11 +24,14 @@ class SpaceManagementPageState extends State { CommunityModel? selectedCommunity; SpaceModel? selectedSpace; final CommunitySpaceManagementApi _api = CommunitySpaceManagementApi(); + final ProductApi _productApi = ProductApi(); Map> communitySpaces = {}; double canvasWidth = 1000; double canvasHeight = 1000; List spaces = []; List connections = []; + List products = []; + bool isProductDataLoaded = false; @override void initState() { @@ -37,7 +42,7 @@ class SpaceManagementPageState extends State { Widget build(BuildContext context) { return BlocProvider( create: (context) => - SpaceManagementBloc(CommunitySpaceManagementApi())..add(LoadCommunityAndSpacesEvent()), + SpaceManagementBloc(_api, _productApi)..add(LoadCommunityAndSpacesEvent()), child: WebScaffold( appBarTitle: Text('Space Management', style: Theme.of(context).textTheme.headlineLarge), enableMenuSidebar: false, @@ -57,6 +62,7 @@ class SpaceManagementPageState extends State { communities: state.communities, selectedCommunity: selectedCommunity, selectedSpace: selectedSpace, + products:state.products, onCommunitySelected: (community) { setState(() { selectedCommunity = community; diff --git a/lib/pages/spaces_management/widgets/add_device_type_widget.dart b/lib/pages/spaces_management/widgets/add_device_type_widget.dart index be0eb735..e0686dcb 100644 --- a/lib/pages/spaces_management/widgets/add_device_type_widget.dart +++ b/lib/pages/spaces_management/widgets/add_device_type_widget.dart @@ -1,30 +1,44 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; -import 'package:syncrow_web/pages/device_managment/all_devices/models/device_type_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/counter_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; class AddDeviceWidget extends StatefulWidget { - const AddDeviceWidget({super.key}); + final List? products; + final ValueChanged>? onProductsSelected; + + const AddDeviceWidget({super.key, this.products, this.onProductsSelected}); @override _AddDeviceWidgetState createState() => _AddDeviceWidgetState(); } -// Create a static list of DeviceTypeModel -final List staticDeviceTypes = [ - DeviceTypeModel(name: 'Smart Light', icon: Assets.smartLightIcon), - DeviceTypeModel(name: 'Presence Sensor', icon: Assets.presenceSensor), - DeviceTypeModel(name: '3 Gang Smart switch', icon: Assets.Gang3SwitchIcon), - DeviceTypeModel(name: '2 Gang Smart switch', icon: Assets.Gang2SwitchIcon), - DeviceTypeModel(name: '1 Gang Smart switch', icon: Assets.Gang1SwitchIcon), - DeviceTypeModel(name: 'Smart Door Lock', icon: Assets.DoorLockIcon), - DeviceTypeModel(name: 'Smart Gateway', icon: Assets.SmartGatewayIcon) -]; - class _AddDeviceWidgetState extends State { + late ScrollController _scrollController; + Map productCounts = {}; + + @override + void initState() { + super.initState(); + _scrollController = ScrollController(); + + if (widget.products != null) { + for (var product in widget.products!) { + productCounts[product.uuid] = 0; + } + } + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { Size size = MediaQuery.of(context).size; @@ -33,38 +47,41 @@ class _AddDeviceWidgetState extends State { title: const Text('Add Devices'), backgroundColor: ColorsManager.whiteColors, content: Container( - width: size.width * 0.65, // Set width for the dialog - height: size.height * 0.57, // Set height for the dialog + width: size.width * 0.65, + height: size.height * 0.57, // Set width for the dialog color: ColorsManager.textFieldGreyColor, child: Column( children: [ const SizedBox(height: 16.0), Expanded( child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 20.0), // Add horizontal padding - child: GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 6, // Display 6 items in a row - mainAxisSpacing: 10, - crossAxisSpacing: 10, - childAspectRatio: 0.7, // Adjust the aspect ratio - ), - itemCount: staticDeviceTypes.length, - itemBuilder: (context, index) { - final deviceType = staticDeviceTypes[index]; - return _buildDeviceTypeTile(deviceType); - }, - ), - ), + padding: const EdgeInsets.symmetric(horizontal: 20.0), // Add horizontal padding + child: Scrollbar( + controller: _scrollController, + thumbVisibility: false, + child: GridView.builder( + controller: _scrollController, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 6, // Display 6 items in a row + mainAxisSpacing: 10, + crossAxisSpacing: 10, + childAspectRatio: 0.7, // Adjust the aspect ratio + ), + itemCount: widget.products?.length ?? 0, + itemBuilder: (context, index) { + final deviceType = widget.products![index]; + return _buildDeviceTypeTile(deviceType); + }, + ), + )), ), ], ), ), actions: [ Row( - mainAxisAlignment: MainAxisAlignment - .spaceBetween, // Align cancel to the left and continue to the right + mainAxisAlignment: + MainAxisAlignment.spaceBetween, // Align cancel to the left and continue to the right children: [ SizedBox( width: 200, // Define a specific width for the button @@ -94,42 +111,48 @@ class _AddDeviceWidgetState extends State { ); } - Widget _buildDeviceTypeTile(DeviceTypeModel deviceType) { + Widget _buildDeviceTypeTile(ProductModel? deviceType) { return SizedBox( - width: 90, - height: 150, // Increase height if needed + width: 75, + height: 90, // Increase height if needed child: Card( elevation: 2, color: ColorsManager.whiteColors, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + borderRadius: BorderRadius.circular(8), ), child: Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(6.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Fixed height container for the icon Container( - height: 70, // Fixed height for the icon + height: 80, + width: 80, + decoration: BoxDecoration( + shape: BoxShape.circle, // Make it circular + color: ColorsManager.textFieldGreyColor, // Background color of the circle + border: Border.all( + color: ColorsManager.neutralGray, // Border color + width: 2, // Border width + ), + ), // Fixed height for the icon child: Center( child: SvgPicture.asset( - deviceType.icon, - width: 40, - height: 40, + deviceType?.icon ?? Assets.sensors, + width: 45, + height: 45, ), ), ), const SizedBox(height: 8), // Fixed height container for the name - Container( + SizedBox( height: 35, // Fixed height for the text (adjust as needed) child: Text( - deviceType.name, - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - ), + deviceType?.name ?? '', + style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor), textAlign: TextAlign.center, maxLines: 2, // Allow up to 2 lines for long names overflow: TextOverflow.ellipsis, // Handle overflow @@ -137,7 +160,21 @@ class _AddDeviceWidgetState extends State { ), const SizedBox(height: 8), // The custom counter widget aligned at the bottom - CounterWidget(), + CounterWidget( + initialCount: 0, + onCountChanged: (newCount) { + setState(() { + if (newCount > 0) { + productCounts[deviceType!.uuid] = newCount; + } else { + productCounts.remove(deviceType!.uuid); + } + if (widget.onProductsSelected != null) { + widget.onProductsSelected!(productCounts); + } + }); + }, + ), ], ), ), diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 26e268c7..b3107b25 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; +import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart'; @@ -15,6 +16,7 @@ import 'package:syncrow_web/utils/color_manager.dart'; class CommunityStructureArea extends StatefulWidget { final CommunityModel? selectedCommunity; final SpaceModel? selectedSpace; + final List? products; final List spaces; final List connections; @@ -22,6 +24,7 @@ class CommunityStructureArea extends StatefulWidget { CommunityStructureArea({ this.selectedCommunity, this.selectedSpace, + this.products, required this.spaces, required this.connections, }); @@ -236,6 +239,7 @@ class _CommunityStructureAreaState extends State { context: context, builder: (BuildContext context) { return CreateSpaceDialog( + products: widget.products, onCreateSpace: (String name, String icon) { setState(() { // Set the first space in the center or use passed position diff --git a/lib/pages/spaces_management/widgets/counter_widget.dart b/lib/pages/spaces_management/widgets/counter_widget.dart index 5b1bfbf0..fa5f81a5 100644 --- a/lib/pages/spaces_management/widgets/counter_widget.dart +++ b/lib/pages/spaces_management/widgets/counter_widget.dart @@ -2,20 +2,34 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class CounterWidget extends StatefulWidget { + final int initialCount; + final ValueChanged onCountChanged; + + const CounterWidget({ + Key? key, + this.initialCount = 0, + required this.onCountChanged, + }) : super(key: key); + @override _CounterWidgetState createState() => _CounterWidgetState(); } class _CounterWidgetState extends State { - int _counter = 0; + late int _counter = 0; + + @override + void initState() { + super.initState(); + _counter = widget.initialCount; + } @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), decoration: BoxDecoration( - color: ColorsManager - .counterBackgroundColor, // Background color for the counter + color: ColorsManager.counterBackgroundColor, // Background color for the counter borderRadius: BorderRadius.circular(20), // Rounded corners ), child: Row( @@ -28,10 +42,11 @@ class _CounterWidgetState extends State { setState(() { if (_counter > 0) { _counter--; + widget.onCountChanged(_counter); } }); }, - child: Icon( + child: const Icon( Icons.remove, color: ColorsManager.spaceColor, // Blue color size: 18, // Icon size @@ -52,6 +67,7 @@ class _CounterWidgetState extends State { onTap: () { setState(() { _counter++; + widget.onCountChanged(_counter); }); }, child: const Icon( diff --git a/lib/pages/spaces_management/widgets/loaded_space_widget.dart b/lib/pages/spaces_management/widgets/loaded_space_widget.dart index f3318107..082224b6 100644 --- a/lib/pages/spaces_management/widgets/loaded_space_widget.dart +++ b/lib/pages/spaces_management/widgets/loaded_space_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/community_structure_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/gradient_canvas_border_widget.dart'; @@ -11,6 +12,7 @@ class LoadedSpaceView extends StatefulWidget { final SpaceModel? selectedSpace; final ValueChanged onCommunitySelected; final ValueChanged onSpaceSelected; + final List? products; const LoadedSpaceView({ Key? key, @@ -19,6 +21,7 @@ class LoadedSpaceView extends StatefulWidget { this.selectedSpace, required this.onCommunitySelected, required this.onSpaceSelected, + this.products, }) : super(key: key); @override @@ -43,6 +46,7 @@ class _LoadedStateViewState extends State { selectedSpace: widget.selectedSpace, spaces: widget.selectedCommunity?.spaces ?? [], connections: [], + products: widget.products, ), ], ), diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 58782f6b..44a45f0e 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -220,7 +220,6 @@ class _SidebarWidgetState extends State { _selectedSpaceUuid = space.uuid; _selectedCommunityUuid = community.uuid; // Update selected community }); - print(_selectedSpaceUuid); if (widget.onSpaceSelected != null) { widget.onSpaceSelected!(space); } From 2d60b2e225d878bfea514e3a622f051a57e66f08 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 20 Nov 2024 20:11:01 +0400 Subject: [PATCH 070/122] added product api and model --- lib/services/product_api.dart | 26 ++++++++++++++++++++++++++ lib/services/space_mana_api.dart | 1 - lib/utils/constants/api_const.dart | 3 +++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 lib/services/product_api.dart diff --git a/lib/services/product_api.dart b/lib/services/product_api.dart new file mode 100644 index 00000000..f33a4135 --- /dev/null +++ b/lib/services/product_api.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; +import 'package:syncrow_web/utils/constants/api_const.dart'; + +class ProductApi { + Future> fetchProducts() async { + try { + final response = await HTTPService().get( + path: ApiEndpoints.listProducts, + expectedResponseModel: (json) { + List jsonData = json['data']; + + List productList = jsonData.map((jsonItem) { + return ProductModel.fromMap(jsonItem); + }).toList(); + return productList; + }, + ); + return response; + } catch (e) { + debugPrint('Error fetching products: $e'); + return []; + } + } +} diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index 2461d5c8..64b5c376 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -157,7 +157,6 @@ class CommunitySpaceManagementApi { if (parentId != null) { body['parentUuid'] = parentId; } - print(ApiEndpoints.createSpace.replaceAll('{communityId}', communityId)); final response = await HTTPService().post( path: ApiEndpoints.createSpace.replaceAll('{communityId}', communityId), body: body, diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 950b7223..b4bd575f 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -78,4 +78,7 @@ abstract class ApiEndpoints { static const String factoryReset = '/device/factory/reset/{deviceUuid}'; static const String powerClamp = '/device/{powerClampUuid}/power-clamp/status'; + + //product + static const String listProducts = '/products'; } From 60710c383fc786fef184e6ecb9ec0b8b58af3108 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 20 Nov 2024 20:11:12 +0400 Subject: [PATCH 071/122] added assets --- lib/utils/color_manager.dart | 1 + lib/utils/constants/assets.dart | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index 6eb6a2d0..dca1619a 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -50,6 +50,7 @@ abstract class ColorsManager { static const Color transparentColor = Color(0x00000000); static const Color spaceColor = Color(0xB2023DFE); static const Color counterBackgroundColor = Color(0xCCF4F4F4); + static const Color neutralGray = Color(0xFFE5E5E5); } //background: #background: #5D5D5D; diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index c57cbdb1..e37c36a1 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -233,4 +233,5 @@ class Assets { static const String oneTouchSwitch = 'assets/icons/1G_touch_switch.svg'; static const String garageDoor = 'assets/icons/garage_opener.svg'; + static const String doorSensor = 'assets/icons/door_sensor.svg'; } From 6fd845a9fcc08eae62712c4eef3336c6f7387a64 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 20 Nov 2024 20:41:12 +0400 Subject: [PATCH 072/122] fixed device type counts --- .../view/dialogs/create_space_dialog.dart | 213 +++++++++++++----- .../widgets/add_device_type_widget.dart | 14 +- 2 files changed, 165 insertions(+), 62 deletions(-) diff --git a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart index 4351203f..d7bfeba6 100644 --- a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart @@ -42,7 +42,7 @@ class CreateSpaceDialogState extends State { width: 100, height: 100, decoration: const BoxDecoration( - color: Color(0xFFF5F6F7), + color: ColorsManager.boxColor, shape: BoxShape.circle, ), ), @@ -87,12 +87,12 @@ class CreateSpaceDialogState extends State { color: ColorsManager.lightGrayColor, fontWeight: FontWeight.w400), filled: true, - fillColor: const Color(0xFFF5F6F7), + fillColor: ColorsManager.boxColor, enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide( - color: - Color(0xFFF5F6F7), // Light gray color when enabled (not focused) + color: ColorsManager + .boxColor, // Light gray color when enabled (not focused) width: 1.5, ), ), @@ -100,7 +100,7 @@ class CreateSpaceDialogState extends State { focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide( - color: Color(0xFFF5F6F7), // Primary color when focused + color: ColorsManager.boxColor, // Primary color when focused width: 1.5, ), ), @@ -108,7 +108,7 @@ class CreateSpaceDialogState extends State { disabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide( - color: Color(0xFFF5F6F7), // Light gray for disabled state + color: ColorsManager.boxColor, // Light gray for disabled state width: 1.5, ), ), @@ -116,7 +116,7 @@ class CreateSpaceDialogState extends State { errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide( - color: Color(0xFFF5F6F7), // Red border when there's an error + color: ColorsManager.boxColor, // Red border when there's an error width: 1.5, ), ), @@ -124,7 +124,8 @@ class CreateSpaceDialogState extends State { focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: const BorderSide( - color: ColorsManager.boxColor, // Red border when there's an error and it's focused + color: ColorsManager + .boxColor, // Red border when there's an error and it's focused width: 1.5, ), ), @@ -132,55 +133,59 @@ class CreateSpaceDialogState extends State { ), const SizedBox(height: 16), // Add Devices or Space Model Button - ElevatedButton( - onPressed: () { - showDialog( - context: context, - builder: (context) => AddDeviceWidget( - products: widget.products, - onProductsSelected: (selectedProductsMap) { - setState(() { - selectedProducts = selectedProductsMap; - }); - }, - ), - ); - }, - style: ElevatedButton.styleFrom( - backgroundColor: ColorsManager.textFieldGreyColor, - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), - shape: RoundedRectangleBorder( - side: const BorderSide( - // Add border side here - color: ColorsManager.neutralGray, // Define your desired border color - width: 2.0, // Define border width + if (selectedProducts.isNotEmpty) + _buildSelectedProductsButtons(widget.products ?? []) + else + ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => AddDeviceWidget( + products: widget.products, + onProductsSelected: (selectedProductsMap) { + setState(() { + selectedProducts = selectedProductsMap; + }); + }, ), - borderRadius: BorderRadius.circular(20)), - ), - child: Row( - mainAxisSize: - MainAxisSize.min, // Adjust the button size to fit the content - children: [ - SvgPicture.asset( - Assets.addIcon, // Replace with your actual icon path - width: 20, // Set the size of the icon - height: 20, - ), - const SizedBox(width: 8), // Add spacing between the icon and text - const Text( - 'Add devices / Assign a space model', - style: TextStyle( - color: Colors.black, - fontSize: 16, - fontFamily: 'Aftika', - fontWeight: FontWeight.w400, - height: 1.5, // Adjust line height for better spacing + ); + }, + style: ElevatedButton.styleFrom( + backgroundColor: ColorsManager.textFieldGreyColor, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), + shape: RoundedRectangleBorder( + side: const BorderSide( + // Add border side here + color: + ColorsManager.neutralGray, // Define your desired border color + width: 2.0, // Define border width + ), + borderRadius: BorderRadius.circular(20)), + ), + child: Row( + mainAxisSize: + MainAxisSize.min, // Adjust the button size to fit the content + children: [ + SvgPicture.asset( + Assets.addIcon, // Replace with your actual icon path + width: 20, // Set the size of the icon + height: 20, ), - ), - const SizedBox(width: 8), - ], + const SizedBox(width: 8), // Add spacing between the icon and text + const Text( + 'Add devices / Assign a space model', + style: TextStyle( + color: Colors.black, + fontSize: 16, + fontFamily: 'Aftika', + fontWeight: FontWeight.w400, + height: 1.5, // Adjust line height for better spacing + ), + ), + const SizedBox(width: 8), + ], + ), ), - ), ], ), ), @@ -231,7 +236,7 @@ class CreateSpaceDialogState extends State { height: 200, padding: const EdgeInsets.all(18), decoration: BoxDecoration( - color: const Color(0xFFF5F6F7), + color: ColorsManager.boxColor, borderRadius: BorderRadius.circular(12), ), child: GridView.builder( @@ -263,6 +268,104 @@ class CreateSpaceDialogState extends State { ); } + Widget _buildSelectedProductsButtons(List products) { + return Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: ColorsManager.textFieldGreyColor, + borderRadius: BorderRadius.circular(12), + ), + child: Wrap( + spacing: 8, // Horizontal spacing between buttons + runSpacing: 8, // Vertical spacing between rows + children: [ + // Dynamically create a button for each selected product + for (var entry in selectedProducts.entries) + GestureDetector( + onTap: () { + + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + // Display the product icon + SvgPicture.asset( + _mapIconToProduct(entry.key, products), + width: 24, + height: 24, + ), + const SizedBox(width: 8), + // Display the product count + Text( + 'x${entry.value}', + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: ColorsManager.spaceColor, + ), + ), + ], + ), + ), + ), + // Add Button + GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (context) => AddDeviceWidget( + products: widget.products, + initialSelectedProducts: selectedProducts, + onProductsSelected: (selectedProductsMap) { + setState(() { + selectedProducts = selectedProductsMap; + }); + }, + ), + ); + }, + child: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: ColorsManager.textFieldGreyColor, + borderRadius: BorderRadius.circular(16), + ), + child: const Icon( + Icons.add, + color: ColorsManager.spaceColor, + size: 24, + ), + ), + ), + ], + ), + ); + } + + String _mapIconToProduct(String uuid, List products) { + // Find the product with the matching UUID + final product = products.firstWhere( + (product) => product.uuid == uuid, + orElse: () => ProductModel( + uuid: '', + catName: '', + prodId: '', + prodType: '', + name: '', + icon: Assets.presenceSensor, + ), + ); + + return product.icon ?? Assets.presenceSensor; + } + final List _iconList = [ Assets.location, Assets.villa, diff --git a/lib/pages/spaces_management/widgets/add_device_type_widget.dart b/lib/pages/spaces_management/widgets/add_device_type_widget.dart index e0686dcb..d6f4e8db 100644 --- a/lib/pages/spaces_management/widgets/add_device_type_widget.dart +++ b/lib/pages/spaces_management/widgets/add_device_type_widget.dart @@ -10,8 +10,10 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart'; class AddDeviceWidget extends StatefulWidget { final List? products; final ValueChanged>? onProductsSelected; + final Map? initialSelectedProducts; - const AddDeviceWidget({super.key, this.products, this.onProductsSelected}); + const AddDeviceWidget( + {super.key, this.products, this.initialSelectedProducts, this.onProductsSelected}); @override _AddDeviceWidgetState createState() => _AddDeviceWidgetState(); @@ -25,11 +27,9 @@ class _AddDeviceWidgetState extends State { void initState() { super.initState(); _scrollController = ScrollController(); - - if (widget.products != null) { - for (var product in widget.products!) { - productCounts[product.uuid] = 0; - } + print(widget.initialSelectedProducts); + if (widget.initialSelectedProducts != null && widget.initialSelectedProducts!.isNotEmpty) { + productCounts = widget.initialSelectedProducts!; } } @@ -161,7 +161,7 @@ class _AddDeviceWidgetState extends State { const SizedBox(height: 8), // The custom counter widget aligned at the bottom CounterWidget( - initialCount: 0, + initialCount: productCounts[deviceType!.uuid] ?? 0, onCountChanged: (newCount) { setState(() { if (newCount > 0) { From 6bc6097a7e4d11e89e66852737b67c926233ac03 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 10:04:07 +0400 Subject: [PATCH 073/122] added edit space option --- assets/icons/door_sensor.svg | 35 +++++++ .../bloc/space_management_bloc.dart | 3 +- .../model/selected_product_model.dart | 13 +++ .../spaces_management/model/space_model.dart | 20 +--- .../view/dialogs/create_space_dialog.dart | 88 +++++++++-------- .../widgets/add_device_type_widget.dart | 24 +++-- .../widgets/community_structure_widget.dart | 51 ++++++++-- .../widgets/hoverable_button.dart | 77 +++++++++++++++ .../widgets/space_container_widget.dart | 94 ++++++++++--------- lib/services/space_mana_api.dart | 5 +- lib/utils/color_manager.dart | 1 + 11 files changed, 291 insertions(+), 120 deletions(-) create mode 100644 assets/icons/door_sensor.svg create mode 100644 lib/pages/spaces_management/model/selected_product_model.dart create mode 100644 lib/pages/spaces_management/widgets/hoverable_button.dart diff --git a/assets/icons/door_sensor.svg b/assets/icons/door_sensor.svg new file mode 100644 index 00000000..fdeb661c --- /dev/null +++ b/assets/icons/door_sensor.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 831c6f30..41c7e234 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -119,7 +119,6 @@ class SpaceManagementBloc extends Bloc toJson() { + return { + 'productId': productId, + 'count': count, + }; + } +} diff --git a/lib/pages/spaces_management/model/space_model.dart b/lib/pages/spaces_management/model/space_model.dart index e64b741f..9c85724d 100644 --- a/lib/pages/spaces_management/model/space_model.dart +++ b/lib/pages/spaces_management/model/space_model.dart @@ -1,14 +1,15 @@ import 'dart:ui'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; enum SpaceStatus { newSpace, modified, unchanged } class SpaceModel { String? uuid; - final String? icon; + String? icon; final String? spaceTuyaUuid; - final String name; + String name; final bool isPrivate; final String? invitationCode; SpaceModel? parent; @@ -17,6 +18,7 @@ class SpaceModel { Offset position; bool isHovered; SpaceStatus status; + List selectedProducts; List outgoingConnections = []; // Connections from this space Connection? incomingConnection; // Connections to this space @@ -35,6 +37,7 @@ class SpaceModel { this.isHovered = false, this.incomingConnection, this.status = SpaceStatus.unchanged, + this.selectedProducts = const [], }); factory SpaceModel.fromJson(Map json) { @@ -72,19 +75,6 @@ class SpaceModel { return instance; } - @override - String toString() { - return ''' -SpaceModel { - uuid: $uuid, - name: $name, - isPrivate: $isPrivate, - position: {dx: ${position.dx}, dy: ${position.dy}}, - parentUuid: ${parent?.uuid}, - children: ${children.map((child) => child.name).toList()}, - isHovered: $isHovered -}'''; - } Map toMap() { return { diff --git a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart index d7bfeba6..76987253 100644 --- a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart @@ -3,15 +3,28 @@ import 'package:flutter_svg/flutter_svg.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/model/product_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/add_device_type_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/hoverable_button.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class CreateSpaceDialog extends StatefulWidget { - final Function(String, String) onCreateSpace; + final Function(String, String, List selectedProducts) onCreateSpace; final List? products; + final String? name; + final String? icon; + final bool isEdit; + final List selectedProducts; - const CreateSpaceDialog({super.key, required this.onCreateSpace, this.products}); + const CreateSpaceDialog( + {super.key, + required this.onCreateSpace, + this.products, + this.name, + this.icon, + this.isEdit = false, + this.selectedProducts = const []}); @override CreateSpaceDialogState createState() => CreateSpaceDialogState(); @@ -20,12 +33,21 @@ class CreateSpaceDialog extends StatefulWidget { class CreateSpaceDialogState extends State { String selectedIcon = Assets.location; String enteredName = ''; - Map selectedProducts = {}; + List selectedProducts = []; + late TextEditingController nameController; + + @override + void initState() { + super.initState(); + selectedIcon = widget.icon ?? Assets.location; + nameController = TextEditingController(text: widget.name ?? ''); + selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : []; + } @override Widget build(BuildContext context) { return AlertDialog( - title: const Text('Create New Space'), + title: widget.isEdit ? Text('Edit Space') : Text('Create new Space'), backgroundColor: ColorsManager.whiteColors, content: SizedBox( width: 600, @@ -76,6 +98,7 @@ class CreateSpaceDialogState extends State { children: [ // Name input field TextField( + controller: nameController, onChanged: (value) { enteredName = value; }, @@ -208,8 +231,10 @@ class CreateSpaceDialogState extends State { Expanded( child: DefaultButton( onPressed: () { - if (enteredName.isNotEmpty) { - widget.onCreateSpace(enteredName, selectedIcon); // Pass the name and icon back + late String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? ''); + if (newName.isNotEmpty) { + widget.onCreateSpace( + newName, selectedIcon, selectedProducts); // Pass the name and icon back Navigator.of(context).pop(); // Close the dialog } }, @@ -270,49 +295,31 @@ class CreateSpaceDialogState extends State { Widget _buildSelectedProductsButtons(List products) { return Container( + width: 600, padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: ColorsManager.textFieldGreyColor, borderRadius: BorderRadius.circular(12), + border: Border.all( + color: ColorsManager.neutralGray, + width: 2, // Set the border width + ), ), child: Wrap( spacing: 8, // Horizontal spacing between buttons runSpacing: 8, // Vertical spacing between rows children: [ // Dynamically create a button for each selected product - for (var entry in selectedProducts.entries) - GestureDetector( + for (var product in selectedProducts) + HoverableButton( + iconPath: _mapIconToProduct(product.productId, products), + text: 'x${product.count}', onTap: () { - + setState(() { + selectedProducts.remove(product); + }); + // Handle button tap }, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - // Display the product icon - SvgPicture.asset( - _mapIconToProduct(entry.key, products), - width: 24, - height: 24, - ), - const SizedBox(width: 8), - // Display the product count - Text( - 'x${entry.value}', - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: ColorsManager.spaceColor, - ), - ), - ], - ), - ), ), // Add Button GestureDetector( @@ -331,10 +338,9 @@ class CreateSpaceDialogState extends State { ); }, child: Container( - width: 50, - height: 50, + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( - color: ColorsManager.textFieldGreyColor, + color: ColorsManager.whiteColors, borderRadius: BorderRadius.circular(16), ), child: const Icon( @@ -359,7 +365,7 @@ class CreateSpaceDialogState extends State { prodId: '', prodType: '', name: '', - icon: Assets.presenceSensor, + icon: Assets.presenceSensor, ), ); diff --git a/lib/pages/spaces_management/widgets/add_device_type_widget.dart b/lib/pages/spaces_management/widgets/add_device_type_widget.dart index d6f4e8db..d069b391 100644 --- a/lib/pages/spaces_management/widgets/add_device_type_widget.dart +++ b/lib/pages/spaces_management/widgets/add_device_type_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/counter_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -9,8 +10,8 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart'; class AddDeviceWidget extends StatefulWidget { final List? products; - final ValueChanged>? onProductsSelected; - final Map? initialSelectedProducts; + final ValueChanged>? onProductsSelected; + final List? initialSelectedProducts; const AddDeviceWidget( {super.key, this.products, this.initialSelectedProducts, this.onProductsSelected}); @@ -21,15 +22,14 @@ class AddDeviceWidget extends StatefulWidget { class _AddDeviceWidgetState extends State { late ScrollController _scrollController; - Map productCounts = {}; + List productCounts = []; @override void initState() { super.initState(); _scrollController = ScrollController(); - print(widget.initialSelectedProducts); if (widget.initialSelectedProducts != null && widget.initialSelectedProducts!.isNotEmpty) { - productCounts = widget.initialSelectedProducts!; + productCounts = List.from(widget.initialSelectedProducts!); } } @@ -112,6 +112,12 @@ class _AddDeviceWidgetState extends State { } Widget _buildDeviceTypeTile(ProductModel? deviceType) { + + SelectedProduct? existingProduct = productCounts.firstWhere( + (product) => product.productId == deviceType?.uuid, + orElse: () => SelectedProduct(productId: deviceType!.uuid, count: 0), + ); + return SizedBox( width: 75, height: 90, // Increase height if needed @@ -161,11 +167,15 @@ class _AddDeviceWidgetState extends State { const SizedBox(height: 8), // The custom counter widget aligned at the bottom CounterWidget( - initialCount: productCounts[deviceType!.uuid] ?? 0, + initialCount: existingProduct.count, onCountChanged: (newCount) { setState(() { if (newCount > 0) { - productCounts[deviceType!.uuid] = newCount; + if (!productCounts.contains(existingProduct)) { + productCounts.add(SelectedProduct(productId: deviceType!.uuid, count: newCount)); + } else { + existingProduct.count = newCount; + } } else { productCounts.remove(deviceType!.uuid); } diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index b3107b25..f7494a55 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart'; @@ -114,6 +115,9 @@ class _CommunityStructureAreaState extends State { buildSpaceContainer: (int index) { return SpaceContainerWidget( index: index, + onDoubleTap: () { + _showEditSpaceDialog(spaces[index]); + }, icon: spaces[index].icon ?? '', name: spaces[index].name, ); @@ -239,8 +243,8 @@ class _CommunityStructureAreaState extends State { context: context, builder: (BuildContext context) { return CreateSpaceDialog( - products: widget.products, - onCreateSpace: (String name, String icon) { + products: widget.products, + onCreateSpace: (String name, String icon, List selectedProducts) { setState(() { // Set the first space in the center or use passed position Offset centerPosition = position ?? @@ -249,13 +253,13 @@ class _CommunityStructureAreaState extends State { screenSize.height / 2 - 50, // Slightly above the center vertically ); SpaceModel newSpace = SpaceModel( - name: name, - icon: icon, - position: centerPosition, - isPrivate: false, - children: [], - status: SpaceStatus.newSpace, - ); + name: name, + icon: icon, + position: centerPosition, + isPrivate: false, + children: [], + status: SpaceStatus.newSpace, + selectedProducts: selectedProducts); if (parentIndex != null && direction != null) { SpaceModel parentSpace = spaces[parentIndex]; @@ -280,6 +284,35 @@ class _CommunityStructureAreaState extends State { ); } + +void _showEditSpaceDialog(SpaceModel space) { + showDialog( + context: context, + builder: (BuildContext context) { + return CreateSpaceDialog( + products: widget.products, + name: space.name, + icon: space.icon, + isEdit: true, + selectedProducts: space.selectedProducts, + onCreateSpace: (String name, String icon, List selectedProducts) { + setState(() { + // Update the space's properties + space.name = name; + space.icon = icon; + space.selectedProducts = selectedProducts; + + if (space.status != SpaceStatus.newSpace) { + space.status = SpaceStatus.modified; // Mark as modified + } + }); + }, + // Pre-fill the dialog with current space data + key: Key(space.name), // Add a unique key to ensure dialog refresh + ); + }, + ); +} void _handleHoverChanged(int index, bool isHovered) { setState(() { spaces[index].isHovered = isHovered; diff --git a/lib/pages/spaces_management/widgets/hoverable_button.dart b/lib/pages/spaces_management/widgets/hoverable_button.dart new file mode 100644 index 00000000..bc39f87e --- /dev/null +++ b/lib/pages/spaces_management/widgets/hoverable_button.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class HoverableButton extends StatefulWidget { + final String iconPath; + final String text; + final VoidCallback onTap; + + const HoverableButton({ + Key? key, + required this.iconPath, + required this.text, + required this.onTap, + }) : super(key: key); + + @override + _HoverableButtonState createState() => _HoverableButtonState(); +} + +class _HoverableButtonState extends State { + bool isHovered = false; // Track hover state + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: widget.onTap, + child: MouseRegion( + onEnter: (_) => setState(() => isHovered = true), + onExit: (_) => setState(() => isHovered = false), + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 8), + decoration: BoxDecoration( + color: isHovered ? ColorsManager.warningRed : Colors.white, // Change color on hover + borderRadius: BorderRadius.circular(16), + boxShadow: [ + if (isHovered) + BoxShadow( + color: ColorsManager.warningRed, + ), + ], + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (!isHovered) + SvgPicture.asset( + widget.iconPath, + width: 24, + height: 24, + ) + else + Center( + child: const Icon( + Icons.close, // Display "X" on hover + color: Colors.white, + size: 24, + )), + const SizedBox(width: 8), + if (!isHovered) ...[ + Text( + widget.text, + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.w500, + color: ColorsManager.spaceColor, + ), + ), + ], + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/spaces_management/widgets/space_container_widget.dart b/lib/pages/spaces_management/widgets/space_container_widget.dart index 0f98c236..bb40da51 100644 --- a/lib/pages/spaces_management/widgets/space_container_widget.dart +++ b/lib/pages/spaces_management/widgets/space_container_widget.dart @@ -2,68 +2,70 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_web/utils/color_manager.dart'; - class SpaceContainerWidget extends StatelessWidget { final int index; final String icon; final String name; + final VoidCallback? onDoubleTap; const SpaceContainerWidget({ super.key, required this.index, required this.icon, required this.name, + this.onDoubleTap, }); @override Widget build(BuildContext context) { - return Container( - width: 150, - height: 60, - decoration: BoxDecoration( - color: ColorsManager.whiteColors, - borderRadius: BorderRadius.circular(15), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: const Offset(0, 3), // shadow position - ), - ], - ), - child: Row( - children: [ - Container( - width: 40, - height: 60, - decoration: const BoxDecoration( - color: ColorsManager.spaceColor, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(15), - bottomLeft: Radius.circular(15), + return GestureDetector( + onDoubleTap: onDoubleTap, + child: Container( + width: 150, + height: 60, + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: const Offset(0, 3), // shadow position ), - ), - child: Center( - child: SvgPicture.asset( - icon, - color: ColorsManager.whiteColors, - width: 24, - height: 24, + ], + ), + child: Row( + children: [ + Container( + width: 40, + height: 60, + decoration: const BoxDecoration( + color: ColorsManager.spaceColor, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(15), + bottomLeft: Radius.circular(15), + ), + ), + child: Center( + child: SvgPicture.asset( + icon, + color: ColorsManager.whiteColors, + width: 24, + height: 24, + ), + ), ), - ), + const SizedBox(width: 10), + Text( + name, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ], ), - const SizedBox(width: 10), - Text( - name, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - ], - ), - - ); + )); } } diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index 64b5c376..892d9ecd 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_response_model.dart'; import 'package:syncrow_web/services/api/http_service.dart'; @@ -144,6 +145,7 @@ class CommunitySpaceManagementApi { bool isPrivate = false, required Offset position, String? icon, + required List products, }) async { try { final body = { @@ -152,7 +154,8 @@ class CommunitySpaceManagementApi { 'x': position.dx, 'y': position.dy, 'direction': direction, - 'icon': icon + 'icon': icon, + 'products': products.map((product) => product.toJson()).toList(), }; if (parentId != null) { body['parentUuid'] = parentId; diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index dca1619a..2bc3fe66 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -51,6 +51,7 @@ abstract class ColorsManager { static const Color spaceColor = Color(0xB2023DFE); static const Color counterBackgroundColor = Color(0xCCF4F4F4); static const Color neutralGray = Color(0xFFE5E5E5); + static const Color warningRed = Color(0xFFFF6465); } //background: #background: #5D5D5D; From 8a7f9ab2dc1304246239b34fc3bb4fb3bceda794 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 11:52:27 +0400 Subject: [PATCH 074/122] delete Space --- assets/icons/delete.svg | 9 + .../bloc/space_management_bloc.dart | 2 + .../model/selected_product_model.dart | 5 + .../spaces_management/model/space_model.dart | 22 ++ .../widgets/community_structure_widget.dart | 122 ++++++---- .../dialogs/create_community_dialog.dart | 0 .../dialogs/create_space_dialog.dart | 1 + .../widgets/dialogs/delete_dialogue.dart | 225 ++++++++++++++++++ .../widgets/sidebar_widget.dart | 2 +- lib/services/space_mana_api.dart | 9 +- lib/utils/constants/assets.dart | 2 + 11 files changed, 353 insertions(+), 46 deletions(-) create mode 100644 assets/icons/delete.svg rename lib/pages/spaces_management/{view => widgets}/dialogs/create_community_dialog.dart (100%) rename lib/pages/spaces_management/{view => widgets}/dialogs/create_space_dialog.dart (99%) create mode 100644 lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart diff --git a/assets/icons/delete.svg b/assets/icons/delete.svg new file mode 100644 index 00000000..050a4521 --- /dev/null +++ b/assets/icons/delete.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 41c7e234..3ff8216e 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -110,6 +110,7 @@ class SpaceManagementBloc extends Bloc emit, @@ -146,6 +147,7 @@ class SpaceManagementBloc extends Bloc { return SpaceContainerWidget( index: index, onDoubleTap: () { + print(spaces[index].toString()); _showEditSpaceDialog(spaces[index]); }, icon: spaces[index].icon ?? '', @@ -178,23 +182,55 @@ class _CommunityStructureAreaState extends State { ), // Show "Save" button only if there are spaces if (spaces.isNotEmpty && widget.selectedCommunity != null) - ElevatedButton.icon( - onPressed: () { - _saveSpaces(); - }, - icon: const Icon(Icons.save, size: 18), - label: const Text("Save"), - style: ElevatedButton.styleFrom( - backgroundColor: ColorsManager.whiteColors, - foregroundColor: Colors.black, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), + Row(children: [ + ElevatedButton.icon( + onPressed: () { + _saveSpaces(); + }, + icon: const Icon( + Icons.save, + size: 18, + color: ColorsManager.spaceColor, + ), + label: const Text("Save"), + style: ElevatedButton.styleFrom( + backgroundColor: ColorsManager.textFieldGreyColor, + foregroundColor: ColorsManager.blackColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + side: BorderSide(color: Colors.grey.shade300), + elevation: 0, ), - padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), - side: BorderSide(color: Colors.grey.shade300), - elevation: 0, ), - ), + const SizedBox(width: 10), + ElevatedButton.icon( + onPressed: () { + showDeleteConfirmationDialog(context, () { + // Handle the delete action here + print("Delete confirmed"); + Navigator.of(context).pop(); // Close the dialog after confirming + }, widget.selectedSpace != null); + }, + icon: SvgPicture.asset( + Assets.acFanAuto, // Path to your SVG asset + width: 18, // Adjust width as needed + height: 18, // Adjust height as needed + ), + label: const Text("Delete"), + style: ElevatedButton.styleFrom( + backgroundColor: ColorsManager.textFieldGreyColor, + foregroundColor: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + side: BorderSide(color: ColorsManager.neutralGray), + elevation: 0, + ), + ), + ]) ], ), ); @@ -284,35 +320,35 @@ class _CommunityStructureAreaState extends State { ); } + void _showEditSpaceDialog(SpaceModel space) { + showDialog( + context: context, + builder: (BuildContext context) { + return CreateSpaceDialog( + products: widget.products, + name: space.name, + icon: space.icon, + isEdit: true, + selectedProducts: space.selectedProducts, + onCreateSpace: (String name, String icon, List selectedProducts) { + setState(() { + // Update the space's properties + space.name = name; + space.icon = icon; + space.selectedProducts = selectedProducts; -void _showEditSpaceDialog(SpaceModel space) { - showDialog( - context: context, - builder: (BuildContext context) { - return CreateSpaceDialog( - products: widget.products, - name: space.name, - icon: space.icon, - isEdit: true, - selectedProducts: space.selectedProducts, - onCreateSpace: (String name, String icon, List selectedProducts) { - setState(() { - // Update the space's properties - space.name = name; - space.icon = icon; - space.selectedProducts = selectedProducts; + if (space.status != SpaceStatus.newSpace) { + space.status = SpaceStatus.modified; // Mark as modified + } + }); + }, + // Pre-fill the dialog with current space data + key: Key(space.name), // Add a unique key to ensure dialog refresh + ); + }, + ); + } - if (space.status != SpaceStatus.newSpace) { - space.status = SpaceStatus.modified; // Mark as modified - } - }); - }, - // Pre-fill the dialog with current space data - key: Key(space.name), // Add a unique key to ensure dialog refresh - ); - }, - ); -} void _handleHoverChanged(int index, bool isHovered) { setState(() { spaces[index].isHovered = isHovered; diff --git a/lib/pages/spaces_management/view/dialogs/create_community_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/create_community_dialog.dart similarity index 100% rename from lib/pages/spaces_management/view/dialogs/create_community_dialog.dart rename to lib/pages/spaces_management/widgets/dialogs/create_community_dialog.dart diff --git a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart similarity index 99% rename from lib/pages/spaces_management/view/dialogs/create_space_dialog.dart rename to lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart index 76987253..54218d22 100644 --- a/lib/pages/spaces_management/view/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart @@ -369,6 +369,7 @@ class CreateSpaceDialogState extends State { ), ); + return product.icon ?? Assets.presenceSensor; } diff --git a/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart b/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart new file mode 100644 index 00000000..4e9b25fc --- /dev/null +++ b/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart @@ -0,0 +1,225 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +void showDeleteConfirmationDialog(BuildContext context, VoidCallback onConfirm, bool isSpace) { + showDialog( + context: context, + barrierDismissible: false, // Prevent dismissing by tapping outside + builder: (BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + ), + child: SizedBox( + width: 500, // Set the desired width + child: Container( + color: ColorsManager.whiteColors, + padding: const EdgeInsets.all(20.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Icon + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: ColorsManager.warningRed, + shape: BoxShape.circle, + ), + child: const Icon( + Icons.close, + color: Colors.white, + size: 40, + ), + ), + const SizedBox(height: 20), + // Title + isSpace + ? const Text( + 'Delete Space', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ) + : const Text( + 'Delete Community', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 10), + // Subtitle + isSpace + ? const Text( + 'All the data in the space will be lost', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + color: Colors.grey, + ), + ) + : const Text( + 'All the data in the community will be lost', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + color: Colors.grey, + ), + ), + + const SizedBox(height: 20), + // Buttons + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); // Close the first dialog + // Trigger the second popup + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: const BorderSide(color: ColorsManager.boxColor), // Black border + ), + ), + child: const Text( + 'Cancel', + style: TextStyle(color: Colors.black), + ), + ), + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); // Close the first dialog + showProcessingPopup(context, isSpace, onConfirm); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + child: const Text( + 'Continue', + style: TextStyle(color: Colors.white), + ), + ), + ], + ), + ], + ), + ), + ), + ); + }, + ); +} + +void showProcessingPopup(BuildContext context, bool isSpace, VoidCallback onDelete) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.0), + ), + child: SizedBox( + width: 500, + child: Container( + color: ColorsManager.whiteColors, + padding: const EdgeInsets.all(20.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Icon + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: ColorsManager.warningRed, + shape: BoxShape.circle, + ), + child: const Icon( + Icons.close, + color: Colors.white, + size: 40, + ), + ), + const SizedBox(height: 20), + // Title + isSpace + ? const Text( + 'Delete Space', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ) + : const Text( + 'Delete Community', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 10), + // Subtitle + const Text( + 'Are you sure you want to Delete?', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + color: Colors.grey, + ), + ), + const SizedBox(height: 20), + // Buttons (Optional) + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () { + // Trigger the second popup + onDelete(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: ColorsManager.warningRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + side: const BorderSide(color: ColorsManager.boxColor), // Black border + ), + ), + child: const Text( + 'Delete', + style: TextStyle(color: Colors.white), + ), + ), + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); // Close the first dialog + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + child: const Text( + 'Cancel', + style: TextStyle(color: Colors.black), + ), + ), + ], + ), + ], + ), + ), + ), + ); + }, + ); +} diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 44a45f0e..4e8a9046 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -7,7 +7,7 @@ import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event. import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/community_tile.dart'; -import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_community_dialog.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/create_community_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/space_tile_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index 892d9ecd..e09fe8f6 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -183,6 +183,7 @@ class CommunitySpaceManagementApi { String? direction, bool isPrivate = false, required Offset position, + required List products, }) async { try { final body = { @@ -191,7 +192,8 @@ class CommunitySpaceManagementApi { 'x': position.dx, 'y': position.dy, 'direction': direction, - 'icon': icon + 'icon': icon, + 'products': products.map((product) => product.toJson()).toList(), }; if (parentId != null) { body['parentUuid'] = parentId; @@ -235,7 +237,10 @@ class CommunitySpaceManagementApi { final response = await HTTPService().get( path: ApiEndpoints.getSpaceHierarchy.replaceAll('{communityId}', communityId), expectedResponseModel: (json) { - return (json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList(); + final spaceModels = + (json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList(); + + return spaceModels; }, ); return response; diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index e37c36a1..4c359771 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -234,4 +234,6 @@ class Assets { static const String garageDoor = 'assets/icons/garage_opener.svg'; static const String doorSensor = 'assets/icons/door_sensor.svg'; + + static const String delete = 'assets/icons/delete_svg'; } From 288360f1afc17b74efa1d9de79ab849e3440db09 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 13:02:48 +0400 Subject: [PATCH 075/122] fixed delete community --- .../bloc/space_management_bloc.dart | 59 ++++++++++++------- .../bloc/space_management_event.dart | 11 ++++ .../view/spaces_management_page.dart | 6 +- .../widgets/community_structure_widget.dart | 14 ++++- lib/utils/constants/assets.dart | 2 +- 5 files changed, 64 insertions(+), 28 deletions(-) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 3ff8216e..1f413bd8 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -19,6 +19,7 @@ class SpaceManagementBloc extends Bloc(_onCreateCommunity); on(_onSaveSpaces); on(_onFetchProducts); + on(_onCommunityDelete); } void _onFetchProducts( @@ -74,11 +75,29 @@ class SpaceManagementBloc extends Bloc emit, + ) async { + try { + emit(SpaceManagementLoading()); + + final success = await _api.deleteCommunity(event.communityUuid); + if (success) { + add(LoadCommunityAndSpacesEvent()); + } else { + emit(const SpaceManagementError('Failed to delete the community.')); + } + } catch (e) { + // Handle unexpected errors + emit(SpaceManagementError('Error saving spaces: $e')); + } + } + void _onUpdateSpacePosition( UpdateSpacePositionEvent event, Emitter emit, ) { - // Handle space position update logic } void _onCreateCommunity( @@ -110,7 +129,6 @@ class SpaceManagementBloc extends Bloc emit, @@ -139,29 +157,26 @@ class SpaceManagementBloc extends Bloc get props => [communityUuid]; +} + class CreateSpaceEvent extends SpaceManagementEvent { final String name; final String icon; diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 49c4b064..c2c9e8de 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -56,13 +56,15 @@ class SpaceManagementPageState extends State { ); if (selectedIndex != -1) { selectedCommunity = state.communities[selectedIndex]; + } else { + selectedCommunity = null; + selectedSpace = null; } - return LoadedSpaceView( communities: state.communities, selectedCommunity: selectedCommunity, selectedSpace: selectedSpace, - products:state.products, + products: state.products, onCommunitySelected: (community) { setState(() { selectedCommunity = community; diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index dfb76bc3..97265ca1 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -119,7 +119,6 @@ class _CommunityStructureAreaState extends State { return SpaceContainerWidget( index: index, onDoubleTap: () { - print(spaces[index].toString()); _showEditSpaceDialog(spaces[index]); }, icon: spaces[index].icon ?? '', @@ -209,12 +208,12 @@ class _CommunityStructureAreaState extends State { onPressed: () { showDeleteConfirmationDialog(context, () { // Handle the delete action here - print("Delete confirmed"); Navigator.of(context).pop(); // Close the dialog after confirming + _onDelete(); }, widget.selectedSpace != null); }, icon: SvgPicture.asset( - Assets.acFanAuto, // Path to your SVG asset + Assets.delete, // Path to your SVG asset width: 18, // Adjust width as needed height: 18, // Adjust height as needed ), @@ -426,4 +425,13 @@ class _CommunityStructureAreaState extends State { communityUuid: communityUuid, )); } + + void _onDelete() { + if (widget.selectedCommunity != null && widget.selectedCommunity?.uuid != null) { + context.read().add(DeleteCommunityEvent( + communityUuid: widget.selectedCommunity!.uuid, + )); + + } + } } diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 4c359771..d96ce0dc 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -235,5 +235,5 @@ class Assets { static const String garageDoor = 'assets/icons/garage_opener.svg'; static const String doorSensor = 'assets/icons/door_sensor.svg'; - static const String delete = 'assets/icons/delete_svg'; + static const String delete = 'assets/icons/delete.svg'; } From 9be03850a5a263081dd0aa31a62b832194a2416f Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 16:16:14 +0400 Subject: [PATCH 076/122] fixed delete --- lib/common/custom_expansion_tile.dart | 1 - .../bloc/space_management_bloc.dart | 21 +++++- .../spaces_management/model/space_model.dart | 15 +---- .../widgets/community_structure_widget.dart | 67 ++++++++++++++----- .../widgets/loaded_space_widget.dart | 4 +- .../widgets/sidebar_widget.dart | 6 +- 6 files changed, 74 insertions(+), 40 deletions(-) diff --git a/lib/common/custom_expansion_tile.dart b/lib/common/custom_expansion_tile.dart index a6412b3c..364ccdce 100644 --- a/lib/common/custom_expansion_tile.dart +++ b/lib/common/custom_expansion_tile.dart @@ -96,7 +96,6 @@ class CustomExpansionTileState extends State { if (widget.onItemSelected != null) { widget.onItemSelected!(); } - debugPrint('${widget.title} ${widget.isSelected} tapped for selection'); }, child: Text( _capitalizeFirstLetter(widget.title), diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 1f413bd8..4b79ff1c 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -97,8 +97,7 @@ class SpaceManagementBloc extends Bloc emit, - ) { - } + ) {} void _onCreateCommunity( CreateCommunityEvent event, @@ -152,10 +151,26 @@ class SpaceManagementBloc extends Bloc spaces, String communityUuid) async { final orderedSpaces = flattenHierarchy(spaces); + final parentsToDelete = orderedSpaces.where((space) => + space.status == SpaceStatus.deleted && + (space.parent == null || space.parent?.status != SpaceStatus.deleted)); + + for (var parent in parentsToDelete) { + try { + // Ensure parent.uuid is not null before calling the API + if (parent.uuid != null) { + await _api.deleteSpace(communityUuid, parent.uuid!); + } + } catch (e) { + print( + 'Error deleting space ${parent.name} (UUID: ${parent.uuid}, Community UUID: $communityUuid): $e'); + rethrow; // Decide whether to stop execution or continue + } + } + for (var space in orderedSpaces) { try { if (space.uuid != null && space.uuid!.isNotEmpty) { - // Call update if the space already has a UUID final response = await _api.updateSpace( communityId: communityUuid, spaceId: space.uuid!, diff --git a/lib/pages/spaces_management/model/space_model.dart b/lib/pages/spaces_management/model/space_model.dart index 9996da6f..f8a5d412 100644 --- a/lib/pages/spaces_management/model/space_model.dart +++ b/lib/pages/spaces_management/model/space_model.dart @@ -3,7 +3,7 @@ import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; -enum SpaceStatus { newSpace, modified, unchanged } +enum SpaceStatus { newSpace, modified, unchanged, deleted } class SpaceModel { String? uuid; @@ -106,17 +106,4 @@ class SpaceModel { outgoingConnections.add(connection); } - @override - String toString() { - return ''' -SpaceModel( - uuid: $uuid, - name: $name, - icon: $icon, - isPrivate: $isPrivate, - position: $position, - selectedProducts: $selectedProducts -) -'''; - } } diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 97265ca1..51bdccc2 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -66,6 +66,9 @@ class _CommunityStructureAreaState extends State { @override Widget build(BuildContext context) { + final visibleSpaces = flattenSpaces(widget.spaces); + final visibleConnections = createConnections(widget.spaces); + Size screenSize = MediaQuery.of(context).size; return Expanded( child: Container( @@ -81,7 +84,7 @@ class _CommunityStructureAreaState extends State { Flexible( child: Stack( children: [ - if (spaces.isNotEmpty) + if (visibleSpaces.isNotEmpty) InteractiveViewer( boundaryMargin: EdgeInsets.all(500), minScale: 0.5, @@ -94,7 +97,7 @@ class _CommunityStructureAreaState extends State { children: [ for (var connection in connections) CustomPaint(painter: CurvedLinePainter([connection])), - for (var entry in spaces.asMap().entries) + for (var entry in visibleSpaces.asMap().entries) Positioned( left: entry.value.position.dx, top: entry.value.position.dy, @@ -103,7 +106,7 @@ class _CommunityStructureAreaState extends State { onButtonTap: (int index, Offset newPosition, String direction) { _showCreateSpaceDialog( screenSize, - position: spaces[index].position + newPosition, + position: visibleSpaces[index].position + newPosition, parentIndex: index, direction: direction, ); @@ -119,10 +122,10 @@ class _CommunityStructureAreaState extends State { return SpaceContainerWidget( index: index, onDoubleTap: () { - _showEditSpaceDialog(spaces[index]); + _showEditSpaceDialog(visibleSpaces[index]); }, - icon: spaces[index].icon ?? '', - name: spaces[index].name, + icon: visibleSpaces[index].icon ?? '', + name: visibleSpaces[index].name, ); }, ), @@ -131,7 +134,7 @@ class _CommunityStructureAreaState extends State { ), ), ), - if (spaces.isEmpty) + if (visibleSpaces.isEmpty) Center( child: AddSpaceButton( onTap: () { @@ -180,7 +183,7 @@ class _CommunityStructureAreaState extends State { ], ), // Show "Save" button only if there are spaces - if (spaces.isNotEmpty && widget.selectedCommunity != null) + if (widget.spaces.isNotEmpty && widget.selectedCommunity != null) Row(children: [ ElevatedButton.icon( onPressed: () { @@ -358,16 +361,15 @@ class _CommunityStructureAreaState extends State { List result = []; void flatten(SpaceModel space) { - // Add the current space to the result + if (space.status == SpaceStatus.deleted) return; + result.add(space); - // Recursively flatten child spaces for (var child in space.children) { flatten(child); } } - // Process each top-level space for (var space in spaces) { flatten(space); } @@ -379,7 +381,11 @@ class _CommunityStructureAreaState extends State { List connections = []; void addConnections(SpaceModel parent, String direction) { + if (parent.status == SpaceStatus.deleted) return; + for (var child in parent.children) { + if (child.status == SpaceStatus.deleted) continue; + // Create a connection object connections.add( Connection( @@ -395,7 +401,6 @@ class _CommunityStructureAreaState extends State { } } - // Process each top-level space for (var space in spaces) { addConnections(space, "down"); } @@ -405,16 +410,18 @@ class _CommunityStructureAreaState extends State { void _saveSpaces() { if (widget.selectedCommunity == null) { - print("No community selected for saving spaces."); + debugPrint("No community selected for saving spaces."); return; } List spacesToSave = spaces.where((space) { - return space.status == SpaceStatus.newSpace || space.status == SpaceStatus.modified; + return space.status == SpaceStatus.newSpace || + space.status == SpaceStatus.modified || + space.status == SpaceStatus.deleted; }).toList(); if (spacesToSave.isEmpty) { - print("No new or modified spaces to save."); + debugPrint("No new or modified spaces to save."); return; } @@ -427,11 +434,37 @@ class _CommunityStructureAreaState extends State { } void _onDelete() { - if (widget.selectedCommunity != null && widget.selectedCommunity?.uuid != null) { + if (widget.selectedCommunity != null && + widget.selectedCommunity?.uuid != null && + widget.selectedSpace == null) { context.read().add(DeleteCommunityEvent( communityUuid: widget.selectedCommunity!.uuid, )); - + } + if (widget.selectedSpace != null) { + setState(() { + for (var space in spaces) { + if (space.uuid == widget.selectedSpace?.uuid) { + space.status = SpaceStatus.deleted; + _markChildrenAsDeleted(space); + } + } + _removeConnectionsForDeletedSpaces(); + }); } } + + void _markChildrenAsDeleted(SpaceModel parent) { + for (var child in parent.children) { + child.status = SpaceStatus.deleted; + _markChildrenAsDeleted(child); + } + } + + void _removeConnectionsForDeletedSpaces() { + connections.removeWhere((connection) { + return connection.startSpace.status == SpaceStatus.deleted || + connection.endSpace.status == SpaceStatus.deleted; + }); + } } diff --git a/lib/pages/spaces_management/widgets/loaded_space_widget.dart b/lib/pages/spaces_management/widgets/loaded_space_widget.dart index 082224b6..79a493bb 100644 --- a/lib/pages/spaces_management/widgets/loaded_space_widget.dart +++ b/lib/pages/spaces_management/widgets/loaded_space_widget.dart @@ -10,8 +10,8 @@ class LoadedSpaceView extends StatefulWidget { final List communities; final CommunityModel? selectedCommunity; final SpaceModel? selectedSpace; - final ValueChanged onCommunitySelected; - final ValueChanged onSpaceSelected; + final ValueChanged? onCommunitySelected; + final ValueChanged? onSpaceSelected; final List? products; const LoadedSpaceView({ diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 4e8a9046..41a9e649 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -15,7 +15,7 @@ import 'package:syncrow_web/utils/style.dart'; class SidebarWidget extends StatefulWidget { final Function(CommunityModel)? onCommunitySelected; - final Function(SpaceModel)? onSpaceSelected; + final Function(SpaceModel?)? onSpaceSelected; final List communities; const SidebarWidget( @@ -190,7 +190,8 @@ class _SidebarWidgetState extends State { }); if (widget.onCommunitySelected != null) { - widget.onCommunitySelected!(community); // Pass the entire community + widget.onCommunitySelected!(community); + widget.onSpaceSelected!(null); // Pass the entire community } }, onExpansionChanged: (String title, bool expanded) { @@ -204,7 +205,6 @@ class _SidebarWidgetState extends State { Widget _buildSpaceTile(SpaceModel space, CommunityModel community) { bool isExpandedSpace = _isSpaceOrChildSelected(space); - bool isSelectedSpace = _selectedSpaceUuid == space.uuid; // Check if space should be expanded return SpaceTile( title: space.name, From 6c5b01e7c210f1034c4eabfbbc05bbeced2beb82 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 16:50:25 +0400 Subject: [PATCH 077/122] fixed edit community --- assets/icons/edit.svg | 4 ++ .../bloc/space_management_bloc.dart | 20 +++++- .../bloc/space_management_event.dart | 13 ++++ .../model/community_model.dart | 13 ++-- .../widgets/community_structure_widget.dart | 67 ++++++++++++++++++- lib/services/space_mana_api.dart | 6 +- lib/utils/constants/assets.dart | 1 + 7 files changed, 109 insertions(+), 15 deletions(-) create mode 100644 assets/icons/edit.svg diff --git a/assets/icons/edit.svg b/assets/icons/edit.svg new file mode 100644 index 00000000..ac510f4a --- /dev/null +++ b/assets/icons/edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 4b79ff1c..401a960a 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -20,6 +20,24 @@ class SpaceManagementBloc extends Bloc(_onSaveSpaces); on(_onFetchProducts); on(_onCommunityDelete); + on(_onUpdateCommunity); + } + + void _onUpdateCommunity( + UpdateCommunityEvent event, + Emitter emit, + ) async { + try { + emit(SpaceManagementLoading()); + final success = await _api.updateCommunity(event.communityUuid, event.name); + if (success) { + add(LoadCommunityAndSpacesEvent()); + } else { + emit(const SpaceManagementError('Failed to update the community.')); + } + } catch (e) { + emit(SpaceManagementError('Error updating community: $e')); + } } void _onFetchProducts( @@ -160,7 +178,7 @@ class SpaceManagementBloc extends Bloc get props => [communityId]; } + +class UpdateCommunityEvent extends SpaceManagementEvent { + final String communityUuid; + final String name; + + const UpdateCommunityEvent({ + required this.communityUuid, + required this.name, + }); + + @override + List get props => [communityUuid, name]; +} diff --git a/lib/pages/spaces_management/model/community_model.dart b/lib/pages/spaces_management/model/community_model.dart index 182aee73..b61b780b 100644 --- a/lib/pages/spaces_management/model/community_model.dart +++ b/lib/pages/spaces_management/model/community_model.dart @@ -5,7 +5,7 @@ class CommunityModel { final String uuid; final DateTime createdAt; final DateTime updatedAt; - final String name; + String name; final String description; final RegionModel? region; List spaces; @@ -27,12 +27,9 @@ class CommunityModel { updatedAt: DateTime.parse(json['updatedAt']), name: json['name'], description: json['description'], - region: - json['region'] != null ? RegionModel.fromJson(json['region']) : null, + region: json['region'] != null ? RegionModel.fromJson(json['region']) : null, spaces: json['spaces'] != null - ? (json['spaces'] as List) - .map((space) => SpaceModel.fromJson(space)) - .toList() + ? (json['spaces'] as List).map((space) => SpaceModel.fromJson(space)).toList() : [], ); } @@ -45,9 +42,7 @@ class CommunityModel { 'name': name, 'description': description, 'region': region?.toJson(), - 'spaces': spaces - .map((space) => space.toMap()) - .toList(), // Convert spaces to Map + 'spaces': spaces.map((space) => space.toMap()).toList(), // Convert spaces to Map }; } } diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 51bdccc2..a6a679f4 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -42,6 +42,8 @@ class _CommunityStructureAreaState extends State { double canvasHeight = 1000; List spaces = []; List connections = []; + late TextEditingController _nameController; + bool isEditingName = false; @override void initState() { @@ -49,6 +51,15 @@ class _CommunityStructureAreaState extends State { spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; _adjustCanvasSizeForSpaces(); + _nameController = TextEditingController( + text: widget.selectedCommunity?.name ?? '', + ); + } + + @override + void dispose() { + _nameController.dispose(); + super.dispose(); } @override @@ -176,9 +187,59 @@ class _CommunityStructureAreaState extends State { style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), ), if (widget.selectedCommunity != null) - Text( - widget.selectedCommunity?.name ?? '', - style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), + Row( + children: [ + // Show Text widget when not editing + if (!isEditingName) + Text( + widget.selectedCommunity?.name ?? '', + style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), + ), + if (isEditingName) // Show TextField when editing + SizedBox( + width: 200, // Adjusted width to make TextField visible + child: TextField( + controller: _nameController, + decoration: const InputDecoration( + border: InputBorder.none, + isDense: true, // Reduce the height of the TextField + ), + style: const TextStyle( + fontSize: 16, + color: ColorsManager.blackColor, + ), + onSubmitted: (value) { + context.read().add( + UpdateCommunityEvent( + communityUuid: widget.selectedCommunity!.uuid, + name: value, + ), + ); + setState(() { + widget.selectedCommunity?.name = value; // Update the name + isEditingName = false; // Exit edit mode + }); + }, + ), + ), + const SizedBox(width: 8), + if (!isEditingName) + GestureDetector( + onTap: () { + setState(() { + isEditingName = !isEditingName; // Toggle edit mode + }); + if (isEditingName) { + _nameController.text = widget.selectedCommunity?.name ?? ''; // Pre-fill + } + }, + child: SvgPicture.asset( + Assets.iconEdit, // Path to the edit icon SVG asset + width: 16, + height: 16, + ), + ), + ], ), ], ), diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index e09fe8f6..07b98f10 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -65,11 +65,13 @@ class CommunitySpaceManagementApi { } } - Future updateCommunity(String communityId, CommunityModel community) async { + Future updateCommunity(String communityId, String name) async { try { final response = await HTTPService().put( path: ApiEndpoints.updateCommunity.replaceAll('{communityId}', communityId), - body: community.toMap(), + body: { + 'name': name, + }, expectedResponseModel: (json) { return json['success'] ?? false; }, diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index d96ce0dc..6abd689e 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -236,4 +236,5 @@ class Assets { static const String doorSensor = 'assets/icons/door_sensor.svg'; static const String delete = 'assets/icons/delete.svg'; + static const String edit = 'assets/icons/edit.svg'; } From 923911677338edd05e473cf720e178d4a84dde42 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 16:54:20 +0400 Subject: [PATCH 078/122] set default icon for space --- lib/pages/spaces_management/model/space_model.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages/spaces_management/model/space_model.dart b/lib/pages/spaces_management/model/space_model.dart index f8a5d412..e1594a4c 100644 --- a/lib/pages/spaces_management/model/space_model.dart +++ b/lib/pages/spaces_management/model/space_model.dart @@ -2,6 +2,7 @@ import 'dart:ui'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; enum SpaceStatus { newSpace, modified, unchanged, deleted } @@ -53,7 +54,7 @@ class SpaceModel { children: json['children'] != null ? (json['children'] as List).map((child) => SpaceModel.fromJson(child)).toList() : [], - icon: json['icon'] as String?, + icon: json['icon'] != null? json['icon'] : Assets.location, position: json['x'] != null && json['y'] != null ? Offset(json['x'], json['y']) : const Offset(0, 0), From 4a9d99d7b020040dc97d74f7e8096eccf050246e Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 19:36:16 +0400 Subject: [PATCH 079/122] fixed moving space on selecting space --- .../widgets/community_structure_widget.dart | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index a6a679f4..cdfb3345 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -44,6 +44,7 @@ class _CommunityStructureAreaState extends State { List connections = []; late TextEditingController _nameController; bool isEditingName = false; + late TransformationController _transformationController; @override void initState() { @@ -54,11 +55,18 @@ class _CommunityStructureAreaState extends State { _nameController = TextEditingController( text: widget.selectedCommunity?.name ?? '', ); + _transformationController = TransformationController(); + if (widget.selectedSpace != null) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _moveToSpace(widget.selectedSpace!); + }); + } } @override void dispose() { _nameController.dispose(); + _transformationController.dispose(); super.dispose(); } @@ -73,6 +81,12 @@ class _CommunityStructureAreaState extends State { _adjustCanvasSizeForSpaces(); }); } + + if (widget.selectedSpace != oldWidget.selectedSpace && widget.selectedSpace != null) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _moveToSpace(widget.selectedSpace!); + }); + } } @override @@ -97,6 +111,7 @@ class _CommunityStructureAreaState extends State { children: [ if (visibleSpaces.isNotEmpty) InteractiveViewer( + transformationController: _transformationController, boundaryMargin: EdgeInsets.all(500), minScale: 0.5, maxScale: 3.0, @@ -528,4 +543,16 @@ class _CommunityStructureAreaState extends State { connection.endSpace.status == SpaceStatus.deleted; }); } + + void _moveToSpace(SpaceModel space) { + final double viewportWidth = MediaQuery.of(context).size.width; + final double viewportHeight = MediaQuery.of(context).size.height; + + final double dx = -space.position.dx + (viewportWidth / 2) - 400; + final double dy = -space.position.dy + (viewportHeight / 2) - 350; + + _transformationController.value = Matrix4.identity() + ..translate(dx, dy) + ..scale(1.2); + } } From 6f554dc941688e6b930ae7d0cece8ff8a4d3e3ac Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 19:39:55 +0400 Subject: [PATCH 080/122] fixed community selection --- lib/pages/spaces_management/widgets/sidebar_widget.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 41a9e649..585b01b8 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -179,8 +179,8 @@ class _SidebarWidgetState extends State { // Check if this community is selected return CommunityTile( title: community.name, - key: ValueKey(community.uuid), - isSelected: _selectedId == community.uuid, + key: ValueKey(community.uuid), + isSelected: _selectedId == community.uuid, isExpanded: false, onItemSelected: () { setState(() { @@ -208,7 +208,7 @@ class _SidebarWidgetState extends State { // Check if space should be expanded return SpaceTile( title: space.name, - key: ValueKey(space.uuid), + key: ValueKey(space.uuid), isSelected: _selectedId == space.uuid, initiallyExpanded: isExpandedSpace, onExpansionChanged: (bool expanded) { @@ -221,6 +221,7 @@ class _SidebarWidgetState extends State { _selectedCommunityUuid = community.uuid; // Update selected community }); if (widget.onSpaceSelected != null) { + widget.onCommunitySelected!(community); widget.onSpaceSelected!(space); } }, From ede4512b6893760fdccc880df8c00ed0dfab5470 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 19:41:41 +0400 Subject: [PATCH 081/122] fixed positioning --- .../spaces_management/widgets/community_structure_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index cdfb3345..8a7a14ad 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -549,7 +549,7 @@ class _CommunityStructureAreaState extends State { final double viewportHeight = MediaQuery.of(context).size.height; final double dx = -space.position.dx + (viewportWidth / 2) - 400; - final double dy = -space.position.dy + (viewportHeight / 2) - 350; + final double dy = -space.position.dy + (viewportHeight / 2) - 300; _transformationController.value = Matrix4.identity() ..translate(dx, dy) From 082f8b0b270eb9e45906bb44d00a45445ce780e0 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 20:05:21 +0400 Subject: [PATCH 082/122] select space --- .../widgets/community_structure_widget.dart | 8 ++++++++ .../spaces_management/widgets/space_container_widget.dart | 2 ++ 2 files changed, 10 insertions(+) diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 8a7a14ad..da7f3126 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -150,6 +150,9 @@ class _CommunityStructureAreaState extends State { onDoubleTap: () { _showEditSpaceDialog(visibleSpaces[index]); }, + onTap: (){ + _selectSpace(visibleSpaces[index]); + }, icon: visibleSpaces[index].icon ?? '', name: visibleSpaces[index].name, ); @@ -555,4 +558,9 @@ class _CommunityStructureAreaState extends State { ..translate(dx, dy) ..scale(1.2); } + + void _selectSpace(SpaceModel space){ + print(space.name); + } + } diff --git a/lib/pages/spaces_management/widgets/space_container_widget.dart b/lib/pages/spaces_management/widgets/space_container_widget.dart index bb40da51..6bc70705 100644 --- a/lib/pages/spaces_management/widgets/space_container_widget.dart +++ b/lib/pages/spaces_management/widgets/space_container_widget.dart @@ -7,12 +7,14 @@ class SpaceContainerWidget extends StatelessWidget { final String icon; final String name; final VoidCallback? onDoubleTap; + final VoidCallback? onTap; const SpaceContainerWidget({ super.key, required this.index, required this.icon, required this.name, + this.onTap, this.onDoubleTap, }); From 278d39cca17d4bc4b63b9cd6b4fa8276653d60ec Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 20:40:36 +0400 Subject: [PATCH 083/122] fixed space select --- .../widgets/community_structure_widget.dart | 17 +++++--- .../widgets/loaded_space_widget.dart | 32 ++++++++++++--- .../widgets/sidebar_widget.dart | 39 +++++++++++++------ .../widgets/space_container_widget.dart | 1 + 4 files changed, 68 insertions(+), 21 deletions(-) diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index da7f3126..c8f8b3f1 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -19,8 +19,9 @@ import 'package:syncrow_web/utils/constants/assets.dart'; class CommunityStructureArea extends StatefulWidget { final CommunityModel? selectedCommunity; - final SpaceModel? selectedSpace; + SpaceModel? selectedSpace; final List? products; + final ValueChanged? onSpaceSelected; final List spaces; final List connections; @@ -31,6 +32,7 @@ class CommunityStructureArea extends StatefulWidget { this.products, required this.spaces, required this.connections, + this.onSpaceSelected, }); @override @@ -150,7 +152,7 @@ class _CommunityStructureAreaState extends State { onDoubleTap: () { _showEditSpaceDialog(visibleSpaces[index]); }, - onTap: (){ + onTap: () { _selectSpace(visibleSpaces[index]); }, icon: visibleSpaces[index].icon ?? '', @@ -559,8 +561,13 @@ class _CommunityStructureAreaState extends State { ..scale(1.2); } - void _selectSpace(SpaceModel space){ - print(space.name); - } + void _selectSpace(SpaceModel space) { + setState(() { + widget.selectedSpace = space; + }); + if (widget.onSpaceSelected != null) { + widget.onSpaceSelected!(space); + } + } } diff --git a/lib/pages/spaces_management/widgets/loaded_space_widget.dart b/lib/pages/spaces_management/widgets/loaded_space_widget.dart index 79a493bb..509ddac1 100644 --- a/lib/pages/spaces_management/widgets/loaded_space_widget.dart +++ b/lib/pages/spaces_management/widgets/loaded_space_widget.dart @@ -37,16 +37,29 @@ class _LoadedStateViewState extends State { Row( children: [ SidebarWidget( - communities: widget.communities, - onCommunitySelected: widget.onCommunitySelected, - onSpaceSelected: widget.onSpaceSelected, - ), + communities: widget.communities, + onCommunitySelected: widget.onCommunitySelected, + onSpaceSelected: widget.onSpaceSelected, + selectedSpaceUuid: widget.selectedSpace?.uuid, + onSelectedSpaceChanged: (String? spaceUuid) { + setState(() { + final selectedSpace = findSpaceByUuid(spaceUuid, widget.communities); + if (selectedSpace != null) { + widget.onSpaceSelected!(selectedSpace); + } + }); + }), CommunityStructureArea( selectedCommunity: widget.selectedCommunity, selectedSpace: widget.selectedSpace, spaces: widget.selectedCommunity?.spaces ?? [], - connections: [], + connections: const [], products: widget.products, + onSpaceSelected: (SpaceModel? space) { + setState(() { + widget.onSpaceSelected!(space); + }); + }, ), ], ), @@ -54,4 +67,13 @@ class _LoadedStateViewState extends State { ], ); } + + SpaceModel? findSpaceByUuid(String? uuid, List communities) { + for (var community in communities) { + for (var space in community.spaces) { + if (space.uuid == uuid) return space; + } + } + return null; + } } diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 585b01b8..25da7d04 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -17,9 +17,18 @@ class SidebarWidget extends StatefulWidget { final Function(CommunityModel)? onCommunitySelected; final Function(SpaceModel?)? onSpaceSelected; final List communities; + final Function(String?)? onSelectedSpaceChanged; // New callback - const SidebarWidget( - {super.key, this.onCommunitySelected, this.onSpaceSelected, required this.communities}); + final String? selectedSpaceUuid; + + const SidebarWidget({ + super.key, + this.onCommunitySelected, + this.onSpaceSelected, + this.onSelectedSpaceChanged, + required this.communities, + this.selectedSpaceUuid, + }); @override _SidebarWidgetState createState() => _SidebarWidgetState(); @@ -27,13 +36,23 @@ class SidebarWidget extends StatefulWidget { class _SidebarWidgetState extends State { String _searchQuery = ''; // Track search query - String? _selectedCommunityUuid; String? _selectedSpaceUuid; String? _selectedId; @override void initState() { super.initState(); + _selectedId = widget.selectedSpaceUuid; // Initialize with the passed selected space UUID + } + + @override + void didUpdateWidget(covariant SidebarWidget oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.selectedSpaceUuid != oldWidget.selectedSpaceUuid) { + setState(() { + _selectedId = widget.selectedSpaceUuid; + }); + } } void _showCreateCommunityDialog(BuildContext parentContext) { @@ -57,7 +76,6 @@ class _SidebarWidgetState extends State { List _filterCommunities() { if (_searchQuery.isEmpty) { // Reset the selected community and space UUIDs if there's no query - _selectedCommunityUuid = null; _selectedSpaceUuid = null; return widget.communities; } @@ -69,9 +87,7 @@ class _SidebarWidgetState extends State { final containsQueryInSpaces = community.spaces.any((space) => _containsQuery(space, _searchQuery.toLowerCase())); - if (containsQueryInCommunity || containsQueryInSpaces) { - _selectedCommunityUuid = community.uuid; // Set the community to expanded - } + return containsQueryInCommunity || containsQueryInSpaces; }).toList(); @@ -174,9 +190,7 @@ class _SidebarWidgetState extends State { Widget _buildCommunityTile(CommunityModel community) { bool hasChildren = community.spaces.isNotEmpty; - bool isSelectedCommunity = _selectedCommunityUuid == community.uuid; - // Check if this community is selected return CommunityTile( title: community.name, key: ValueKey(community.uuid), @@ -185,7 +199,6 @@ class _SidebarWidgetState extends State { onItemSelected: () { setState(() { _selectedId = community.uuid; - _selectedCommunityUuid = community.uuid; _selectedSpaceUuid = null; // Update the selected community }); @@ -218,12 +231,16 @@ class _SidebarWidgetState extends State { setState(() { _selectedId = space.uuid; _selectedSpaceUuid = space.uuid; - _selectedCommunityUuid = community.uuid; // Update selected community }); + if (widget.onSpaceSelected != null) { widget.onCommunitySelected!(community); widget.onSpaceSelected!(space); } + + if (widget.onSelectedSpaceChanged != null) { + widget.onSelectedSpaceChanged!(space.uuid); + } }, children: space.children.isNotEmpty ? space.children.map((childSpace) => _buildSpaceTile(childSpace, community)).toList() diff --git a/lib/pages/spaces_management/widgets/space_container_widget.dart b/lib/pages/spaces_management/widgets/space_container_widget.dart index 6bc70705..273319ce 100644 --- a/lib/pages/spaces_management/widgets/space_container_widget.dart +++ b/lib/pages/spaces_management/widgets/space_container_widget.dart @@ -22,6 +22,7 @@ class SpaceContainerWidget extends StatelessWidget { Widget build(BuildContext context) { return GestureDetector( onDoubleTap: onDoubleTap, + onTap: onTap, child: Container( width: 150, height: 60, From 8b7de7c8fd6550f0a24b3e2cefcc45f363d02e1e Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 21:05:49 +0400 Subject: [PATCH 084/122] deselect on outside --- .../widgets/device_search_filters.dart | 1 - .../widgets/community_structure_widget.dart | 57 ++++++++++++++----- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/lib/pages/device_managment/all_devices/widgets/device_search_filters.dart b/lib/pages/device_managment/all_devices/widgets/device_search_filters.dart index 71974156..c68f9485 100644 --- a/lib/pages/device_managment/all_devices/widgets/device_search_filters.dart +++ b/lib/pages/device_managment/all_devices/widgets/device_search_filters.dart @@ -4,7 +4,6 @@ import 'package:syncrow_web/pages/common/text_field/custom_text_field.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_managment_bloc.dart'; import 'package:syncrow_web/pages/common/buttons/search_reset_buttons.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; -import 'package:syncrow_web/utils/style.dart'; class DeviceSearchFilters extends StatefulWidget { const DeviceSearchFilters({super.key}); diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index c8f8b3f1..4f609827 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -94,10 +94,13 @@ class _CommunityStructureAreaState extends State { @override Widget build(BuildContext context) { final visibleSpaces = flattenSpaces(widget.spaces); - final visibleConnections = createConnections(widget.spaces); Size screenSize = MediaQuery.of(context).size; return Expanded( + child: GestureDetector( + onTap: () { + _deselectSpace(); + }, child: Container( decoration: const BoxDecoration( border: Border( @@ -147,17 +150,22 @@ class _CommunityStructureAreaState extends State { _updateNodePosition(entry.value, newPosition); }, buildSpaceContainer: (int index) { - return SpaceContainerWidget( - index: index, - onDoubleTap: () { - _showEditSpaceDialog(visibleSpaces[index]); - }, - onTap: () { - _selectSpace(visibleSpaces[index]); - }, - icon: visibleSpaces[index].icon ?? '', - name: visibleSpaces[index].name, - ); + final bool isHighlighted = + _isHighlightedSpace(visibleSpaces[index]); + + return Opacity( + opacity: isHighlighted ? 1.0 : 0.3, + child: SpaceContainerWidget( + index: index, + onDoubleTap: () { + _showEditSpaceDialog(visibleSpaces[index]); + }, + onTap: () { + _selectSpace(visibleSpaces[index]); + }, + icon: visibleSpaces[index].icon ?? '', + name: visibleSpaces[index].name, + )); }, ), ), @@ -178,7 +186,7 @@ class _CommunityStructureAreaState extends State { ], ), ), - ); + )); } Widget _buildHeader() { @@ -570,4 +578,27 @@ class _CommunityStructureAreaState extends State { widget.onSpaceSelected!(space); } } + + bool _isHighlightedSpace(SpaceModel space) { + if (widget.selectedSpace == null) return true; + if (space == widget.selectedSpace) return true; + + if (widget.selectedSpace?.parent?.name == space.name && + widget.selectedSpace?.parent?.position == space.position) return true; + if (widget.selectedSpace?.children.contains(space) == true) return true; + + return false; // Otherwise, reduce opacity + } + + void _deselectSpace() { + if (widget.selectedSpace != null) { + setState(() { + widget.selectedSpace = null; + }); + + if (widget.onSpaceSelected != null) { + widget.onSpaceSelected!(null); // Notify parent that no space is selected + } + } + } } From b6998c055abc321bf9c31e0130474bc282fdaad8 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 21:09:49 +0400 Subject: [PATCH 085/122] applied opacity to connections --- .../widgets/community_structure_widget.dart | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 4f609827..51b4a403 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -127,7 +127,12 @@ class _CommunityStructureAreaState extends State { child: Stack( children: [ for (var connection in connections) - CustomPaint(painter: CurvedLinePainter([connection])), + Opacity( + opacity: _isHighlightedConnection(connection) + ? 1.0 + : 0.3, // Adjust opacity + child: CustomPaint(painter: CurvedLinePainter([connection])), + ), for (var entry in visibleSpaces.asMap().entries) Positioned( left: entry.value.position.dx, @@ -601,4 +606,11 @@ class _CommunityStructureAreaState extends State { } } } + + bool _isHighlightedConnection(Connection connection) { + if (widget.selectedSpace == null) return true; + + return connection.startSpace == widget.selectedSpace || + connection.endSpace == widget.selectedSpace; + } } From 82343e06342dbe87f6295d19bc77ec0d41a1d7e3 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 21 Nov 2024 21:34:56 +0400 Subject: [PATCH 086/122] fixed create space --- .../widgets/community_structure_widget.dart | 9 +++------ .../spaces_management/widgets/loaded_space_widget.dart | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 51b4a403..cdeccf32 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -24,14 +24,12 @@ class CommunityStructureArea extends StatefulWidget { final ValueChanged? onSpaceSelected; final List spaces; - final List connections; CommunityStructureArea({ this.selectedCommunity, this.selectedSpace, this.products, required this.spaces, - required this.connections, this.onSpaceSelected, }); @@ -76,7 +74,7 @@ class _CommunityStructureAreaState extends State { void didUpdateWidget(covariant CommunityStructureArea oldWidget) { super.didUpdateWidget(oldWidget); - if (oldWidget.spaces != widget.spaces || oldWidget.connections != widget.connections) { + if (oldWidget.spaces != widget.spaces) { setState(() { spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; @@ -93,8 +91,7 @@ class _CommunityStructureAreaState extends State { @override Widget build(BuildContext context) { - final visibleSpaces = flattenSpaces(widget.spaces); - + final visibleSpaces = flattenSpaces(spaces); Size screenSize = MediaQuery.of(context).size; return Expanded( child: GestureDetector( @@ -277,7 +274,7 @@ class _CommunityStructureAreaState extends State { ], ), // Show "Save" button only if there are spaces - if (widget.spaces.isNotEmpty && widget.selectedCommunity != null) + if (spaces.isNotEmpty && widget.selectedCommunity != null) Row(children: [ ElevatedButton.icon( onPressed: () { diff --git a/lib/pages/spaces_management/widgets/loaded_space_widget.dart b/lib/pages/spaces_management/widgets/loaded_space_widget.dart index 509ddac1..3003b855 100644 --- a/lib/pages/spaces_management/widgets/loaded_space_widget.dart +++ b/lib/pages/spaces_management/widgets/loaded_space_widget.dart @@ -53,7 +53,6 @@ class _LoadedStateViewState extends State { selectedCommunity: widget.selectedCommunity, selectedSpace: widget.selectedSpace, spaces: widget.selectedCommunity?.spaces ?? [], - connections: const [], products: widget.products, onSpaceSelected: (SpaceModel? space) { setState(() { From 1a967e2ba04a2d52c8e85975a2c8774bba133e05 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 22 Nov 2024 00:29:35 +0400 Subject: [PATCH 087/122] removed region from create --- lib/pages/spaces_management/bloc/space_management_bloc.dart | 3 +-- lib/pages/spaces_management/bloc/space_management_event.dart | 4 +--- .../widgets/dialogs/create_community_dialog.dart | 3 +-- lib/pages/spaces_management/widgets/sidebar_widget.dart | 3 +-- lib/services/space_mana_api.dart | 3 +-- 5 files changed, 5 insertions(+), 11 deletions(-) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 401a960a..913c4c92 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -127,8 +127,7 @@ class SpaceManagementBloc extends Bloc get props => [name, description, regionId]; + List get props => [name, description]; } class FetchProductsEvent extends SpaceManagementEvent {} diff --git a/lib/pages/spaces_management/widgets/dialogs/create_community_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/create_community_dialog.dart index a02ea572..f875f5ef 100644 --- a/lib/pages/spaces_management/widgets/dialogs/create_community_dialog.dart +++ b/lib/pages/spaces_management/widgets/dialogs/create_community_dialog.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class CreateCommunityDialog extends StatefulWidget { - final Function(String name, String description, String regionId) + final Function(String name, String description) onCreateCommunity; const CreateCommunityDialog({super.key, required this.onCreateCommunity}); @@ -114,7 +114,6 @@ class CreateCommunityDialogState extends State { widget.onCreateCommunity( enteredName, "", - "42dc377a-1a39-4df9-b85a-b3817af88525", ); Navigator.of(context).pop(); } diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 25da7d04..94817e75 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -59,12 +59,11 @@ class _SidebarWidgetState extends State { showDialog( context: parentContext, builder: (context) => CreateCommunityDialog( - onCreateCommunity: (String communityName, String description, String regionId) { + onCreateCommunity: (String communityName, String description) { parentContext.read().add( CreateCommunityEvent( name: communityName, description: description, - regionId: regionId, ), ); }, diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index 07b98f10..01248262 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -45,14 +45,13 @@ class CommunitySpaceManagementApi { } } - Future createCommunity(String name, String description, String regionId) async { + Future createCommunity(String name, String description) async { try { final response = await HTTPService().post( path: ApiEndpoints.createCommunity, body: { 'name': name, 'description': description, - 'regionId': regionId, }, expectedResponseModel: (json) { return CommunityModel.fromJson(json['data']); From 12bf0f0b57a2e535d5e7f4a741a584be688baf86 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sat, 23 Nov 2024 15:37:37 +0400 Subject: [PATCH 088/122] fixed hover issue --- .../widgets/community_structure_widget.dart | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index cdeccf32..6de9e06c 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -91,7 +91,6 @@ class _CommunityStructureAreaState extends State { @override Widget build(BuildContext context) { - final visibleSpaces = flattenSpaces(spaces); Size screenSize = MediaQuery.of(context).size; return Expanded( child: GestureDetector( @@ -111,7 +110,6 @@ class _CommunityStructureAreaState extends State { Flexible( child: Stack( children: [ - if (visibleSpaces.isNotEmpty) InteractiveViewer( transformationController: _transformationController, boundaryMargin: EdgeInsets.all(500), @@ -130,7 +128,8 @@ class _CommunityStructureAreaState extends State { : 0.3, // Adjust opacity child: CustomPaint(painter: CurvedLinePainter([connection])), ), - for (var entry in visibleSpaces.asMap().entries) + for (var entry in spaces.asMap().entries) + if(entry.value.status != SpaceStatus.deleted) Positioned( left: entry.value.position.dx, top: entry.value.position.dy, @@ -139,7 +138,7 @@ class _CommunityStructureAreaState extends State { onButtonTap: (int index, Offset newPosition, String direction) { _showCreateSpaceDialog( screenSize, - position: visibleSpaces[index].position + newPosition, + position: spaces[index].position + newPosition, parentIndex: index, direction: direction, ); @@ -153,20 +152,20 @@ class _CommunityStructureAreaState extends State { }, buildSpaceContainer: (int index) { final bool isHighlighted = - _isHighlightedSpace(visibleSpaces[index]); + _isHighlightedSpace(spaces[index]); return Opacity( opacity: isHighlighted ? 1.0 : 0.3, child: SpaceContainerWidget( index: index, onDoubleTap: () { - _showEditSpaceDialog(visibleSpaces[index]); + _showEditSpaceDialog(spaces[index]); }, onTap: () { - _selectSpace(visibleSpaces[index]); + _selectSpace(spaces[index]); }, - icon: visibleSpaces[index].icon ?? '', - name: visibleSpaces[index].name, + icon: spaces[index].icon ?? '', + name: spaces[index].name, )); }, ), @@ -175,7 +174,7 @@ class _CommunityStructureAreaState extends State { ), ), ), - if (visibleSpaces.isEmpty) + if (spaces.isEmpty) Center( child: AddSpaceButton( onTap: () { From 729bd41bec15dde0b49b9f4262319a66611ad678 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sat, 23 Nov 2024 18:34:52 +0400 Subject: [PATCH 089/122] fixed style --- .../widgets/dialogs/delete_dialogue.dart | 51 +++++-------------- .../widgets/sidebar_widget.dart | 14 +++-- 2 files changed, 20 insertions(+), 45 deletions(-) diff --git a/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart b/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart index 4e9b25fc..18a67dd2 100644 --- a/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart +++ b/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; import 'package:syncrow_web/utils/color_manager.dart'; void showDeleteConfirmationDialog(BuildContext context, VoidCallback onConfirm, bool isSpace) { @@ -74,22 +75,9 @@ void showDeleteConfirmationDialog(BuildContext context, VoidCallback onConfirm, Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - ElevatedButton( - onPressed: () { - Navigator.of(context).pop(); // Close the first dialog - // Trigger the second popup - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - side: const BorderSide(color: ColorsManager.boxColor), // Black border - ), - ), - child: const Text( - 'Cancel', - style: TextStyle(color: Colors.black), - ), + CancelButton( + label: 'Cancel', + onPressed: () => Navigator.of(context).pop(), ), ElevatedButton( onPressed: () { @@ -97,11 +85,11 @@ void showDeleteConfirmationDialog(BuildContext context, VoidCallback onConfirm, showProcessingPopup(context, isSpace, onConfirm); }, style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - ), + backgroundColor: Colors.blue, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + fixedSize: Size(140, 40)), child: const Text( 'Continue', style: TextStyle(color: Colors.white), @@ -184,13 +172,13 @@ void showProcessingPopup(BuildContext context, bool isSpace, VoidCallback onDele ElevatedButton( onPressed: () { // Trigger the second popup - onDelete(); + onDelete(); }, style: ElevatedButton.styleFrom( backgroundColor: ColorsManager.warningRed, + fixedSize: Size(140, 40), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), - side: const BorderSide(color: ColorsManager.boxColor), // Black border ), ), child: const Text( @@ -198,20 +186,9 @@ void showProcessingPopup(BuildContext context, bool isSpace, VoidCallback onDele style: TextStyle(color: Colors.white), ), ), - ElevatedButton( - onPressed: () { - Navigator.of(context).pop(); // Close the first dialog - }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - ), - child: const Text( - 'Cancel', - style: TextStyle(color: Colors.black), - ), + CancelButton( + label: 'Cancel', + onPressed: () => Navigator.of(context).pop(), ), ], ), diff --git a/lib/pages/spaces_management/widgets/sidebar_widget.dart b/lib/pages/spaces_management/widgets/sidebar_widget.dart index 94817e75..507cd703 100644 --- a/lib/pages/spaces_management/widgets/sidebar_widget.dart +++ b/lib/pages/spaces_management/widgets/sidebar_widget.dart @@ -50,7 +50,7 @@ class _SidebarWidgetState extends State { super.didUpdateWidget(oldWidget); if (widget.selectedSpaceUuid != oldWidget.selectedSpaceUuid) { setState(() { - _selectedId = widget.selectedSpaceUuid; + _selectedId = widget.selectedSpaceUuid; }); } } @@ -86,8 +86,6 @@ class _SidebarWidgetState extends State { final containsQueryInSpaces = community.spaces.any((space) => _containsQuery(space, _searchQuery.toLowerCase())); - - return containsQueryInCommunity || containsQueryInSpaces; }).toList(); } @@ -140,10 +138,10 @@ class _SidebarWidgetState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - 'Communities', - style: Theme.of(context).textTheme.titleMedium, - ), + Text('Communities', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: Colors.black, + )), GestureDetector( onTap: () => _showCreateCommunityDialog(context), child: Container( @@ -238,7 +236,7 @@ class _SidebarWidgetState extends State { } if (widget.onSelectedSpaceChanged != null) { - widget.onSelectedSpaceChanged!(space.uuid); + widget.onSelectedSpaceChanged!(space.uuid); } }, children: space.children.isNotEmpty From bca7aa059d0cdee5e76ff14498a60f5d31465e7f Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sat, 23 Nov 2024 18:41:44 +0400 Subject: [PATCH 090/122] optimized --- .../widgets/dialogs/delete_dialogue.dart | 182 ++++++------------ 1 file changed, 59 insertions(+), 123 deletions(-) diff --git a/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart b/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart index 18a67dd2..41646a47 100644 --- a/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart +++ b/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart @@ -3,75 +3,31 @@ import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; import 'package:syncrow_web/utils/color_manager.dart'; void showDeleteConfirmationDialog(BuildContext context, VoidCallback onConfirm, bool isSpace) { + final String title = isSpace ? 'Delete Space' : 'Delete Community'; + final String subtitle = isSpace + ? 'All the data in the space will be lost' + : 'All the data in the community will be lost'; + showDialog( context: context, - barrierDismissible: false, // Prevent dismissing by tapping outside + barrierDismissible: false, builder: (BuildContext context) { return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16.0)), child: SizedBox( - width: 500, // Set the desired width + width: 500, child: Container( color: ColorsManager.whiteColors, padding: const EdgeInsets.all(20.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ - // Icon - Container( - width: 50, - height: 50, - decoration: BoxDecoration( - color: ColorsManager.warningRed, - shape: BoxShape.circle, - ), - child: const Icon( - Icons.close, - color: Colors.white, - size: 40, - ), - ), + _buildWarningIcon(), const SizedBox(height: 20), - // Title - isSpace - ? const Text( - 'Delete Space', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ) - : const Text( - 'Delete Community', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), + _buildDialogTitle(context,title), const SizedBox(height: 10), - // Subtitle - isSpace - ? const Text( - 'All the data in the space will be lost', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - color: Colors.grey, - ), - ) - : const Text( - 'All the data in the community will be lost', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - color: Colors.grey, - ), - ), - + _buildDialogSubtitle(context, subtitle), const SizedBox(height: 20), - // Buttons Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ @@ -84,16 +40,8 @@ void showDeleteConfirmationDialog(BuildContext context, VoidCallback onConfirm, Navigator.of(context).pop(); // Close the first dialog showProcessingPopup(context, isSpace, onConfirm); }, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - fixedSize: Size(140, 40)), - child: const Text( - 'Continue', - style: TextStyle(color: Colors.white), - ), + style: _dialogButtonStyle(Colors.blue), + child: const Text('Continue', style: TextStyle(color: Colors.white)), ), ], ), @@ -107,14 +55,14 @@ void showDeleteConfirmationDialog(BuildContext context, VoidCallback onConfirm, } void showProcessingPopup(BuildContext context, bool isSpace, VoidCallback onDelete) { + final String title = isSpace ? 'Delete Space' : 'Delete Community'; + showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16.0)), child: SizedBox( width: 500, child: Container( @@ -123,68 +71,19 @@ void showProcessingPopup(BuildContext context, bool isSpace, VoidCallback onDele child: Column( mainAxisSize: MainAxisSize.min, children: [ - // Icon - Container( - width: 50, - height: 50, - decoration: BoxDecoration( - color: ColorsManager.warningRed, - shape: BoxShape.circle, - ), - child: const Icon( - Icons.close, - color: Colors.white, - size: 40, - ), - ), + _buildWarningIcon(), const SizedBox(height: 20), - // Title - isSpace - ? const Text( - 'Delete Space', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ) - : const Text( - 'Delete Community', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - ), - ), + _buildDialogTitle(context,title), const SizedBox(height: 10), - // Subtitle - const Text( - 'Are you sure you want to Delete?', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - color: Colors.grey, - ), - ), + _buildDialogSubtitle(context, 'Are you sure you want to delete?'), const SizedBox(height: 20), - // Buttons (Optional) Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( - onPressed: () { - // Trigger the second popup - onDelete(); - }, - style: ElevatedButton.styleFrom( - backgroundColor: ColorsManager.warningRed, - fixedSize: Size(140, 40), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - ), - child: const Text( - 'Delete', - style: TextStyle(color: Colors.white), - ), + onPressed: onDelete, + style: _dialogButtonStyle(ColorsManager.warningRed), + child: const Text('Delete', style: TextStyle(color: Colors.white)), ), CancelButton( label: 'Cancel', @@ -200,3 +99,40 @@ void showProcessingPopup(BuildContext context, bool isSpace, VoidCallback onDele }, ); } + +Widget _buildWarningIcon() { + return Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: ColorsManager.warningRed, + shape: BoxShape.circle, + ), + child: const Icon(Icons.close, color: Colors.white, size: 40), + ); +} + +Widget _buildDialogTitle(BuildContext context, String title) { + return Text( + title, + style: Theme.of(context).textTheme.headlineMedium, + ); +} + +Widget _buildDialogSubtitle(BuildContext context, String subtitle) { + return Text( + subtitle, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: ColorsManager.grayColor + ), + ); +} + +ButtonStyle _dialogButtonStyle(Color color) { + return ElevatedButton.styleFrom( + backgroundColor: color, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)), + fixedSize: const Size(140, 40), + ); +} From cb0abe751ea82a254511def94dc45e867d604c6c Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sat, 23 Nov 2024 18:55:32 +0400 Subject: [PATCH 091/122] separated header --- .../community_stricture_header_widget.dart | 135 +++++++++++ .../widgets/community_structure_widget.dart | 226 +++++------------- .../widgets/dialogs/delete_dialogue.dart | 8 +- 3 files changed, 196 insertions(+), 173 deletions(-) create mode 100644 lib/pages/spaces_management/widgets/community_stricture_header_widget.dart diff --git a/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart b/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart new file mode 100644 index 00000000..90376bb4 --- /dev/null +++ b/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart @@ -0,0 +1,135 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class CommunityStructureHeader extends StatelessWidget { + final String? communityName; + final bool isEditingName; + final TextEditingController nameController; + final VoidCallback onSave; + final VoidCallback onDelete; + final VoidCallback onEditName; + final ValueChanged onNameSubmitted; + + const CommunityStructureHeader({ + Key? key, + required this.communityName, + required this.isEditingName, + required this.nameController, + required this.onSave, + required this.onDelete, + required this.onEditName, + required this.onNameSubmitted, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), + width: double.infinity, + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + boxShadow: [ + BoxShadow( + color: ColorsManager.shadowBlackColor, + spreadRadius: 0, + blurRadius: 8, + offset: const Offset(0, 4), + ), + ], + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Community Structure', + style: Theme.of(context) + .textTheme + .headlineLarge + ?.copyWith(color: ColorsManager.blackColor), + ), + if (communityName != null) + Row( + children: [ + if (!isEditingName) + Text( + communityName!, + style: Theme.of(context) + .textTheme + .bodyLarge + ?.copyWith(color: ColorsManager.blackColor), + ), + if (isEditingName) + SizedBox( + width: 200, + child: TextField( + controller: nameController, + decoration: const InputDecoration( + border: InputBorder.none, + isDense: true, + ), + style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), + onSubmitted: onNameSubmitted, + ), + ), + const SizedBox(width: 8), + GestureDetector( + onTap: onEditName, + child: SvgPicture.asset( + Assets.iconEdit, + width: 16, + height: 16, + ), + ), + ], + ), + ], + ), + Row( + children: [ + ElevatedButton.icon( + onPressed: onSave, + icon: const Icon(Icons.save, size: 18, color: ColorsManager.spaceColor), + label: const Text("Save"), + style: ElevatedButton.styleFrom( + backgroundColor: ColorsManager.textFieldGreyColor, + foregroundColor: ColorsManager.blackColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + side: BorderSide(color: Colors.grey.shade300), + elevation: 0, + ), + ), + const SizedBox(width: 10), + ElevatedButton.icon( + onPressed: onDelete, + icon: SvgPicture.asset( + Assets.delete, + width: 18, + height: 18, + ), + label: const Text("Delete"), + style: ElevatedButton.styleFrom( + backgroundColor: ColorsManager.textFieldGreyColor, + foregroundColor: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + side: BorderSide(color: ColorsManager.neutralGray), + elevation: 0, + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 6de9e06c..f93d66c6 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -1,18 +1,22 @@ +// Flutter imports import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/svg.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +// Syncrow project imports import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; -import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/create_space_dialog.dart'; -import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart'; -import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/delete_dialogue.dart'; -import 'package:syncrow_web/pages/spaces_management/widgets/space_card_widget.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/community_stricture_header_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/create_space_dialog.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/delete_dialogue.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/space_card_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/space_container_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -106,30 +110,55 @@ class _CommunityStructureAreaState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildHeader(), + CommunityStructureHeader( + communityName: widget.selectedCommunity?.name, + isEditingName: isEditingName, + nameController: _nameController, + onSave: _saveSpaces, + onDelete: _onDelete, + onEditName: () { + setState(() { + isEditingName = !isEditingName; + if (isEditingName) { + _nameController.text = widget.selectedCommunity?.name ?? ''; + } + }); + }, + onNameSubmitted: (value) { + context.read().add( + UpdateCommunityEvent( + communityUuid: widget.selectedCommunity!.uuid, + name: value, + ), + ); + setState(() { + widget.selectedCommunity?.name = value; + isEditingName = false; + }); + }, + ), Flexible( child: Stack( children: [ - InteractiveViewer( - transformationController: _transformationController, - boundaryMargin: EdgeInsets.all(500), - minScale: 0.5, - maxScale: 3.0, - constrained: false, - child: Container( - width: canvasWidth, - height: canvasHeight, - child: Stack( - children: [ - for (var connection in connections) - Opacity( - opacity: _isHighlightedConnection(connection) - ? 1.0 - : 0.3, // Adjust opacity - child: CustomPaint(painter: CurvedLinePainter([connection])), - ), - for (var entry in spaces.asMap().entries) - if(entry.value.status != SpaceStatus.deleted) + InteractiveViewer( + transformationController: _transformationController, + boundaryMargin: EdgeInsets.all(500), + minScale: 0.5, + maxScale: 3.0, + constrained: false, + child: Container( + width: canvasWidth, + height: canvasHeight, + child: Stack( + children: [ + for (var connection in connections) + Opacity( + opacity: + _isHighlightedConnection(connection) ? 1.0 : 0.3, // Adjust opacity + child: CustomPaint(painter: CurvedLinePainter([connection])), + ), + for (var entry in spaces.asMap().entries) + if (entry.value.status != SpaceStatus.deleted) Positioned( left: entry.value.position.dx, top: entry.value.position.dy, @@ -151,8 +180,7 @@ class _CommunityStructureAreaState extends State { _updateNodePosition(entry.value, newPosition); }, buildSpaceContainer: (int index) { - final bool isHighlighted = - _isHighlightedSpace(spaces[index]); + final bool isHighlighted = _isHighlightedSpace(spaces[index]); return Opacity( opacity: isHighlighted ? 1.0 : 0.3, @@ -170,10 +198,10 @@ class _CommunityStructureAreaState extends State { }, ), ), - ], - ), + ], ), ), + ), if (spaces.isEmpty) Center( child: AddSpaceButton( @@ -190,144 +218,6 @@ class _CommunityStructureAreaState extends State { )); } - Widget _buildHeader() { - return Container( - padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), - width: double.infinity, - decoration: BoxDecoration( - color: ColorsManager.whiteColors, - boxShadow: [ - BoxShadow( - color: ColorsManager.shadowBlackColor, - spreadRadius: 0, - blurRadius: 8, - offset: const Offset(0, 4), - ), - ], - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Community Structure', - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - ), - if (widget.selectedCommunity != null) - Row( - children: [ - // Show Text widget when not editing - if (!isEditingName) - Text( - widget.selectedCommunity?.name ?? '', - style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), - ), - if (isEditingName) // Show TextField when editing - SizedBox( - width: 200, // Adjusted width to make TextField visible - child: TextField( - controller: _nameController, - decoration: const InputDecoration( - border: InputBorder.none, - isDense: true, // Reduce the height of the TextField - ), - style: const TextStyle( - fontSize: 16, - color: ColorsManager.blackColor, - ), - onSubmitted: (value) { - context.read().add( - UpdateCommunityEvent( - communityUuid: widget.selectedCommunity!.uuid, - name: value, - ), - ); - setState(() { - widget.selectedCommunity?.name = value; // Update the name - isEditingName = false; // Exit edit mode - }); - }, - ), - ), - const SizedBox(width: 8), - if (!isEditingName) - GestureDetector( - onTap: () { - setState(() { - isEditingName = !isEditingName; // Toggle edit mode - }); - if (isEditingName) { - _nameController.text = widget.selectedCommunity?.name ?? ''; // Pre-fill - } - }, - child: SvgPicture.asset( - Assets.iconEdit, // Path to the edit icon SVG asset - width: 16, - height: 16, - ), - ), - ], - ), - ], - ), - // Show "Save" button only if there are spaces - if (spaces.isNotEmpty && widget.selectedCommunity != null) - Row(children: [ - ElevatedButton.icon( - onPressed: () { - _saveSpaces(); - }, - icon: const Icon( - Icons.save, - size: 18, - color: ColorsManager.spaceColor, - ), - label: const Text("Save"), - style: ElevatedButton.styleFrom( - backgroundColor: ColorsManager.textFieldGreyColor, - foregroundColor: ColorsManager.blackColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - side: BorderSide(color: Colors.grey.shade300), - elevation: 0, - ), - ), - const SizedBox(width: 10), - ElevatedButton.icon( - onPressed: () { - showDeleteConfirmationDialog(context, () { - // Handle the delete action here - Navigator.of(context).pop(); // Close the dialog after confirming - _onDelete(); - }, widget.selectedSpace != null); - }, - icon: SvgPicture.asset( - Assets.delete, // Path to your SVG asset - width: 18, // Adjust width as needed - height: 18, // Adjust height as needed - ), - label: const Text("Delete"), - style: ElevatedButton.styleFrom( - backgroundColor: ColorsManager.textFieldGreyColor, - foregroundColor: Colors.black, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - side: BorderSide(color: ColorsManager.neutralGray), - elevation: 0, - ), - ), - ]) - ], - ), - ); - } - void _updateNodePosition(SpaceModel node, Offset newPosition) { setState(() { node.position = newPosition; diff --git a/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart b/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart index 41646a47..8607f9e0 100644 --- a/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart +++ b/lib/pages/spaces_management/widgets/dialogs/delete_dialogue.dart @@ -24,7 +24,7 @@ void showDeleteConfirmationDialog(BuildContext context, VoidCallback onConfirm, children: [ _buildWarningIcon(), const SizedBox(height: 20), - _buildDialogTitle(context,title), + _buildDialogTitle(context, title), const SizedBox(height: 10), _buildDialogSubtitle(context, subtitle), const SizedBox(height: 20), @@ -73,7 +73,7 @@ void showProcessingPopup(BuildContext context, bool isSpace, VoidCallback onDele children: [ _buildWarningIcon(), const SizedBox(height: 20), - _buildDialogTitle(context,title), + _buildDialogTitle(context, title), const SizedBox(height: 10), _buildDialogSubtitle(context, 'Are you sure you want to delete?'), const SizedBox(height: 20), @@ -123,9 +123,7 @@ Widget _buildDialogSubtitle(BuildContext context, String subtitle) { return Text( subtitle, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: ColorsManager.grayColor - ), + style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: ColorsManager.grayColor), ); } From 0830fa1cfde843e99f3bef44a0980f266965ba09 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sat, 23 Nov 2024 20:10:00 +0400 Subject: [PATCH 092/122] added default button --- .../community_stricture_header_widget.dart | 194 ++++++++++-------- .../widgets/community_structure_widget.dart | 4 +- 2 files changed, 107 insertions(+), 91 deletions(-) diff --git a/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart b/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart index 90376bb4..c1de16c1 100644 --- a/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart +++ b/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart @@ -1,11 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class CommunityStructureHeader extends StatelessWidget { final String? communityName; final bool isEditingName; + final bool isSave; final TextEditingController nameController; final VoidCallback onSave; final VoidCallback onDelete; @@ -15,6 +17,7 @@ class CommunityStructureHeader extends StatelessWidget { const CommunityStructureHeader({ Key? key, required this.communityName, + required this.isSave, required this.isEditingName, required this.nameController, required this.onSave, @@ -25,15 +28,15 @@ class CommunityStructureHeader extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = Theme.of(context); + return Container( padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), - width: double.infinity, decoration: BoxDecoration( color: ColorsManager.whiteColors, boxShadow: [ BoxShadow( color: ColorsManager.shadowBlackColor, - spreadRadius: 0, blurRadius: 8, offset: const Offset(0, 4), ), @@ -42,94 +45,109 @@ class CommunityStructureHeader extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Community Structure', - style: Theme.of(context) - .textTheme - .headlineLarge - ?.copyWith(color: ColorsManager.blackColor), - ), - if (communityName != null) - Row( - children: [ - if (!isEditingName) - Text( - communityName!, - style: Theme.of(context) - .textTheme - .bodyLarge - ?.copyWith(color: ColorsManager.blackColor), - ), - if (isEditingName) - SizedBox( - width: 200, - child: TextField( - controller: nameController, - decoration: const InputDecoration( - border: InputBorder.none, - isDense: true, - ), - style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), - onSubmitted: onNameSubmitted, - ), - ), - const SizedBox(width: 8), - GestureDetector( - onTap: onEditName, - child: SvgPicture.asset( - Assets.iconEdit, - width: 16, - height: 16, - ), - ), - ], - ), - ], - ), - Row( - children: [ - ElevatedButton.icon( - onPressed: onSave, - icon: const Icon(Icons.save, size: 18, color: ColorsManager.spaceColor), - label: const Text("Save"), - style: ElevatedButton.styleFrom( - backgroundColor: ColorsManager.textFieldGreyColor, - foregroundColor: ColorsManager.blackColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - side: BorderSide(color: Colors.grey.shade300), - elevation: 0, - ), - ), - const SizedBox(width: 10), - ElevatedButton.icon( - onPressed: onDelete, - icon: SvgPicture.asset( - Assets.delete, - width: 18, - height: 18, - ), - label: const Text("Delete"), - style: ElevatedButton.styleFrom( - backgroundColor: ColorsManager.textFieldGreyColor, - foregroundColor: Colors.black, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - side: BorderSide(color: ColorsManager.neutralGray), - elevation: 0, - ), - ), - ], - ), + _buildCommunityInfo(theme), + _buildActionButtons(), ], ), ); } + + Widget _buildCommunityInfo(ThemeData theme) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Community Structure', + style: theme.textTheme.headlineLarge?.copyWith(color: ColorsManager.blackColor), + ), + if (communityName != null) _buildCommunityName(), + ], + ); + } + + Widget _buildCommunityName() { + return Row( + children: [ + if (!isEditingName) + Text( + communityName!, + style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), + ), + if (isEditingName) + SizedBox( + width: 200, + child: TextField( + controller: nameController, + decoration: const InputDecoration( + border: InputBorder.none, + isDense: true, + ), + style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), + onSubmitted: onNameSubmitted, + ), + ), + const SizedBox(width: 8), + GestureDetector( + onTap: onEditName, + child: SvgPicture.asset( + Assets.iconEdit, + width: 16, + height: 16, + ), + ), + ], + ); + } + + Widget _buildActionButtons() { + return Row( + children: [ + if (isSave) + _buildButton( + label: "Save", + icon: const Icon(Icons.save, size: 18, color: ColorsManager.spaceColor), + onPressed: onSave, + ), + if (isSave) const SizedBox(width: 10), + if (communityName != null && communityName != '') + _buildButton( + label: "Delete", + icon: SvgPicture.asset( + Assets.delete, + width: 18, + height: 18, + ), + onPressed: onDelete, + ), + ], + ); + } + + Widget _buildButton({ + required String label, + required Widget icon, + required VoidCallback onPressed, + }) { + return SizedBox( + width: 100, + height: 30, + child: DefaultButton( + onPressed: onPressed, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + icon, + const SizedBox(width: 8), + Text(label), + ], + ), + backgroundColor: ColorsManager.textFieldGreyColor, + foregroundColor: ColorsManager.blackColor, + borderRadius: 8.0, + padding: 2.0, + elevation: 0, + borderColor: Colors.grey.shade300, + ), + ); + } } diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index f93d66c6..71b2ed27 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -1,7 +1,6 @@ // Flutter imports import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_svg/flutter_svg.dart'; // Syncrow project imports import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; @@ -14,12 +13,10 @@ import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/community_stricture_header_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/create_space_dialog.dart'; -import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/delete_dialogue.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/space_card_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/space_container_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/constants/assets.dart'; class CommunityStructureArea extends StatefulWidget { final CommunityModel? selectedCommunity; @@ -112,6 +109,7 @@ class _CommunityStructureAreaState extends State { children: [ CommunityStructureHeader( communityName: widget.selectedCommunity?.name, + isSave: widget.spaces.isNotEmpty, isEditingName: isEditingName, nameController: _nameController, onSave: _saveSpaces, From 42a7ee22b2c4fdab77eb8ed4b232aea0f10e9e84 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sat, 23 Nov 2024 20:19:45 +0400 Subject: [PATCH 093/122] changed Text style to use theme --- .../widgets/community_structure_widget.dart | 2 +- .../widgets/space_container_widget.dart | 105 ++++++++++-------- 2 files changed, 61 insertions(+), 46 deletions(-) diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 71b2ed27..32639efb 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -109,7 +109,7 @@ class _CommunityStructureAreaState extends State { children: [ CommunityStructureHeader( communityName: widget.selectedCommunity?.name, - isSave: widget.spaces.isNotEmpty, + isSave: spaces.isNotEmpty, isEditingName: isEditingName, nameController: _nameController, onSave: _saveSpaces, diff --git a/lib/pages/spaces_management/widgets/space_container_widget.dart b/lib/pages/spaces_management/widgets/space_container_widget.dart index 273319ce..78af5f10 100644 --- a/lib/pages/spaces_management/widgets/space_container_widget.dart +++ b/lib/pages/spaces_management/widgets/space_container_widget.dart @@ -20,55 +20,70 @@ class SpaceContainerWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = Theme.of(context); + return GestureDetector( - onDoubleTap: onDoubleTap, - onTap: onTap, - child: Container( - width: 150, - height: 60, - decoration: BoxDecoration( - color: ColorsManager.whiteColors, - borderRadius: BorderRadius.circular(15), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: const Offset(0, 3), // shadow position - ), - ], - ), - child: Row( - children: [ - Container( - width: 40, - height: 60, - decoration: const BoxDecoration( - color: ColorsManager.spaceColor, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(15), - bottomLeft: Radius.circular(15), - ), - ), - child: Center( - child: SvgPicture.asset( - icon, - color: ColorsManager.whiteColors, - width: 24, - height: 24, - ), - ), - ), - const SizedBox(width: 10), - Text( + onDoubleTap: onDoubleTap, + onTap: onTap, + child: Container( + width: 150, + height: 60, + decoration: _containerDecoration(), + child: Row( + children: [ + _buildIconContainer(), + const SizedBox(width: 10), + Expanded( + child: Text( name, - style: const TextStyle( + style: theme.textTheme.bodyLarge?.copyWith( fontWeight: FontWeight.bold, - fontSize: 16, + color: ColorsManager.blackColor, ), + overflow: TextOverflow.ellipsis, // Handle long names gracefully ), - ], - ), - )); + ), + ], + ), + ), + ); + } + + /// Builds the icon container with the SVG asset. + Widget _buildIconContainer() { + return Container( + width: 40, + height: double.infinity, + decoration: const BoxDecoration( + color: ColorsManager.spaceColor, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(15), + bottomLeft: Radius.circular(15), + ), + ), + child: Center( + child: SvgPicture.asset( + icon, + color: ColorsManager.whiteColors, + width: 24, + height: 24, + ), + ), + ); + } + + BoxDecoration _containerDecoration() { + return BoxDecoration( + color: ColorsManager.whiteColors, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: const Offset(0, 3), // Shadow position + ), + ], + ); } } From 797133e16e58ca314a7fef6a6c645009b3fc4088 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sat, 23 Nov 2024 20:24:15 +0400 Subject: [PATCH 094/122] updated add device type --- .../widgets/add_device_type_widget.dart | 201 ++++++++++-------- 1 file changed, 107 insertions(+), 94 deletions(-) diff --git a/lib/pages/spaces_management/widgets/add_device_type_widget.dart b/lib/pages/spaces_management/widgets/add_device_type_widget.dart index d069b391..2c88b866 100644 --- a/lib/pages/spaces_management/widgets/add_device_type_widget.dart +++ b/lib/pages/spaces_management/widgets/add_device_type_widget.dart @@ -13,24 +13,27 @@ class AddDeviceWidget extends StatefulWidget { final ValueChanged>? onProductsSelected; final List? initialSelectedProducts; - const AddDeviceWidget( - {super.key, this.products, this.initialSelectedProducts, this.onProductsSelected}); + const AddDeviceWidget({ + super.key, + this.products, + this.initialSelectedProducts, + this.onProductsSelected, + }); @override _AddDeviceWidgetState createState() => _AddDeviceWidgetState(); } class _AddDeviceWidgetState extends State { - late ScrollController _scrollController; - List productCounts = []; + late final ScrollController _scrollController; + late List productCounts; @override void initState() { super.initState(); _scrollController = ScrollController(); - if (widget.initialSelectedProducts != null && widget.initialSelectedProducts!.isNotEmpty) { - productCounts = List.from(widget.initialSelectedProducts!); - } + productCounts = + widget.initialSelectedProducts != null ? List.from(widget.initialSelectedProducts!) : []; } @override @@ -41,86 +44,72 @@ class _AddDeviceWidgetState extends State { @override Widget build(BuildContext context) { - Size size = MediaQuery.of(context).size; + final size = MediaQuery.of(context).size; return AlertDialog( title: const Text('Add Devices'), backgroundColor: ColorsManager.whiteColors, content: Container( width: size.width * 0.65, - height: size.height * 0.57, // Set width for the dialog + height: size.height * 0.57, color: ColorsManager.textFieldGreyColor, child: Column( children: [ - const SizedBox(height: 16.0), + const SizedBox(height: 16), Expanded( child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), // Add horizontal padding - child: Scrollbar( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Scrollbar( + controller: _scrollController, + thumbVisibility: false, + child: GridView.builder( controller: _scrollController, - thumbVisibility: false, - child: GridView.builder( - controller: _scrollController, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 6, // Display 6 items in a row - mainAxisSpacing: 10, - crossAxisSpacing: 10, - childAspectRatio: 0.7, // Adjust the aspect ratio - ), - itemCount: widget.products?.length ?? 0, - itemBuilder: (context, index) { - final deviceType = widget.products![index]; - return _buildDeviceTypeTile(deviceType); - }, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 6, + mainAxisSpacing: 10, + crossAxisSpacing: 10, + childAspectRatio: 0.7, ), - )), + itemCount: widget.products?.length ?? 0, + itemBuilder: (context, index) { + final product = widget.products![index]; + return _buildDeviceTypeTile(product); + }, + ), + ), + ), ), ], ), ), actions: [ Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, // Align cancel to the left and continue to the right + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - SizedBox( - width: 200, // Define a specific width for the button - child: DefaultButton( - onPressed: () { - Navigator.of(context).pop(); - }, - backgroundColor: ColorsManager.boxColor, - foregroundColor: ColorsManager.blackColor, - child: const Text('Cancel'), - ), - ), - SizedBox( - width: 200, // Define a specific width for the button - child: DefaultButton( - onPressed: () { - Navigator.of(context).pop(); - }, - backgroundColor: ColorsManager.secondaryColor, - foregroundColor: Colors.white, - child: const Text('Continue'), - ), - ), + _buildActionButton('Cancel', ColorsManager.boxColor, ColorsManager.blackColor, () { + Navigator.of(context).pop(); + }), + _buildActionButton('Continue', ColorsManager.secondaryColor, Colors.white, () { + Navigator.of(context).pop(); + if (widget.onProductsSelected != null) { + widget.onProductsSelected!(productCounts); + } + }), ], ), ], ); } - Widget _buildDeviceTypeTile(ProductModel? deviceType) { - - SelectedProduct? existingProduct = productCounts.firstWhere( - (product) => product.productId == deviceType?.uuid, - orElse: () => SelectedProduct(productId: deviceType!.uuid, count: 0), + Widget _buildDeviceTypeTile(ProductModel product) { + final selectedProduct = productCounts.firstWhere( + (p) => p.productId == product.uuid, + orElse: () => SelectedProduct(productId: product.uuid, count: 0), ); return SizedBox( width: 75, - height: 90, // Increase height if needed + height: 90, child: Card( elevation: 2, color: ColorsManager.whiteColors, @@ -132,53 +121,25 @@ class _AddDeviceWidgetState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - // Fixed height container for the icon - Container( - height: 80, - width: 80, - decoration: BoxDecoration( - shape: BoxShape.circle, // Make it circular - color: ColorsManager.textFieldGreyColor, // Background color of the circle - border: Border.all( - color: ColorsManager.neutralGray, // Border color - width: 2, // Border width - ), - ), // Fixed height for the icon - child: Center( - child: SvgPicture.asset( - deviceType?.icon ?? Assets.sensors, - width: 45, - height: 45, - ), - ), - ), + _buildDeviceIcon(product), const SizedBox(height: 8), - // Fixed height container for the name - SizedBox( - height: 35, // Fixed height for the text (adjust as needed) - child: Text( - deviceType?.name ?? '', - style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor), - textAlign: TextAlign.center, - maxLines: 2, // Allow up to 2 lines for long names - overflow: TextOverflow.ellipsis, // Handle overflow - ), - ), + _buildDeviceName(product), const SizedBox(height: 8), - // The custom counter widget aligned at the bottom CounterWidget( - initialCount: existingProduct.count, + initialCount: selectedProduct.count, onCountChanged: (newCount) { setState(() { if (newCount > 0) { - if (!productCounts.contains(existingProduct)) { - productCounts.add(SelectedProduct(productId: deviceType!.uuid, count: newCount)); + if (!productCounts.contains(selectedProduct)) { + productCounts + .add(SelectedProduct(productId: product.uuid, count: newCount)); } else { - existingProduct.count = newCount; + selectedProduct.count = newCount; } } else { - productCounts.remove(deviceType!.uuid); + productCounts.removeWhere((p) => p.productId == product.uuid); } + if (widget.onProductsSelected != null) { widget.onProductsSelected!(productCounts); } @@ -191,4 +152,56 @@ class _AddDeviceWidgetState extends State { ), ); } + + Widget _buildDeviceIcon(ProductModel product) { + return Container( + height: 80, + width: 80, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: ColorsManager.textFieldGreyColor, + border: Border.all( + color: ColorsManager.neutralGray, + width: 2, + ), + ), + child: Center( + child: SvgPicture.asset( + product.icon ?? Assets.sensors, + width: 45, + height: 45, + ), + ), + ); + } + + Widget _buildDeviceName(ProductModel product) { + return SizedBox( + height: 35, + child: Text( + product.name ?? '', + style: context.textTheme.bodySmall?.copyWith(color: ColorsManager.blackColor), + textAlign: TextAlign.center, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ); + } + + Widget _buildActionButton( + String label, + Color backgroundColor, + Color foregroundColor, + VoidCallback onPressed, + ) { + return SizedBox( + width: 200, + child: DefaultButton( + onPressed: onPressed, + backgroundColor: backgroundColor, + foregroundColor: foregroundColor, + child: Text(label), + ), + ); + } } From 77d73270b0108a126c915dd9b9336e0fb62cbc84 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sat, 23 Nov 2024 20:28:50 +0400 Subject: [PATCH 095/122] changed text style --- .../widgets/counter_widget.dart | 80 +++++++++---------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/lib/pages/spaces_management/widgets/counter_widget.dart b/lib/pages/spaces_management/widgets/counter_widget.dart index fa5f81a5..66935b12 100644 --- a/lib/pages/spaces_management/widgets/counter_widget.dart +++ b/lib/pages/spaces_management/widgets/counter_widget.dart @@ -12,72 +12,68 @@ class CounterWidget extends StatefulWidget { }) : super(key: key); @override - _CounterWidgetState createState() => _CounterWidgetState(); + State createState() => _CounterWidgetState(); } class _CounterWidgetState extends State { - late int _counter = 0; + late int _counter; - @override + @override void initState() { super.initState(); - _counter = widget.initialCount; + _counter = widget.initialCount; + } + + void _incrementCounter() { + setState(() { + _counter++; + widget.onCountChanged(_counter); + }); + } + + void _decrementCounter() { + setState(() { + if (_counter > 0) { + _counter--; + widget.onCountChanged(_counter); + } + }); } @override Widget build(BuildContext context) { + final theme = Theme.of(context); + return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), decoration: BoxDecoration( - color: ColorsManager.counterBackgroundColor, // Background color for the counter - borderRadius: BorderRadius.circular(20), // Rounded corners + color: ColorsManager.counterBackgroundColor, + borderRadius: BorderRadius.circular(20), ), child: Row( mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, children: [ - // Decrement button - GestureDetector( - onTap: () { - setState(() { - if (_counter > 0) { - _counter--; - widget.onCountChanged(_counter); - } - }); - }, - child: const Icon( - Icons.remove, - color: ColorsManager.spaceColor, // Blue color - size: 18, // Icon size - ), - ), + _buildCounterButton(Icons.remove, _decrementCounter), const SizedBox(width: 8), - // Counter value display Text( '$_counter', - style: const TextStyle( - color: ColorsManager.spaceColor, // Blue color - fontSize: 16, - ), + style: theme.textTheme.bodyLarge?.copyWith(color: ColorsManager.spaceColor), ), const SizedBox(width: 8), - // Increment button - GestureDetector( - onTap: () { - setState(() { - _counter++; - widget.onCountChanged(_counter); - }); - }, - child: const Icon( - Icons.add, - color: ColorsManager.spaceColor, // Blue color - size: 18, // Icon size - ), - ), + _buildCounterButton(Icons.add, _incrementCounter), ], ), ); } + + Widget _buildCounterButton(IconData icon, VoidCallback onPressed) { + return GestureDetector( + onTap: onPressed, + child: Icon( + icon, + color: ColorsManager.spaceColor, + size: 18, + ), + ); + } } From fb6a49354ee1a50222d2e2e97f9b39acb87fcb24 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sat, 23 Nov 2024 20:35:35 +0400 Subject: [PATCH 096/122] hoverable button --- .../widgets/hoverable_button.dart | 71 +++++++++++-------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/lib/pages/spaces_management/widgets/hoverable_button.dart b/lib/pages/spaces_management/widgets/hoverable_button.dart index bc39f87e..0c158c12 100644 --- a/lib/pages/spaces_management/widgets/hoverable_button.dart +++ b/lib/pages/spaces_management/widgets/hoverable_button.dart @@ -15,63 +15,74 @@ class HoverableButton extends StatefulWidget { }) : super(key: key); @override - _HoverableButtonState createState() => _HoverableButtonState(); + State createState() => _HoverableButtonState(); } class _HoverableButtonState extends State { - bool isHovered = false; // Track hover state + bool isHovered = false; @override Widget build(BuildContext context) { + final theme = Theme.of(context); + return GestureDetector( onTap: widget.onTap, child: MouseRegion( - onEnter: (_) => setState(() => isHovered = true), - onExit: (_) => setState(() => isHovered = false), + onEnter: (_) => _updateHoverState(true), + onExit: (_) => _updateHoverState(false), child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 8), decoration: BoxDecoration( - color: isHovered ? ColorsManager.warningRed : Colors.white, // Change color on hover + color: isHovered ? ColorsManager.warningRed : Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ if (isHovered) BoxShadow( - color: ColorsManager.warningRed, + color: ColorsManager.warningRed.withOpacity(0.4), + blurRadius: 8, + offset: const Offset(0, 4), ), ], ), child: Row( mainAxisSize: MainAxisSize.min, children: [ - if (!isHovered) - SvgPicture.asset( - widget.iconPath, - width: 24, - height: 24, - ) - else - Center( - child: const Icon( - Icons.close, // Display "X" on hover - color: Colors.white, - size: 24, - )), - const SizedBox(width: 8), - if (!isHovered) ...[ - Text( - widget.text, - style: TextStyle( - fontSize: 17, - fontWeight: FontWeight.w500, - color: ColorsManager.spaceColor, - ), - ), - ], + _buildIcon(), + if (!isHovered) const SizedBox(width: 8), + if (!isHovered) _buildText(theme), ], ), ), ), ); } + + Widget _buildIcon() { + return isHovered + ? const Icon( + Icons.close, + color: Colors.white, + size: 24, + ) + : SvgPicture.asset( + widget.iconPath, + width: 24, + height: 24, + ); + } + + Widget _buildText(ThemeData theme) { + return Text( + widget.text, + style: theme.textTheme.bodyLarge?.copyWith( + color: ColorsManager.spaceColor, + fontWeight: FontWeight.w500, + ), + ); + } + + void _updateHoverState(bool hover) { + setState(() => isHovered = hover); + } } From ca58d44c02c85ca389c259e01a6e4473e06a6ce6 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sat, 23 Nov 2024 20:53:40 +0400 Subject: [PATCH 097/122] updated theme --- .../widgets/dialogs/create_space_dialog.dart | 52 +++++-------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart index 54218d22..2b1f5ff2 100644 --- a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart @@ -8,6 +8,7 @@ import 'package:syncrow_web/pages/spaces_management/widgets/add_device_type_widg import 'package:syncrow_web/pages/spaces_management/widgets/hoverable_button.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/constants/space_icon_const.dart'; class CreateSpaceDialog extends StatefulWidget { final Function(String, String, List selectedProducts) onCreateSpace; @@ -155,11 +156,10 @@ class CreateSpaceDialogState extends State { ), ), const SizedBox(height: 16), - // Add Devices or Space Model Button if (selectedProducts.isNotEmpty) _buildSelectedProductsButtons(widget.products ?? []) else - ElevatedButton( + DefaultButton( onPressed: () { showDialog( context: context, @@ -173,18 +173,11 @@ class CreateSpaceDialogState extends State { ), ); }, - style: ElevatedButton.styleFrom( - backgroundColor: ColorsManager.textFieldGreyColor, - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), - shape: RoundedRectangleBorder( - side: const BorderSide( - // Add border side here - color: - ColorsManager.neutralGray, // Define your desired border color - width: 2.0, // Define border width - ), - borderRadius: BorderRadius.circular(20)), - ), + backgroundColor: ColorsManager.textFieldGreyColor, + foregroundColor: Colors.black, // Set the desired text/icon color + borderColor: ColorsManager.neutralGray, // Define border color + borderRadius: 20.0, + padding: 20.0, // Add padding child: Row( mainAxisSize: MainAxisSize.min, // Adjust the button size to fit the content @@ -208,7 +201,7 @@ class CreateSpaceDialogState extends State { const SizedBox(width: 8), ], ), - ), + ) ], ), ), @@ -270,17 +263,17 @@ class CreateSpaceDialogState extends State { crossAxisSpacing: 10, mainAxisSpacing: 22, ), - itemCount: _iconList.length, + itemCount: spaceIconList.length, itemBuilder: (BuildContext context, int index) { return GestureDetector( onTap: () { setState(() { - selectedIcon = _iconList[index]; + selectedIcon = spaceIconList[index]; }); Navigator.of(context).pop(); }, child: SvgPicture.asset( - _iconList[index], + spaceIconList[index], width: 50, height: 50, ), @@ -306,10 +299,9 @@ class CreateSpaceDialogState extends State { ), ), child: Wrap( - spacing: 8, // Horizontal spacing between buttons - runSpacing: 8, // Vertical spacing between rows + spacing: 8, + runSpacing: 8, children: [ - // Dynamically create a button for each selected product for (var product in selectedProducts) HoverableButton( iconPath: _mapIconToProduct(product.productId, products), @@ -369,24 +361,6 @@ class CreateSpaceDialogState extends State { ), ); - return product.icon ?? Assets.presenceSensor; } - - final List _iconList = [ - Assets.location, - Assets.villa, - Assets.gym, - Assets.sauna, - Assets.bbq, - Assets.building, - Assets.desk, - Assets.door, - Assets.parking, - Assets.pool, - Assets.stair, - Assets.steamRoom, - Assets.street, - Assets.unit, - ]; } From 59d9a34c4e13f690d6d1d2fb6efa8013939db34a Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sat, 23 Nov 2024 20:53:48 +0400 Subject: [PATCH 098/122] updated theme --- .../widgets/dialogs/create_space_dialog.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart index 2b1f5ff2..4571c0c3 100644 --- a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart @@ -299,8 +299,8 @@ class CreateSpaceDialogState extends State { ), ), child: Wrap( - spacing: 8, - runSpacing: 8, + spacing: 8, + runSpacing: 8, children: [ for (var product in selectedProducts) HoverableButton( From 7806d3de7ddf006fbb54e6e477c6d0310dac08f8 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sat, 23 Nov 2024 23:02:25 +0400 Subject: [PATCH 099/122] add blank space --- .../widgets/blank_community_widget.dart | 73 +++++++++++++++++++ .../widgets/community_structure_widget.dart | 5 ++ .../widgets/loaded_space_widget.dart | 4 +- lib/utils/constants/space_icon_const.dart | 18 +++++ 4 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 lib/pages/spaces_management/widgets/blank_community_widget.dart create mode 100644 lib/utils/constants/space_icon_const.dart diff --git a/lib/pages/spaces_management/widgets/blank_community_widget.dart b/lib/pages/spaces_management/widgets/blank_community_widget.dart new file mode 100644 index 00000000..d9048de8 --- /dev/null +++ b/lib/pages/spaces_management/widgets/blank_community_widget.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/create_community_dialog.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class BlankCommunityWidget extends StatelessWidget { + const BlankCommunityWidget({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Expanded( + child: Container( + color: ColorsManager.whiteColors, // Parent container with white background + child: GridView.builder( + padding: const EdgeInsets.only(left: 40.0, top: 20.0), + gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 400, // Each item's width will be 400 or less + mainAxisSpacing: 10, // Spacing between items + crossAxisSpacing: 10, // Spacing between items + childAspectRatio: 2.0, // Aspect ratio for width:height (e.g., 300:150 = 2.0) + ), + itemCount: 1, // Only one item + itemBuilder: (context, index) { + return GestureDetector( + onTap: () => _showCreateCommunityDialog(context), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, // Center align the content + children: [ + Container( + width: 400, // Explicitly set item width + height: 150, // Item height + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + side: BorderSide( + width: 4, + strokeAlign: BorderSide.strokeAlignOutside, + color: ColorsManager.borderColor, + ), + borderRadius: BorderRadius.circular(5), + ), + ), + ), + const SizedBox(height: 9), // Space between item and text + Text('Blank', // Text below the item + style: Theme.of(context).textTheme.bodyLarge?.copyWith( + color: ColorsManager.blackColor, + )), + ], + )); + }, + ), + ), + ); + } + + void _showCreateCommunityDialog(BuildContext parentContext) { + showDialog( + context: parentContext, + builder: (context) => CreateCommunityDialog( + onCreateCommunity: (String communityName, String description) { + parentContext.read().add( + CreateCommunityEvent( + name: communityName, + description: description, + ), + ); + }, + ), + ); + } +} diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 32639efb..b23bb040 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -11,6 +11,7 @@ import 'package:syncrow_web/pages/spaces_management/model/selected_product_model import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/blank_community_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/community_stricture_header_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart'; @@ -92,6 +93,10 @@ class _CommunityStructureAreaState extends State { @override Widget build(BuildContext context) { + if (widget.selectedCommunity == null) { + return BlankCommunityWidget(); + } + Size screenSize = MediaQuery.of(context).size; return Expanded( child: GestureDetector( diff --git a/lib/pages/spaces_management/widgets/loaded_space_widget.dart b/lib/pages/spaces_management/widgets/loaded_space_widget.dart index 3003b855..aa48d323 100644 --- a/lib/pages/spaces_management/widgets/loaded_space_widget.dart +++ b/lib/pages/spaces_management/widgets/loaded_space_widget.dart @@ -15,14 +15,14 @@ class LoadedSpaceView extends StatefulWidget { final List? products; const LoadedSpaceView({ - Key? key, + super.key, required this.communities, this.selectedCommunity, this.selectedSpace, required this.onCommunitySelected, required this.onSpaceSelected, this.products, - }) : super(key: key); + }); @override _LoadedStateViewState createState() => _LoadedStateViewState(); diff --git a/lib/utils/constants/space_icon_const.dart b/lib/utils/constants/space_icon_const.dart new file mode 100644 index 00000000..5d141a53 --- /dev/null +++ b/lib/utils/constants/space_icon_const.dart @@ -0,0 +1,18 @@ +import 'package:syncrow_web/utils/constants/assets.dart'; + +const List spaceIconList = [ + Assets.location, + Assets.villa, + Assets.gym, + Assets.sauna, + Assets.bbq, + Assets.building, + Assets.desk, + Assets.door, + Assets.parking, + Assets.pool, + Assets.stair, + Assets.steamRoom, + Assets.street, + Assets.unit, +]; From 5ac7eb03018038271b635430fb3c728bc93370fa Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Sun, 24 Nov 2024 02:28:28 +0400 Subject: [PATCH 100/122] updated blank structure --- .../bloc/space_management_bloc.dart | 24 +++++++++++++++---- .../bloc/space_management_state.dart | 6 +++-- .../view/spaces_management_page.dart | 2 ++ .../community_stricture_header_widget.dart | 5 +++- lib/utils/color_manager.dart | 1 + 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 913c4c92..74f76db2 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -27,10 +27,25 @@ class SpaceManagementBloc extends Bloc emit, ) async { + final previousState = state; try { emit(SpaceManagementLoading()); final success = await _api.updateCommunity(event.communityUuid, event.name); if (success) { + if (previousState is SpaceManagementLoaded) { + final updatedCommunities = List.from(previousState.communities); + for(var community in updatedCommunities){ + if(community.uuid == event.communityUuid){ + community.name = event.name; + break; + } + } + emit(SpaceManagementLoaded( + communities: updatedCommunities, + products: previousState.products, + selectedCommunity: previousState.selectedCommunity, + )); + } add(LoadCommunityAndSpacesEvent()); } else { emit(const SpaceManagementError('Failed to update the community.')); @@ -125,17 +140,16 @@ class SpaceManagementBloc extends Bloc.from(previousState.communities) ..add(newCommunity); emit(SpaceManagementLoaded( - communities: updatedCommunities, products: _cachedProducts ?? [])); + communities: updatedCommunities, + products: _cachedProducts ?? [], + selectedCommunity: newCommunity)); } } else { emit(const SpaceManagementError('Error creating community')); diff --git a/lib/pages/spaces_management/bloc/space_management_state.dart b/lib/pages/spaces_management/bloc/space_management_state.dart index 0e05b42e..0264d4e8 100644 --- a/lib/pages/spaces_management/bloc/space_management_state.dart +++ b/lib/pages/spaces_management/bloc/space_management_state.dart @@ -16,9 +16,11 @@ class SpaceManagementLoading extends SpaceManagementState {} class SpaceManagementLoaded extends SpaceManagementState { final List communities; - final List products; // Include products in the state + final List products; + CommunityModel? selectedCommunity; // Include products in the state - SpaceManagementLoaded({required this.communities, required this.products}); + SpaceManagementLoaded( + {required this.communities, required this.products, this.selectedCommunity}); } class SpaceCreationSuccess extends SpaceManagementState { diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index c2c9e8de..452a1657 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -56,6 +56,8 @@ class SpaceManagementPageState extends State { ); if (selectedIndex != -1) { selectedCommunity = state.communities[selectedIndex]; + } else if (state.selectedCommunity != null) { + selectedCommunity = state.selectedCommunity; } else { selectedCommunity = null; selectedSpace = null; diff --git a/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart b/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart index c1de16c1..cdbbf195 100644 --- a/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart +++ b/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart @@ -109,7 +109,9 @@ class CommunityStructureHeader extends StatelessWidget { onPressed: onSave, ), if (isSave) const SizedBox(width: 10), - if (communityName != null && communityName != '') + /* + Commenting till finalize delete + if (communityName != null && communityName != '') _buildButton( label: "Delete", icon: SvgPicture.asset( @@ -119,6 +121,7 @@ class CommunityStructureHeader extends StatelessWidget { ), onPressed: onDelete, ), + */ ], ); } diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index 2bc3fe66..709d4ac7 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -52,6 +52,7 @@ abstract class ColorsManager { static const Color counterBackgroundColor = Color(0xCCF4F4F4); static const Color neutralGray = Color(0xFFE5E5E5); static const Color warningRed = Color(0xFFFF6465); + static const Color borderColor = Color(0xFFE5E5E5); } //background: #background: #5D5D5D; From 703e0efd2eda0a5e43600feac78c7d73b9b63478 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 26 Nov 2024 09:36:12 +0400 Subject: [PATCH 101/122] removed width from blank container --- .../widgets/blank_community_widget.dart | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/pages/spaces_management/widgets/blank_community_widget.dart b/lib/pages/spaces_management/widgets/blank_community_widget.dart index d9048de8..6d224562 100644 --- a/lib/pages/spaces_management/widgets/blank_community_widget.dart +++ b/lib/pages/spaces_management/widgets/blank_community_widget.dart @@ -28,20 +28,24 @@ class BlankCommunityWidget extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.center, // Center align the content children: [ - Container( - width: 400, // Explicitly set item width - height: 150, // Item height - decoration: ShapeDecoration( - shape: RoundedRectangleBorder( - side: BorderSide( - width: 4, - strokeAlign: BorderSide.strokeAlignOutside, - color: ColorsManager.borderColor, + Expanded( + child: AspectRatio( + aspectRatio: 2.0, + child: Container( + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + side: BorderSide( + width: 4, + strokeAlign: BorderSide.strokeAlignOutside, + color: ColorsManager.borderColor, + ), + borderRadius: BorderRadius.circular(5), + ), ), - borderRadius: BorderRadius.circular(5), ), ), ), + const SizedBox(height: 9), // Space between item and text Text('Blank', // Text below the item style: Theme.of(context).textTheme.bodyLarge?.copyWith( From 17943a5ddf66fde48ab5eb7a2a4aa72db960de49 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 26 Nov 2024 12:16:46 +0400 Subject: [PATCH 102/122] responsive create/edit space --- .../widgets/dialogs/create_space_dialog.dart | 313 +++++++++--------- 1 file changed, 154 insertions(+), 159 deletions(-) diff --git a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart index 4571c0c3..b3f0dd45 100644 --- a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart @@ -45,169 +45,162 @@ class CreateSpaceDialogState extends State { selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : []; } + @override @override Widget build(BuildContext context) { + final screenWidth = MediaQuery.of(context).size.width; + final screenHeight = MediaQuery.of(context).size.height; + return AlertDialog( - title: widget.isEdit ? Text('Edit Space') : Text('Create new Space'), + title: widget.isEdit ? const Text('Edit Space') : const Text('Create New Space'), backgroundColor: ColorsManager.whiteColors, content: SizedBox( - width: 600, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Stack( - alignment: Alignment.center, - children: [ - Container( - width: 100, - height: 100, - decoration: const BoxDecoration( - color: ColorsManager.boxColor, - shape: BoxShape.circle, - ), - ), - SvgPicture.asset( - selectedIcon, - width: 60, - height: 60, - ), - Positioned( - top: 2, - left: 2, - child: InkWell( - onTap: () => _showIconSelectionDialog(), - child: Container( - width: 20, - height: 20, - decoration: const BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - ), - child: SvgPicture.asset(Assets.iconEdit, width: 10, height: 10), - ), - ), - ), - ], - ), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + width: screenWidth * 0.5, // Limit dialog width + child: SingleChildScrollView( + // Scrollable content to prevent overflow + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + alignment: Alignment.center, children: [ - // Name input field - TextField( - controller: nameController, - onChanged: (value) { - enteredName = value; - }, - style: const TextStyle(color: Colors.black), - decoration: InputDecoration( - hintText: 'Please enter the name', - hintStyle: const TextStyle( - fontSize: 14, - color: ColorsManager.lightGrayColor, - fontWeight: FontWeight.w400), - filled: true, - fillColor: ColorsManager.boxColor, - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: ColorsManager - .boxColor, // Light gray color when enabled (not focused) - width: 1.5, + Container( + width: screenWidth * 0.1, // Adjusted width + height: screenWidth * 0.1, // Adjusted height + decoration: const BoxDecoration( + color: ColorsManager.boxColor, + shape: BoxShape.circle, + ), + ), + SvgPicture.asset( + selectedIcon, + width: screenWidth * 0.04, + height: screenWidth * 0.04, + ), + Positioned( + top: 6, + right: 6, + child: InkWell( + onTap: _showIconSelectionDialog, + child: Container( + width: screenWidth * 0.020, + height: screenWidth * 0.020, + decoration: const BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, ), - ), - // Set border when focused - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: ColorsManager.boxColor, // Primary color when focused - width: 1.5, - ), - ), - // Set border for disabled state - disabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: ColorsManager.boxColor, // Light gray for disabled state - width: 1.5, - ), - ), - // Set border for error state - errorBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: ColorsManager.boxColor, // Red border when there's an error - width: 1.5, - ), - ), - // Border for focused error state - focusedErrorBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: ColorsManager - .boxColor, // Red border when there's an error and it's focused - width: 1.5, + child: SvgPicture.asset( + Assets.iconEdit, + width: screenWidth * 0.06, + height: screenWidth * 0.06, ), ), ), ), - const SizedBox(height: 16), - if (selectedProducts.isNotEmpty) - _buildSelectedProductsButtons(widget.products ?? []) - else - DefaultButton( - onPressed: () { - showDialog( - context: context, - builder: (context) => AddDeviceWidget( - products: widget.products, - onProductsSelected: (selectedProductsMap) { - setState(() { - selectedProducts = selectedProductsMap; - }); - }, - ), - ); - }, - backgroundColor: ColorsManager.textFieldGreyColor, - foregroundColor: Colors.black, // Set the desired text/icon color - borderColor: ColorsManager.neutralGray, // Define border color - borderRadius: 20.0, - padding: 20.0, // Add padding - child: Row( - mainAxisSize: - MainAxisSize.min, // Adjust the button size to fit the content - children: [ - SvgPicture.asset( - Assets.addIcon, // Replace with your actual icon path - width: 20, // Set the size of the icon - height: 20, - ), - const SizedBox(width: 8), // Add spacing between the icon and text - const Text( - 'Add devices / Assign a space model', - style: TextStyle( - color: Colors.black, - fontSize: 16, - fontFamily: 'Aftika', - fontWeight: FontWeight.w400, - height: 1.5, // Adjust line height for better spacing - ), - ), - const SizedBox(width: 8), - ], - ), - ) ], ), - ), - ], - ), - ], + + const SizedBox(width: 16), + Expanded( + // Ensure the text field expands responsively + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextField( + controller: nameController, + onChanged: (value) { + enteredName = value; + }, + style: const TextStyle(color: Colors.black), + decoration: InputDecoration( + hintText: 'Please enter the name', + hintStyle: const TextStyle( + fontSize: 14, + color: ColorsManager.lightGrayColor, + fontWeight: FontWeight.w400, + ), + filled: true, + fillColor: ColorsManager.boxColor, + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: ColorsManager.boxColor, + width: 1.5, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: ColorsManager.boxColor, + width: 1.5, + ), + ), + ), + ), + const SizedBox(height: 16), + if (selectedProducts.isNotEmpty) + _buildSelectedProductsButtons(widget.products ?? []) + else + DefaultButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => AddDeviceWidget( + products: widget.products, + onProductsSelected: (selectedProductsMap) { + setState(() { + selectedProducts = selectedProductsMap; + }); + }, + ), + ); + }, + backgroundColor: ColorsManager.textFieldGreyColor, + foregroundColor: Colors.black, + borderColor: ColorsManager.neutralGray, + borderRadius: 16.0, + padding: 10.0, // Reduced padding for smaller size + child: Align( + alignment: Alignment.centerLeft, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.only( + left: 6.0), + child: SvgPicture.asset( + Assets.addIcon, + width: screenWidth * 0.015, // Adjust icon size + height: screenWidth * 0.015, + ), + ), + const SizedBox(width: 3), + const Flexible( + child: Text( + 'Add devices / Assign a space model', + overflow: TextOverflow.ellipsis, // Prevent overflow + style: TextStyle( + color: Colors.black, + fontSize: 14, + fontFamily: 'Aftika', + fontWeight: FontWeight.w400, + ), + ), + ), + ], + ), + )), + ], + ), + ), + ], + ), + ], + ), ), ), actions: [ @@ -224,11 +217,10 @@ class CreateSpaceDialogState extends State { Expanded( child: DefaultButton( onPressed: () { - late String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? ''); + String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? ''); if (newName.isNotEmpty) { - widget.onCreateSpace( - newName, selectedIcon, selectedProducts); // Pass the name and icon back - Navigator.of(context).pop(); // Close the dialog + widget.onCreateSpace(newName, selectedIcon, selectedProducts); + Navigator.of(context).pop(); } }, backgroundColor: ColorsManager.secondaryColor, @@ -243,6 +235,8 @@ class CreateSpaceDialogState extends State { } void _showIconSelectionDialog() { + final screenWidth = MediaQuery.of(context).size.width; + showDialog( context: context, builder: (BuildContext context) { @@ -250,8 +244,7 @@ class CreateSpaceDialogState extends State { title: const Text('Select Icon'), backgroundColor: Colors.white, content: Container( - width: 500, - height: 200, + width: screenWidth * 0.5, padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: ColorsManager.boxColor, @@ -274,8 +267,8 @@ class CreateSpaceDialogState extends State { }, child: SvgPicture.asset( spaceIconList[index], - width: 50, - height: 50, + width: screenWidth * 0.06, + height: screenWidth * 0.06, ), ); }, @@ -287,8 +280,10 @@ class CreateSpaceDialogState extends State { } Widget _buildSelectedProductsButtons(List products) { + final screenWidth = MediaQuery.of(context).size.width; + return Container( - width: 600, + width: screenWidth * 0.6, padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: ColorsManager.textFieldGreyColor, From a82505ea6af74297f915201490cbfb33214920f6 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 26 Nov 2024 12:32:36 +0400 Subject: [PATCH 103/122] updated text theme --- .../widgets/dialogs/create_space_dialog.dart | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart index b3f0dd45..40f20ece 100644 --- a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart @@ -87,7 +87,7 @@ class CreateSpaceDialogState extends State { child: InkWell( onTap: _showIconSelectionDialog, child: Container( - width: screenWidth * 0.020, + width: screenWidth * 0.020, height: screenWidth * 0.020, decoration: const BoxDecoration( color: Colors.white, @@ -103,7 +103,6 @@ class CreateSpaceDialogState extends State { ), ], ), - const SizedBox(width: 16), Expanded( // Ensure the text field expands responsively @@ -170,8 +169,7 @@ class CreateSpaceDialogState extends State { mainAxisSize: MainAxisSize.min, children: [ Padding( - padding: const EdgeInsets.only( - left: 6.0), + padding: const EdgeInsets.only(left: 6.0), child: SvgPicture.asset( Assets.addIcon, width: screenWidth * 0.015, // Adjust icon size @@ -179,16 +177,11 @@ class CreateSpaceDialogState extends State { ), ), const SizedBox(width: 3), - const Flexible( + Flexible( child: Text( 'Add devices / Assign a space model', overflow: TextOverflow.ellipsis, // Prevent overflow - style: TextStyle( - color: Colors.black, - fontSize: 14, - fontFamily: 'Aftika', - fontWeight: FontWeight.w400, - ), + style: Theme.of(context).textTheme.bodyMedium, ), ), ], From 5091aa1e4983c650bd80b4391c1d43df8c5814b0 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 26 Nov 2024 13:29:48 +0400 Subject: [PATCH 104/122] responsive community header --- lib/pages/common/buttons/default_button.dart | 8 +- .../community_stricture_header_widget.dart | 134 ++++++++++++------ 2 files changed, 94 insertions(+), 48 deletions(-) diff --git a/lib/pages/common/buttons/default_button.dart b/lib/pages/common/buttons/default_button.dart index 2d901960..4aa748b7 100644 --- a/lib/pages/common/buttons/default_button.dart +++ b/lib/pages/common/buttons/default_button.dart @@ -70,14 +70,14 @@ class DefaultButton extends StatelessWidget { borderRadius: BorderRadius.circular(borderRadius ?? 20), ), ), - fixedSize: WidgetStateProperty.all( - const Size.fromHeight(50), - ), + fixedSize: height != null + ? WidgetStateProperty.all(Size.fromHeight(height!)) + : null, padding: WidgetStateProperty.all( EdgeInsets.all(padding ?? 10), ), minimumSize: WidgetStateProperty.all( - const Size.fromHeight(50), + const Size.fromHeight(10), ), elevation: WidgetStateProperty.all(elevation ?? 0), ), diff --git a/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart b/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart index cdbbf195..0f7e92d4 100644 --- a/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart +++ b/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart @@ -29,9 +29,10 @@ class CommunityStructureHeader extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final screenWidth = MediaQuery.of(context).size.width; return Container( - padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), decoration: BoxDecoration( color: ColorsManager.whiteColors, boxShadow: [ @@ -42,17 +43,24 @@ class CommunityStructureHeader extends StatelessWidget { ), ], ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildCommunityInfo(theme), - _buildActionButtons(), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: _buildCommunityInfo(theme, screenWidth), + ), + const SizedBox(width: 16), + ], + ), ], ), ); } - Widget _buildCommunityInfo(ThemeData theme) { + Widget _buildCommunityInfo(ThemeData theme, double screenWidth) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -60,22 +68,63 @@ class CommunityStructureHeader extends StatelessWidget { 'Community Structure', style: theme.textTheme.headlineLarge?.copyWith(color: ColorsManager.blackColor), ), - if (communityName != null) _buildCommunityName(), + if (communityName != null) + Row( + children: [ + if (!isEditingName) + Flexible( + child: Text( + communityName!, + style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + if (isEditingName) + SizedBox( + width: screenWidth * 0.3, + child: TextField( + controller: nameController, + decoration: const InputDecoration( + border: InputBorder.none, + isDense: true, + ), + style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), + onSubmitted: onNameSubmitted, + ), + ), + const SizedBox(width: 8), + GestureDetector( + onTap: onEditName, + child: SvgPicture.asset( + Assets.iconEdit, + width: 16, + height: 16, + ), + ), + const Spacer(flex: 3), // Pushes the Save button to the right + if (isSave) _buildActionButtons(), + ], + ), ], ); } - Widget _buildCommunityName() { + Widget _buildCommunityName(double screenWidth) { return Row( children: [ if (!isEditingName) - Text( - communityName!, - style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), + Flexible( + child: Text( + communityName!, + style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), ), if (isEditingName) SizedBox( - width: 200, + width: screenWidth * 0.3, child: TextField( controller: nameController, decoration: const InputDecoration( @@ -100,28 +149,15 @@ class CommunityStructureHeader extends StatelessWidget { } Widget _buildActionButtons() { - return Row( + return Wrap( + alignment: WrapAlignment.end, + spacing: 10, children: [ - if (isSave) - _buildButton( - label: "Save", - icon: const Icon(Icons.save, size: 18, color: ColorsManager.spaceColor), - onPressed: onSave, - ), - if (isSave) const SizedBox(width: 10), - /* - Commenting till finalize delete - if (communityName != null && communityName != '') - _buildButton( - label: "Delete", - icon: SvgPicture.asset( - Assets.delete, - width: 18, - height: 18, - ), - onPressed: onDelete, - ), - */ + _buildButton( + label: "Save", + icon: const Icon(Icons.save, size: 18, color: ColorsManager.spaceColor), + onPressed: onSave, + ), ], ); } @@ -131,26 +167,36 @@ class CommunityStructureHeader extends StatelessWidget { required Widget icon, required VoidCallback onPressed, }) { - return SizedBox( - width: 100, - height: 30, + final double buttonHeight = 30; + return ConstrainedBox( + constraints: BoxConstraints(maxWidth: 80, minHeight: buttonHeight), child: DefaultButton( onPressed: onPressed, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - icon, - const SizedBox(width: 8), - Text(label), - ], - ), backgroundColor: ColorsManager.textFieldGreyColor, foregroundColor: ColorsManager.blackColor, borderRadius: 8.0, padding: 2.0, + height: buttonHeight, elevation: 0, borderColor: Colors.grey.shade300, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + icon, + const SizedBox(width: 5), + Flexible( + child: Text( + label, + style: const TextStyle(fontSize: 13), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + ], + ), ), ); } + + } From 6fca5afd6ba8d9e8de2a3b33b268da5a4a576935 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 26 Nov 2024 13:58:50 +0400 Subject: [PATCH 105/122] updated text theme --- .../bloc/space_management_bloc.dart | 2 +- .../community_stricture_header_widget.dart | 54 +++---------------- 2 files changed, 9 insertions(+), 47 deletions(-) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index 74f76db2..eda95ee2 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -45,8 +45,8 @@ class SpaceManagementBloc extends Bloc Date: Tue, 26 Nov 2024 15:39:34 +0400 Subject: [PATCH 106/122] fixed overflow issue for add widget --- .../widgets/add_device_type_widget.dart | 97 +++++++++++-------- 1 file changed, 54 insertions(+), 43 deletions(-) diff --git a/lib/pages/spaces_management/widgets/add_device_type_widget.dart b/lib/pages/spaces_management/widgets/add_device_type_widget.dart index 2c88b866..08c08997 100644 --- a/lib/pages/spaces_management/widgets/add_device_type_widget.dart +++ b/lib/pages/spaces_management/widgets/add_device_type_widget.dart @@ -46,40 +46,50 @@ class _AddDeviceWidgetState extends State { Widget build(BuildContext context) { final size = MediaQuery.of(context).size; + // Adjust the GridView properties based on screen width + final crossAxisCount = size.width > 1200 + ? 8 + : size.width > 800 + ? 5 + : 3; + return AlertDialog( title: const Text('Add Devices'), backgroundColor: ColorsManager.whiteColors, - content: Container( - width: size.width * 0.65, - height: size.height * 0.57, - color: ColorsManager.textFieldGreyColor, - child: Column( - children: [ - const SizedBox(height: 16), - Expanded( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: Scrollbar( - controller: _scrollController, - thumbVisibility: false, - child: GridView.builder( + content: SingleChildScrollView( + child: Container( + width: size.width * 0.9, + height: size.height * 0.65, + color: ColorsManager.textFieldGreyColor, + child: Column( + children: [ + const SizedBox(height: 16), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Scrollbar( controller: _scrollController, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 6, - mainAxisSpacing: 10, - crossAxisSpacing: 10, - childAspectRatio: 0.7, + thumbVisibility: false, + child: GridView.builder( + shrinkWrap: true, + controller: _scrollController, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: crossAxisCount, + mainAxisSpacing: 6, + crossAxisSpacing: 4, + childAspectRatio: .8, + ), + itemCount: widget.products?.length ?? 0, + itemBuilder: (context, index) { + final product = widget.products![index]; + return _buildDeviceTypeTile(product, size); + }, ), - itemCount: widget.products?.length ?? 0, - itemBuilder: (context, index) { - final product = widget.products![index]; - return _buildDeviceTypeTile(product); - }, ), ), ), - ), - ], + ], + ), ), ), actions: [ @@ -101,15 +111,15 @@ class _AddDeviceWidgetState extends State { ); } - Widget _buildDeviceTypeTile(ProductModel product) { + Widget _buildDeviceTypeTile(ProductModel product, Size size) { final selectedProduct = productCounts.firstWhere( (p) => p.productId == product.uuid, orElse: () => SelectedProduct(productId: product.uuid, count: 0), ); return SizedBox( - width: 75, - height: 90, + width: size.width * 0.12, + height: size.height * 0.15, child: Card( elevation: 2, color: ColorsManager.whiteColors, @@ -117,14 +127,15 @@ class _AddDeviceWidgetState extends State { borderRadius: BorderRadius.circular(8), ), child: Padding( - padding: const EdgeInsets.all(6.0), + padding: const EdgeInsets.all(4.0), child: Column( - mainAxisAlignment: MainAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - _buildDeviceIcon(product), - const SizedBox(height: 8), - _buildDeviceName(product), - const SizedBox(height: 8), + _buildDeviceIcon(product, size), + const SizedBox(height: 4), + _buildDeviceName(product, size), + const SizedBox(height: 4), CounterWidget( initialCount: selectedProduct.count, onCountChanged: (newCount) { @@ -153,10 +164,10 @@ class _AddDeviceWidgetState extends State { ); } - Widget _buildDeviceIcon(ProductModel product) { + Widget _buildDeviceIcon(ProductModel product, Size size) { return Container( - height: 80, - width: 80, + height: size.width > 800 ? 50 : 40, + width: size.width > 800 ? 50 : 40, decoration: BoxDecoration( shape: BoxShape.circle, color: ColorsManager.textFieldGreyColor, @@ -168,16 +179,16 @@ class _AddDeviceWidgetState extends State { child: Center( child: SvgPicture.asset( product.icon ?? Assets.sensors, - width: 45, - height: 45, + width: size.width > 800 ? 30 : 20, + height: size.width > 800 ? 30 : 20, ), ), ); } - Widget _buildDeviceName(ProductModel product) { + Widget _buildDeviceName(ProductModel product, Size size) { return SizedBox( - height: 35, + height: size.width > 800 ? 35 : 25, child: Text( product.name ?? '', style: context.textTheme.bodySmall?.copyWith(color: ColorsManager.blackColor), @@ -195,7 +206,7 @@ class _AddDeviceWidgetState extends State { VoidCallback onPressed, ) { return SizedBox( - width: 200, + width: 120, child: DefaultButton( onPressed: onPressed, backgroundColor: backgroundColor, From ef80b79f7d2b2868feda67cf9b54a9c3d3aef153 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 26 Nov 2024 15:45:01 +0400 Subject: [PATCH 107/122] added navigate button --- lib/pages/spaces_management/view/spaces_management_page.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 452a1657..3748bee3 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; @@ -46,6 +47,7 @@ class SpaceManagementPageState extends State { child: WebScaffold( appBarTitle: Text('Space Management', style: Theme.of(context).textTheme.headlineLarge), enableMenuSidebar: false, + rightBody: const NavigateHomeGridView(), scaffoldBody: BlocBuilder(builder: (context, state) { if (state is SpaceManagementLoading) { From 659dde3160e0f7d378c992f4bf31801ef2b2ea1b Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 10:24:46 +0400 Subject: [PATCH 108/122] added validation to create space --- .../widgets/dialogs/create_space_dialog.dart | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart index 40f20ece..4a0c0ca4 100644 --- a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart @@ -36,6 +36,8 @@ class CreateSpaceDialogState extends State { String enteredName = ''; List selectedProducts = []; late TextEditingController nameController; + bool isOkButtonEnabled = false; + bool isNameFieldInvalid = false; @override void initState() { @@ -43,6 +45,7 @@ class CreateSpaceDialogState extends State { selectedIcon = widget.icon ?? Assets.location; nameController = TextEditingController(text: widget.name ?? ''); selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : []; + isOkButtonEnabled = enteredName.isNotEmpty || widget.name!.isNotEmpty; } @override @@ -113,6 +116,14 @@ class CreateSpaceDialogState extends State { controller: nameController, onChanged: (value) { enteredName = value; + setState(() { + if (value.isNotEmpty) { + isOkButtonEnabled = true; + isNameFieldInvalid = false; + } else { + isOkButtonEnabled = false; + } + }); }, style: const TextStyle(color: Colors.black), decoration: InputDecoration( @@ -126,8 +137,9 @@ class CreateSpaceDialogState extends State { fillColor: ColorsManager.boxColor, enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: ColorsManager.boxColor, + borderSide: BorderSide( + color: + isNameFieldInvalid ? ColorsManager.red : ColorsManager.boxColor, width: 1.5, ), ), @@ -140,6 +152,17 @@ class CreateSpaceDialogState extends State { ), ), ), + if (isNameFieldInvalid) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + '*Space name should not be empty.', + style: Theme.of(context) + .textTheme + .bodySmall + ?.copyWith(color: ColorsManager.red), + ), + ), const SizedBox(height: 16), if (selectedProducts.isNotEmpty) _buildSelectedProductsButtons(widget.products ?? []) @@ -210,13 +233,22 @@ class CreateSpaceDialogState extends State { Expanded( child: DefaultButton( onPressed: () { - String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? ''); - if (newName.isNotEmpty) { - widget.onCreateSpace(newName, selectedIcon, selectedProducts); - Navigator.of(context).pop(); + if (enteredName.trim().isEmpty || widget.name!.isEmpty) { + setState(() { + isNameFieldInvalid = true; + }); + return; + } else { + String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? ''); + if (newName.isNotEmpty) { + widget.onCreateSpace(newName, selectedIcon, selectedProducts); + Navigator.of(context).pop(); + } } }, - backgroundColor: ColorsManager.secondaryColor, + borderRadius: 10, + backgroundColor: + isOkButtonEnabled ? ColorsManager.secondaryColor : ColorsManager.grayColor, foregroundColor: ColorsManager.whiteColors, child: const Text('OK'), ), From a7b52d55e507f8b2ee3de89294a140c4e353fadc Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 10:52:12 +0400 Subject: [PATCH 109/122] fixed issue --- .../widgets/dialogs/create_space_dialog.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart index 4a0c0ca4..62d9fe0e 100644 --- a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart @@ -45,14 +45,14 @@ class CreateSpaceDialogState extends State { selectedIcon = widget.icon ?? Assets.location; nameController = TextEditingController(text: widget.name ?? ''); selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : []; - isOkButtonEnabled = enteredName.isNotEmpty || widget.name!.isNotEmpty; + isOkButtonEnabled = enteredName.isNotEmpty || nameController.text.isNotEmpty; + isNameFieldInvalid = nameController.text.isEmpty; } @override @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; - final screenHeight = MediaQuery.of(context).size.height; return AlertDialog( title: widget.isEdit ? const Text('Edit Space') : const Text('Create New Space'), @@ -121,6 +121,7 @@ class CreateSpaceDialogState extends State { isOkButtonEnabled = true; isNameFieldInvalid = false; } else { + isNameFieldInvalid = true; isOkButtonEnabled = false; } }); @@ -233,7 +234,7 @@ class CreateSpaceDialogState extends State { Expanded( child: DefaultButton( onPressed: () { - if (enteredName.trim().isEmpty || widget.name!.isEmpty) { + if (nameController.text.isEmpty) { setState(() { isNameFieldInvalid = true; }); From 35557193f5f57431aa594115a70914b62024049b Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 10:52:26 +0400 Subject: [PATCH 110/122] applied colormanager instead of color --- lib/pages/spaces_management/widgets/plus_button_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/spaces_management/widgets/plus_button_widget.dart b/lib/pages/spaces_management/widgets/plus_button_widget.dart index b077ac9d..40be7284 100644 --- a/lib/pages/spaces_management/widgets/plus_button_widget.dart +++ b/lib/pages/spaces_management/widgets/plus_button_widget.dart @@ -45,7 +45,7 @@ class PlusButtonWidget extends StatelessWidget { color: ColorsManager.spaceColor, shape: BoxShape.circle, ), - child: const Icon(Icons.add, color: Colors.white, size: 20), + child: const Icon(Icons.add, color: ColorsManager.whiteColors, size: 20), ), ), ); From ee50c414c3e6a716fa89ec79968ad83966b2076e Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 11:45:19 +0400 Subject: [PATCH 111/122] fixed save button position --- .../community_stricture_header_widget.dart | 90 ++++++++++--------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart b/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart index 4b6bcbeb..0addbe87 100644 --- a/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart +++ b/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart @@ -71,39 +71,49 @@ class CommunityStructureHeader extends StatelessWidget { if (communityName != null) Row( children: [ - if (!isEditingName) - Flexible( - child: Text( - communityName!, - style: theme.textTheme.bodyLarge?.copyWith(color: ColorsManager.blackColor), - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), - ), - if (isEditingName) - SizedBox( - width: screenWidth * 0.3, - child: TextField( - controller: nameController, - decoration: const InputDecoration( - border: InputBorder.none, - isDense: true, + Expanded( + child: Row( + children: [ + if (!isEditingName) + Flexible( + child: Text( + communityName!, + style: + theme.textTheme.bodyLarge?.copyWith(color: ColorsManager.blackColor), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + if (isEditingName) + SizedBox( + width: screenWidth * 0.3, + child: TextField( + controller: nameController, + decoration: const InputDecoration( + border: InputBorder.none, + isDense: true, + ), + style: + theme.textTheme.bodyLarge?.copyWith(color: ColorsManager.blackColor), + onSubmitted: onNameSubmitted, + ), + ), + const SizedBox(width: 2), + GestureDetector( + onTap: onEditName, + child: SvgPicture.asset( + Assets.iconEdit, + width: 16, + height: 16, + ), ), - style: theme.textTheme.bodyLarge?.copyWith(color: ColorsManager.blackColor), - onSubmitted: onNameSubmitted, - ), - ), - const SizedBox(width: 8), - GestureDetector( - onTap: onEditName, - child: SvgPicture.asset( - Assets.iconEdit, - width: 16, - height: 16, + ], ), ), - const Spacer(flex: 3), // Pushes the Save button to the right - if (isSave) _buildActionButtons(theme), + if (isSave) ...[ + const SizedBox(width: 8), + _buildActionButtons(theme), + ], ], ), ], @@ -116,21 +126,19 @@ class CommunityStructureHeader extends StatelessWidget { spacing: 10, children: [ _buildButton( - label: "Save", - icon: const Icon(Icons.save, size: 18, color: ColorsManager.spaceColor), - onPressed: onSave, - theme: theme - ), + label: "Save", + icon: const Icon(Icons.save, size: 18, color: ColorsManager.spaceColor), + onPressed: onSave, + theme: theme), ], ); } - Widget _buildButton({ - required String label, - required Widget icon, - required VoidCallback onPressed, - required ThemeData theme - }) { + Widget _buildButton( + {required String label, + required Widget icon, + required VoidCallback onPressed, + required ThemeData theme}) { const double buttonHeight = 30; return ConstrainedBox( constraints: BoxConstraints(maxWidth: 80, minHeight: buttonHeight), From 670be705675cd59193057056a9ceef0a98a86ef2 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 12:10:36 +0400 Subject: [PATCH 112/122] updated first space position --- .../widgets/community_structure_widget.dart | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index b23bb040..6123d25c 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -209,7 +209,8 @@ class _CommunityStructureAreaState extends State { Center( child: AddSpaceButton( onTap: () { - _showCreateSpaceDialog(screenSize); + _showCreateSpaceDialog(screenSize, + canvasHeight: canvasHeight, canvasWidth: canvasWidth); }, ), ), @@ -259,7 +260,11 @@ class _CommunityStructureAreaState extends State { } void _showCreateSpaceDialog(Size screenSize, - {Offset? position, int? parentIndex, String? direction}) { + {Offset? position, + int? parentIndex, + String? direction, + double? canvasWidth, + double? canvasHeight}) { showDialog( context: context, builder: (BuildContext context) { @@ -268,11 +273,8 @@ class _CommunityStructureAreaState extends State { onCreateSpace: (String name, String icon, List selectedProducts) { setState(() { // Set the first space in the center or use passed position - Offset centerPosition = position ?? - Offset( - screenSize.width / 2 - 75, // Center horizontally - screenSize.height / 2 - 50, // Slightly above the center vertically - ); + + Offset centerPosition = position ?? _getCenterPosition(screenSize); SpaceModel newSpace = SpaceModel( name: name, icon: icon, @@ -476,7 +478,6 @@ class _CommunityStructureAreaState extends State { bool _isHighlightedSpace(SpaceModel space) { if (widget.selectedSpace == null) return true; if (space == widget.selectedSpace) return true; - if (widget.selectedSpace?.parent?.name == space.name && widget.selectedSpace?.parent?.position == space.position) return true; if (widget.selectedSpace?.children.contains(space) == true) return true; @@ -502,4 +503,11 @@ class _CommunityStructureAreaState extends State { return connection.startSpace == widget.selectedSpace || connection.endSpace == widget.selectedSpace; } + + Offset _getCenterPosition(Size screenSize) { + return Offset( + screenSize.width / 2 - 260, + screenSize.height / 2 - 200, + ); + } } From ff22a6b6ab13c10ef0acecd445227eb40ed07946 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 14:22:48 +0400 Subject: [PATCH 113/122] Fixed size hoverable button --- .../widgets/hoverable_button.dart | 55 ++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/lib/pages/spaces_management/widgets/hoverable_button.dart b/lib/pages/spaces_management/widgets/hoverable_button.dart index 0c158c12..0880471a 100644 --- a/lib/pages/spaces_management/widgets/hoverable_button.dart +++ b/lib/pages/spaces_management/widgets/hoverable_button.dart @@ -24,37 +24,40 @@ class _HoverableButtonState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final screenWidth = MediaQuery.of(context).size.width; return GestureDetector( onTap: widget.onTap, child: MouseRegion( - onEnter: (_) => _updateHoverState(true), - onExit: (_) => _updateHoverState(false), - child: AnimatedContainer( - duration: const Duration(milliseconds: 200), - padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 8), - decoration: BoxDecoration( - color: isHovered ? ColorsManager.warningRed : Colors.white, - borderRadius: BorderRadius.circular(16), - boxShadow: [ - if (isHovered) - BoxShadow( - color: ColorsManager.warningRed.withOpacity(0.4), - blurRadius: 8, - offset: const Offset(0, 4), + onEnter: (_) => _updateHoverState(true), + onExit: (_) => _updateHoverState(false), + child: SizedBox( + width: screenWidth * .07, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 8), + decoration: BoxDecoration( + color: isHovered ? ColorsManager.warningRed : Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + if (isHovered) + BoxShadow( + color: ColorsManager.warningRed.withOpacity(0.4), + blurRadius: 8, + offset: const Offset(0, 4), + ), + ], ), - ], - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - _buildIcon(), - if (!isHovered) const SizedBox(width: 8), - if (!isHovered) _buildText(theme), - ], - ), - ), - ), + child: Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + _buildIcon(), + if (!isHovered) const SizedBox(width: 8), + if (!isHovered) _buildText(theme), + ], + ), + )), + )), ); } From 85bc7ee8c07bd86c1a14b3456b3506966b05d2da Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 14:23:18 +0400 Subject: [PATCH 114/122] add space between buttons --- .../widgets/dialogs/create_space_dialog.dart | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart index 62d9fe0e..b7a17b76 100644 --- a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart @@ -323,18 +323,21 @@ class CreateSpaceDialogState extends State { spacing: 8, runSpacing: 8, children: [ - for (var product in selectedProducts) + for (var i = 0; i < selectedProducts.length; i++) ...[ HoverableButton( - iconPath: _mapIconToProduct(product.productId, products), - text: 'x${product.count}', + iconPath: _mapIconToProduct(selectedProducts[i].productId, products), + text: 'x${selectedProducts[i].count}', onTap: () { setState(() { - selectedProducts.remove(product); + selectedProducts.remove(selectedProducts[i]); }); // Handle button tap }, ), - // Add Button + if (i < selectedProducts.length - 1) + const SizedBox(width: 2), // Add space except after the last button + ], + const SizedBox(width: 2), GestureDetector( onTap: () { showDialog( From 8ab3d44708cd449e0bc46727522c0d25c508f511 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 14:26:10 +0400 Subject: [PATCH 115/122] chananged Colors to use colorsmanager --- lib/pages/spaces_management/widgets/hoverable_button.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/spaces_management/widgets/hoverable_button.dart b/lib/pages/spaces_management/widgets/hoverable_button.dart index 0880471a..49a863b6 100644 --- a/lib/pages/spaces_management/widgets/hoverable_button.dart +++ b/lib/pages/spaces_management/widgets/hoverable_button.dart @@ -36,7 +36,7 @@ class _HoverableButtonState extends State { child: Container( padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 8), decoration: BoxDecoration( - color: isHovered ? ColorsManager.warningRed : Colors.white, + color: isHovered ? ColorsManager.warningRed : ColorsManager.whiteColors, borderRadius: BorderRadius.circular(16), boxShadow: [ if (isHovered) @@ -65,7 +65,7 @@ class _HoverableButtonState extends State { return isHovered ? const Icon( Icons.close, - color: Colors.white, + color: ColorsManager.whiteColors, size: 24, ) : SvgPicture.asset( From 2d7415448c4c9c7728f21481ba1a39be3ea28fcd Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 14:31:07 +0400 Subject: [PATCH 116/122] appear save only if spaces is empty or any new,deleted,updated space --- ...t.dart => community_structure_header_widget.dart} | 0 .../widgets/community_structure_widget.dart | 12 ++++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) rename lib/pages/spaces_management/widgets/{community_stricture_header_widget.dart => community_structure_header_widget.dart} (100%) diff --git a/lib/pages/spaces_management/widgets/community_stricture_header_widget.dart b/lib/pages/spaces_management/widgets/community_structure_header_widget.dart similarity index 100% rename from lib/pages/spaces_management/widgets/community_stricture_header_widget.dart rename to lib/pages/spaces_management/widgets/community_structure_header_widget.dart diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 6123d25c..40cb6077 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -12,7 +12,7 @@ import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/blank_community_widget.dart'; -import 'package:syncrow_web/pages/spaces_management/widgets/community_stricture_header_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/community_structure_header_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/space_card_widget.dart'; @@ -114,7 +114,7 @@ class _CommunityStructureAreaState extends State { children: [ CommunityStructureHeader( communityName: widget.selectedCommunity?.name, - isSave: spaces.isNotEmpty, + isSave: isSave(spaces), isEditingName: isEditingName, nameController: _nameController, onSave: _saveSpaces, @@ -510,4 +510,12 @@ class _CommunityStructureAreaState extends State { screenSize.height / 2 - 200, ); } + + bool isSave(List spaces) { + return spaces.isNotEmpty && + spaces.any((space) => + space.status == SpaceStatus.newSpace || + space.status == SpaceStatus.modified || + space.status == SpaceStatus.deleted); + } } From 79b3d116ca6839729de362770a46b2f855fb1edf Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 19:17:15 +0400 Subject: [PATCH 117/122] added internal id for mapping spaces internally --- .../bloc/space_management_bloc.dart | 1 - .../spaces_management/model/space_model.dart | 62 ++++++++++--------- .../view/spaces_management_page.dart | 6 -- .../widgets/community_structure_widget.dart | 19 +++--- 4 files changed, 45 insertions(+), 43 deletions(-) diff --git a/lib/pages/spaces_management/bloc/space_management_bloc.dart b/lib/pages/spaces_management/bloc/space_management_bloc.dart index eda95ee2..f178dd3a 100644 --- a/lib/pages/spaces_management/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/bloc/space_management_bloc.dart @@ -86,7 +86,6 @@ class SpaceManagementBloc extends Bloc communities = await _api.fetchCommunities(); - // Use Future.wait to handle async calls within map List updatedCommunities = await Future.wait( communities.map((community) async { List spaces = await _api.getSpaceHierarchy(community.uuid); diff --git a/lib/pages/spaces_management/model/space_model.dart b/lib/pages/spaces_management/model/space_model.dart index e1594a4c..6efe19ec 100644 --- a/lib/pages/spaces_management/model/space_model.dart +++ b/lib/pages/spaces_management/model/space_model.dart @@ -3,6 +3,7 @@ import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:uuid/uuid.dart'; enum SpaceStatus { newSpace, modified, unchanged, deleted } @@ -20,12 +21,14 @@ class SpaceModel { bool isHovered; SpaceStatus status; List selectedProducts; + String internalId; List outgoingConnections = []; // Connections from this space Connection? incomingConnection; // Connections to this space SpaceModel({ this.uuid, + String? internalId, this.spaceTuyaUuid, required this.icon, required this.name, @@ -39,25 +42,44 @@ class SpaceModel { this.incomingConnection, this.status = SpaceStatus.unchanged, this.selectedProducts = const [], - }); + }) : internalId = internalId ?? const Uuid().v4(); - factory SpaceModel.fromJson(Map json) { - // Create SpaceModel instance first - final instance = SpaceModel( + factory SpaceModel.fromJson(Map json, {String? parentInternalId}) { + final String internalId = json['internalId'] ?? const Uuid().v4(); + + final List children = json['children'] != null + ? (json['children'] as List).map((childJson) { + return SpaceModel.fromJson( + childJson, + parentInternalId: internalId, + ); + }).toList() + : []; + + return SpaceModel( + internalId: internalId, uuid: json['uuid'] ?? '', spaceTuyaUuid: json['spaceTuyaUuid'], name: json['spaceName'], isPrivate: json['isPrivate'] ?? false, invitationCode: json['invitationCode'], - parent: json['parent'] != null ? SpaceModel.fromJson(json['parent']) : null, + parent: parentInternalId != null + ? SpaceModel( + internalId: parentInternalId, + uuid: json['parent']?['uuid'], + spaceTuyaUuid: json['parent']?['spaceTuyaUuid'], + name: json['parent']?['spaceName'] ?? '', + isPrivate: json['parent']?['isPrivate'] ?? false, + invitationCode: json['parent']?['invitationCode'], + children: [], + position: Offset(json['parent']?['x'] ?? 0, json['parent']?['y'] ?? 0), + icon: json['parent']?['icon'] ?? Assets.location, + ) + : null, community: json['community'] != null ? CommunityModel.fromJson(json['community']) : null, - children: json['children'] != null - ? (json['children'] as List).map((child) => SpaceModel.fromJson(child)).toList() - : [], - icon: json['icon'] != null? json['icon'] : Assets.location, - position: json['x'] != null && json['y'] != null - ? Offset(json['x'], json['y']) - : const Offset(0, 0), + children: children, + icon: json['icon'] ?? Assets.location, + position: Offset(json['x'] ?? 0, json['y'] ?? 0), isHovered: false, selectedProducts: json['spaceProducts'] != null ? (json['spaceProducts'] as List).map((product) { @@ -68,21 +90,6 @@ class SpaceModel { }).toList() : [], ); - - // Add incomingConnection to the instance after creation - if (json['incomingConnections'] != null && - json['incomingConnections'] is List && - (json['incomingConnections'] as List).isNotEmpty && - instance.parent != null) { - final conn = json['incomingConnections'][0]; - instance.incomingConnection = Connection( - startSpace: instance.parent ?? instance, // Parent space - endSpace: instance, // This space instance - direction: conn['direction'], - ); - } - - return instance; } Map toMap() { @@ -106,5 +113,4 @@ class SpaceModel { void addOutgoingConnection(Connection connection) { outgoingConnections.add(connection); } - } diff --git a/lib/pages/spaces_management/view/spaces_management_page.dart b/lib/pages/spaces_management/view/spaces_management_page.dart index 3748bee3..54027066 100644 --- a/lib/pages/spaces_management/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/view/spaces_management_page.dart @@ -5,9 +5,7 @@ import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.d import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_state.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; -import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; -import 'package:syncrow_web/pages/spaces_management/model/space_data_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/loaded_space_widget.dart'; import 'package:syncrow_web/services/product_api.dart'; @@ -27,10 +25,6 @@ class SpaceManagementPageState extends State { final CommunitySpaceManagementApi _api = CommunitySpaceManagementApi(); final ProductApi _productApi = ProductApi(); Map> communitySpaces = {}; - double canvasWidth = 1000; - double canvasHeight = 1000; - List spaces = []; - List connections = []; List products = []; bool isProductDataLoaded = false; diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 40cb6077..7322caaa 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -286,6 +286,7 @@ class _CommunityStructureAreaState extends State { if (parentIndex != null && direction != null) { SpaceModel parentSpace = spaces[parentIndex]; + parentSpace.internalId = spaces[parentIndex].internalId; newSpace.parent = parentSpace; final newConnection = Connection( startSpace: parentSpace, @@ -371,13 +372,11 @@ class _CommunityStructureAreaState extends State { for (var child in parent.children) { if (child.status == SpaceStatus.deleted) continue; - // Create a connection object connections.add( Connection( startSpace: parent, endSpace: child, - direction: child.incomingConnection?.direction ?? - "down", // Assuming "down" for all direct children + direction: child.incomingConnection?.direction ?? "down", ), ); @@ -478,11 +477,15 @@ class _CommunityStructureAreaState extends State { bool _isHighlightedSpace(SpaceModel space) { if (widget.selectedSpace == null) return true; if (space == widget.selectedSpace) return true; - if (widget.selectedSpace?.parent?.name == space.name && - widget.selectedSpace?.parent?.position == space.position) return true; - if (widget.selectedSpace?.children.contains(space) == true) return true; - - return false; // Otherwise, reduce opacity + if (widget.selectedSpace?.parent?.internalId == space.internalId) return true; + if (widget.selectedSpace?.children != null) { + for (var child in widget.selectedSpace!.children) { + if (child.internalId == space.internalId) { + return true; + } + } + } + return false; } void _deselectSpace() { From 49a732edb25f96bb53f599f546b73ef4f00d50a5 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 20:16:04 +0400 Subject: [PATCH 118/122] fixed size of icon --- .../common/buttons/add_space_button.dart | 5 +- .../dialogs/create_community_dialog.dart | 12 +-- .../widgets/dialogs/create_space_dialog.dart | 46 +++--------- .../dialogs/icon_selection_dialog.dart | 74 +++++++++++++++++++ 4 files changed, 92 insertions(+), 45 deletions(-) create mode 100644 lib/pages/spaces_management/widgets/dialogs/icon_selection_dialog.dart diff --git a/lib/pages/common/buttons/add_space_button.dart b/lib/pages/common/buttons/add_space_button.dart index 8348b390..28de2af9 100644 --- a/lib/pages/common/buttons/add_space_button.dart +++ b/lib/pages/common/buttons/add_space_button.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; class AddSpaceButton extends StatelessWidget { final VoidCallback onTap; @@ -28,8 +29,8 @@ class AddSpaceButton extends StatelessWidget { child: Container( width: 40, // Size of the inner circle height: 40, - decoration: BoxDecoration( - color: const Color(0xFFF5F6F7), // Light gray background + decoration: const BoxDecoration( + color: ColorsManager.boxColor, // Light gray background shape: BoxShape.circle, // Circular shape for the icon container ), child: const Icon( diff --git a/lib/pages/spaces_management/widgets/dialogs/create_community_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/create_community_dialog.dart index f875f5ef..0a16b6c9 100644 --- a/lib/pages/spaces_management/widgets/dialogs/create_community_dialog.dart +++ b/lib/pages/spaces_management/widgets/dialogs/create_community_dialog.dart @@ -62,26 +62,26 @@ class CreateCommunityDialogState extends State { decoration: InputDecoration( hintText: 'Please enter the community name', filled: true, - fillColor: const Color(0xFFF5F6F7), + fillColor: ColorsManager.boxColor, hintStyle: const TextStyle( fontSize: 14, - color: Color(0xFF999999), + color: ColorsManager.boxColor, fontWeight: FontWeight.w400, ), border: OutlineInputBorder( borderSide: - const BorderSide(color: Color(0xFFF5F6F7), width: 1), + const BorderSide(color: ColorsManager.boxColor, width: 1), borderRadius: BorderRadius.circular(10), ), enabledBorder: OutlineInputBorder( borderSide: - const BorderSide(color: Color(0xFFF5F6F7), width: 1), + const BorderSide(color: ColorsManager.boxColor, width: 1), borderRadius: BorderRadius.circular(10), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: - const BorderSide(color: Color(0xFFF5F6F7), width: 1), + const BorderSide(color: ColorsManager.boxColor, width: 1), ), ), ), @@ -95,7 +95,7 @@ class CreateCommunityDialogState extends State { onPressed: () => Navigator.of(context).pop(), style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), - backgroundColor: const Color(0xFFF5F6F7), + backgroundColor: ColorsManager.boxColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), diff --git a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart index b7a17b76..5953951c 100644 --- a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart @@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/add_device_type_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/icon_selection_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/hoverable_button.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -261,45 +262,16 @@ class CreateSpaceDialogState extends State { } void _showIconSelectionDialog() { - final screenWidth = MediaQuery.of(context).size.width; - showDialog( context: context, builder: (BuildContext context) { - return AlertDialog( - title: const Text('Select Icon'), - backgroundColor: Colors.white, - content: Container( - width: screenWidth * 0.5, - padding: const EdgeInsets.all(18), - decoration: BoxDecoration( - color: ColorsManager.boxColor, - borderRadius: BorderRadius.circular(12), - ), - child: GridView.builder( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 7, - crossAxisSpacing: 10, - mainAxisSpacing: 22, - ), - itemCount: spaceIconList.length, - itemBuilder: (BuildContext context, int index) { - return GestureDetector( - onTap: () { - setState(() { - selectedIcon = spaceIconList[index]; - }); - Navigator.of(context).pop(); - }, - child: SvgPicture.asset( - spaceIconList[index], - width: screenWidth * 0.06, - height: screenWidth * 0.06, - ), - ); - }, - ), - ), + return IconSelectionDialog( + spaceIconList: spaceIconList, + onIconSelected: (String selectedIcon) { + setState(() { + this.selectedIcon = selectedIcon; + }); + }, ); }, ); @@ -316,7 +288,7 @@ class CreateSpaceDialogState extends State { borderRadius: BorderRadius.circular(12), border: Border.all( color: ColorsManager.neutralGray, - width: 2, // Set the border width + width: 2, ), ), child: Wrap( diff --git a/lib/pages/spaces_management/widgets/dialogs/icon_selection_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/icon_selection_dialog.dart new file mode 100644 index 00000000..5251ba32 --- /dev/null +++ b/lib/pages/spaces_management/widgets/dialogs/icon_selection_dialog.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class IconSelectionDialog extends StatelessWidget { + final List spaceIconList; + final Function(String selectedIcon) onIconSelected; + + const IconSelectionDialog({ + Key? key, + required this.spaceIconList, + required this.onIconSelected, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final screenWidth = MediaQuery.of(context).size.width; + final screenHeight = MediaQuery.of(context).size.height; + + return Dialog( + elevation: 0, + backgroundColor: ColorsManager.transparentColor, + child: Container( + width: screenWidth * 0.44, + height: screenHeight * 0.45, + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), // Shadow color + blurRadius: 20, // Spread of the blur + offset: const Offset(0, 8), // Offset of the shadow + ), + ], + ), + child: AlertDialog( + title: Text('Space Icon',style: Theme.of(context).textTheme.headlineMedium), + backgroundColor: ColorsManager.whiteColors, + content: Container( + width: screenWidth * 0.4, + height: screenHeight * 0.45, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: ColorsManager.boxColor, + borderRadius: BorderRadius.circular(12), + ), + child: GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 7, + crossAxisSpacing: 8, + mainAxisSpacing: 16, + ), + itemCount: spaceIconList.length, + itemBuilder: (BuildContext context, int index) { + return GestureDetector( + onTap: () { + onIconSelected(spaceIconList[index]); + Navigator.of(context).pop(); + }, + child: SvgPicture.asset( + spaceIconList[index], + width: screenWidth * 0.03, + height: screenWidth * 0.03, + ), + ); + }, + ), + ), + ), + ), + ); + } +} From dafe90237ff479048fad25d0c775adf5fac83bfd Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 20:24:44 +0400 Subject: [PATCH 119/122] change color use from colorsmanager --- lib/pages/spaces_management/widgets/plus_button_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/spaces_management/widgets/plus_button_widget.dart b/lib/pages/spaces_management/widgets/plus_button_widget.dart index 40be7284..b077ac9d 100644 --- a/lib/pages/spaces_management/widgets/plus_button_widget.dart +++ b/lib/pages/spaces_management/widgets/plus_button_widget.dart @@ -45,7 +45,7 @@ class PlusButtonWidget extends StatelessWidget { color: ColorsManager.spaceColor, shape: BoxShape.circle, ), - child: const Icon(Icons.add, color: ColorsManager.whiteColors, size: 20), + child: const Icon(Icons.add, color: Colors.white, size: 20), ), ), ); From 448e798da131fb876739d46232f822387b1f97aa Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 20:26:26 +0400 Subject: [PATCH 120/122] change colors to colorsmanager --- lib/common/custom_expansion_tile.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/common/custom_expansion_tile.dart b/lib/common/custom_expansion_tile.dart index 364ccdce..8df9b663 100644 --- a/lib/common/custom_expansion_tile.dart +++ b/lib/common/custom_expansion_tile.dart @@ -85,7 +85,7 @@ class CustomExpansionTileState extends State { }, child: Icon( _isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right, - color: Colors.grey, + color: ColorsManager.lightGrayColor, size: 16.0, // Adjusted size for better alignment ), ), @@ -101,7 +101,7 @@ class CustomExpansionTileState extends State { _capitalizeFirstLetter(widget.title), style: TextStyle( color: widget.isSelected - ? Colors.black // Change color to black when selected + ? ColorsManager.blackColor // Change color to black when selected : ColorsManager.lightGrayColor, // Gray when not selected fontWeight: FontWeight.w400, ), From 22a9ee5a143055a807dc5f64447ff448c292b12a Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 27 Nov 2024 22:41:18 +0400 Subject: [PATCH 121/122] added validity for reusing space name in same layer --- .../widgets/community_structure_widget.dart | 1 + .../widgets/dialogs/create_space_dialog.dart | 43 ++++++++++++++----- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/lib/pages/spaces_management/widgets/community_structure_widget.dart b/lib/pages/spaces_management/widgets/community_structure_widget.dart index 7322caaa..90937f28 100644 --- a/lib/pages/spaces_management/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/widgets/community_structure_widget.dart @@ -270,6 +270,7 @@ class _CommunityStructureAreaState extends State { builder: (BuildContext context) { return CreateSpaceDialog( products: widget.products, + parentSpace: parentIndex != null? spaces[parentIndex] : null, onCreateSpace: (String name, String icon, List selectedProducts) { setState(() { // Set the first space in the center or use passed position diff --git a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart index 5953951c..eba5096c 100644 --- a/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/widgets/dialogs/create_space_dialog.dart @@ -4,6 +4,7 @@ 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/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; +import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/add_device_type_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/icon_selection_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/hoverable_button.dart'; @@ -18,9 +19,11 @@ class CreateSpaceDialog extends StatefulWidget { final String? icon; final bool isEdit; final List selectedProducts; + final SpaceModel? parentSpace; const CreateSpaceDialog( {super.key, + this.parentSpace, required this.onCreateSpace, this.products, this.name, @@ -39,6 +42,7 @@ class CreateSpaceDialogState extends State { late TextEditingController nameController; bool isOkButtonEnabled = false; bool isNameFieldInvalid = false; + bool isNameFieldExist = false; @override void initState() { @@ -47,7 +51,6 @@ class CreateSpaceDialogState extends State { nameController = TextEditingController(text: widget.name ?? ''); selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : []; isOkButtonEnabled = enteredName.isNotEmpty || nameController.text.isNotEmpty; - isNameFieldInvalid = nameController.text.isEmpty; } @override @@ -116,14 +119,20 @@ class CreateSpaceDialogState extends State { TextField( controller: nameController, onChanged: (value) { - enteredName = value; + enteredName = value.trim(); setState(() { - if (value.isNotEmpty) { - isOkButtonEnabled = true; - isNameFieldInvalid = false; - } else { - isNameFieldInvalid = true; - isOkButtonEnabled = false; + isNameFieldExist = false; + isOkButtonEnabled = false; + isNameFieldInvalid = value.isEmpty; + + if (!isNameFieldInvalid) { + if (widget.parentSpace?.children + .any((child) => child.name == value) ?? + false) { + isNameFieldExist = true; + } else { + isOkButtonEnabled = true; + } } }); }, @@ -140,8 +149,9 @@ class CreateSpaceDialogState extends State { enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide( - color: - isNameFieldInvalid ? ColorsManager.red : ColorsManager.boxColor, + color: isNameFieldInvalid || isNameFieldExist + ? ColorsManager.red + : ColorsManager.boxColor, width: 1.5, ), ), @@ -165,6 +175,17 @@ class CreateSpaceDialogState extends State { ?.copyWith(color: ColorsManager.red), ), ), + if (isNameFieldExist) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + '*Name already exist', + style: Theme.of(context) + .textTheme + .bodySmall + ?.copyWith(color: ColorsManager.red), + ), + ), const SizedBox(height: 16), if (selectedProducts.isNotEmpty) _buildSelectedProductsButtons(widget.products ?? []) @@ -269,7 +290,7 @@ class CreateSpaceDialogState extends State { spaceIconList: spaceIconList, onIconSelected: (String selectedIcon) { setState(() { - this.selectedIcon = selectedIcon; + this.selectedIcon = selectedIcon; }); }, ); From 68ebb0807d70794fc663081711eded42512f2f8b Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 28 Nov 2024 12:08:37 +0400 Subject: [PATCH 122/122] added package --- pubspec.lock | 36 ++++++++++++++++++++++++++++++++++-- pubspec.yaml | 2 ++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 92a76b10..56d85c9a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -57,6 +57,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" cupertino_icons: dependency: "direct main" description: @@ -137,6 +145,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" fl_chart: dependency: "direct main" description: @@ -533,6 +549,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -581,6 +605,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" vector_graphics: dependency: transitive description: @@ -617,10 +649,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.2.4" web: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7742e4da..155381ae 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,6 +50,8 @@ dependencies: dropdown_search: ^5.0.6 flutter_dotenv: ^5.1.0 fl_chart: ^0.69.0 + uuid: ^4.4.2 + dev_dependencies: flutter_test: